js基础知识
JS的变量类型
共八个大类!
- String
- Number
- Boolean
- Null
- undefined
- object
- Data
- function
- Array
- symbol
- bigInt
获取变量类型的最佳方法:Object.prototype.toString.call(temp).slice(8,-1)
日期问题
先定义一个Date对象let date=new Date()
获取一周的第几天date.getDay()
获取一月的第几天date.getDate()
获取一年第几个月date.getMonth()
年份值 date.getFullYear()
小时date.getHours()
分钟date.getMinutes()
秒钟date.getSeconds()
毫秒date.getMilliseconds()
返回总的毫秒值date.getTime()
怎么改变一个函数的this作用域
apply()
function bindThis(f,oTarget){
return function(){
return f.apply(oTarget,arguments)
}
}
call()
function bindThis(f,oTarget){
return function(){
return f.call(oTarget,...arguments)
}
}
bind()
function bindThis(f, oTarget) {
return f.bind(oTarget)
}
字符串问题
获取字符串长度 str.length
删除一个字符串的左边空白字符 str.leftTrim()
获取字符串的后3位 str.substr(-3)
原型链
Javascript只有一种结构,那就是:对象。在Javascript中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为null为止(也就是不再有原型指向)。这种一级一级的链结构就成为原型链(prototype chain)
原型链的继承
优点:能通过instanceOf和isPrototypeOf的检测
缺点:
- 1.SuperType中的属性页变成了SubType的prototype中的公用属性,如上面例子中的color属性,可以同时被instance1和instance2修改
- 2.创建子类型的时候,不能向父类型的构造函数中传递参数
function SuperType(){
this.colors=["red","blue","green"]
}
SuperType.prototype.Fun=function(){
}
function SubType(){
}
SubType.prototype = new SuperType()
let instance1= new SubType()
instance1.colors.push("black")
console.log(instance1.colors);
let instance2=new SubType()
console.log(instance2.colors);
借用构造函数继承
原理:在子类型构造函数的内部调用超类型构造函数
优点:解决了superType私有属性变共有的问题,可以传递参数了
缺点:方法在函数中定义,无法得到复用
function SuperType(){
this.colors=["red","blue","green"]
}
function SubType(){
SuperType.call(this)
}
let instance1=new SubType()
instance1.colors.push("black")
console.log(instance1.colors);
let instance2=new SubType()
console.log(instance2.colors);
-------------------------------------------
function SuperType2(name){
this.name=name
}
function SubType2(){
SuperType2.call(this,"41")
this.age=24
}
let instance =new SubType2()
console.log(instance.name);
console.log(instance.age);
组合继承
优点:继承前两者的优点,能通过instanceOf和isPrototypeOf的检测
缺点:两次调用父构造器函数,浪费内存
// 组合继承
function SuperType(name){
this.name=name
this.colors=["red","blue","green"]
}
SuperType.prototype.sayName=function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name) // 构造函数继承属性,二次调用
this.age=age
}
SubType.prototype = new SuperType(); // 原型链继承,一次调用
SubType.prototype.constructor=SubType // 二次调用
SubType.prototype.sayAge=function(){
console.log(this.age);
}
let instance1= new SubType("41",24)
instance1.colors.push("black")
console.log(instance1.colors);
instance1.sayName()
instance1.sayAge()
let instance2= new SubType("42",25)
console.log(instance2.colors);
instance2.sayName()
instance2.sayAge()
原型式继承
// 原型式继承
function object(o){
function F(){}
F.prototype=o
return new F()
}
没必要使用构造函数的时候!我们只需要一个对象可以这么写!
寄生继承
缺点:方法在函数中定义,无法得到复用
function createAnother(orginal){
let clone=orginal // 创建一个新对象
clone.sayHi=function(){ //寄生
console.log("hi!");
}
return clone
}
let person={
name:"41",
friends:["41","42","43"]
}
let anotherPerson=createAnother(person)
anotherPerson.sayHi()
寄生组合继承
function inheritPrototype(son,father){
let temp=Object.create(father.prototype)
temp.constructor=son
son.prototype=temp
}
function SuperType(name){
this.name=name
this.colors=["red","blue","green"]
}
function SubType(name,age){
SuperType.call(this,name)
this.age=age
}
SuperType.prototype.sayName=function(){
console.log(this.name);
}
inheritPrototype(SubType,SuperType) //实现继承
SubType.prototype.sayAge=function(){
console.log(this.age);
}
let sub1=new SubType("41",24)
sub1.colors.push("black")
console.log(sub1.colors);
sub1.sayAge()
sub1.sayName()
let sub2=new SubType("41",24)
console.log(sub2.colors);
sub2.sayAge()
sub2.sayName()
算法
把一个字符串里面的所有小写变成大写
function ans(str){
return str.toUpperCase()
}
str='123456asdsav'
console.log(ans(str));
把一个字符串里面所有的数字+1
function addone(str){
let newStr=''
str.split('').forEach(item=>{
if(item>-1&&item<10) newStr+=(+item)+1
else newStr+=item
})
return newStr
}
str='abc123456'
console.log(addone(str));
快排
function quick(arr,left,right){
if(arr.length>1){
let index=partition(arr,left,right)
if(left<index-1){
quick(arr,left,index-1)
}
if(index<right){
quick(arr,index,right)
}
}
return arr
}
function partition(arr,left,right){
let i=left,j=right,goal=arr[left]
while(i<=j){
while(arr[i]<goal) i++
while(arr[j]>goal) j--
if(i<=j){
[arr[i],arr[j]]=[arr[j],arr[i]]
i++
j--
}
}
return i
}
Array.prototype.mySort=function(){ //挂载到数组上!
return quick(this,0,this.length-1)
}
let arr=[12132,12,54654,8789,1212]
console.log(arr.mySort());
二分查找
服务端知识
cookie和session
cookie和session都是用来跟踪浏览器用户身份的会话方式
Cookie工作原理
- 1.浏览器端第一次发送请求到服务器端
- 2.服务器端创建Cookie,该Cookie中包含用户的信息,然后将该Cookie发送到浏览器端
- 3.浏览器端再次访问服务器端时会携带服务器端创建的Cookie
- 4.服务器端通过Cookie中携带的数据区分不同的用户
Session工作原理
- 1.浏览器端第一次发送请求到服务器端,服务器端创建一个Session,同时会创建一个特殊的Cookie(name为JSESSIONID的固定值,value为session对象的ID),然后将该Cookie发送至浏览器端
- 2.浏览器端发送第N(N>1)次请求到服务器端,浏览器端访问服务器端时就会携带该name为JSESSIONID的Cookie对象
- 3.服务器端根据name为JSESSIONID的Cookie的value来查询Session对象,从而区分不同用户
区别
Cookie | Session |
---|---|
Cookie数据存放在客户的浏览器上 | Session数据放在服务器上 |
Cookie不是很安全,别人可以分析存放在本地的Cookie进行Cookie欺骗 | 如果考虑到安全应当使用Session |
如果主要考虑到减轻服务器性能,应当使用Cookie | Session会在一定事件内保存在服务器上。当访问增多,会比较占用你服务器的性能 |
单个Cookie在客户端的限制是3K,一个站点在客户端存放的Cookie不能超过3K |
所以:将登录信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在Cookie中
Linux基础命令
NodeJS框架
如Express,Sail,KOA,Derby,Flatiron,Hapi,Mean.IO,Mojito,Egg.js,Midway,NEST等
MYSQL的索引类型
- 1.普通索引
- 2.唯一索引(值需要唯一)
- 3.主键索引(一张表只能有一个主键)
- 4.组合索引(最左前缀集合)
- 5.全文索引(搭配match,against使用)
Web Server
Webserver能够解析HTTP协议。当Webserver接收到一个HTTP请求,会返回一个HTTP响应,比如送回一个HTML页面。为了处理一个请求Webserver能够响应一个静态页面或图片,进行页面跳转或者把动态响应的产生托付给一些其他的程序比如CGI脚本,JSP脚本,servlets,ASP脚本,server端JavaScript,或者一些其他的server端技术。
这些server端的程序通常产生一个HTML的响应来让浏览器能够浏览。
常用的web服务器有Apache,Nginx,Lighttpd,Tomcat,IBM websphere等,其中应用最广泛的是Apache。而Windows NT/2000/2003平台下最常用的服务器则是IIS
Apache服务器
Apache仍然是世界上用的最多的Web服务器,市场占有率达60%;
它的优势在开源代码开放,可以运行在几乎所有的Unix,Linux,Windows系统平台上;
缺点在于消耗的内存页比其他的web服务器要高。
Nginx服务器
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like协议下发行。
特点是:占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:京东,新浪,网易,腾讯,淘宝等
Lighttpd服务器
Lighttpd服务器其目标是提供一个专门针对高性能网站,安全,快速,兼容性好并且灵活的Web Server环境。它具有内存开销低,CPU占用率低,效能好,以及模块丰富等特点。
Tomcat服务器
Tomcat服务器是一个开放源代码,运行servlet和JSP Web应用软件的基于java的Web应用软件容器。
IIS
IIS是一种web服务组件,其中包括Web服务器,FTP服务器,NNTP服务器和SMTP服务器,分别用于网页浏览,文件传输,新闻服务和邮件发送等方面,它使得在网络上发送信息成为一件很容易的事。但IIS只能运行在Windows平台,Linuex/Unix平台上
流行的框架
RequireJS和SeaJS的区别
- 1.相同之处
RequireJS和SeaJS都是模块加载器,倡导的是一种模块化开发理念,核心价值是让JavaScript的模块化开发变得更加简单自然 - 2.不同之处
区别 | RequireJS | SeaJS |
---|---|---|
定位有差异 | RequireJS想成为浏览器端的模块加载器,同时也想成为Rhino/Node等环境的模块加载器 | SeaJS则专注于Web浏览器端,同时通过Node扩展的方式可以很方便跑在Node服务器端 |
遵循的规范不同 | RequireJS遵循的是AMD(异步模块定义)规范 | SeaJS遵循的是CMD(通用模块定义)规范SeaJS的API更简洁优雅,更贴近CommonJS Modules/1.1 和Node Modules规范 |
社区理念有差异 | RequireJS在尝试让第三方类库修改自身来支持RequireJS,目前只有少数社区采纳 | SeaJS不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略 |
代码质量有差异 | RequireJS是没有明显的bug | SeaJS是明显没有bug |
对调试等的支持有差异 | 无支持 | SeaJS通过插件,可以实现Fiddler中自动映射的功能,还可以实现自动combo等功能,非常方便 |
插件机制不同 | RequireJS采取的是在源码中预留接口的形式,源码中留有为插件而写的代码 | SeaJS采取的是插件机制则与JavaScript语言以及Node的方式一致:开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件 |
amd和cmd的区别
AMD | CMD |
---|---|
AMD是在RequireJS推广过程中对模块定义的规范化产出 | CMD式SeaJS在推广过程中对模块定义的规范化产出 |
对于依赖的模块,AMD提前执行(2.0开始页可以延迟执行) | CMD延迟执行 |
推崇依赖前置 | 推崇依赖就近 |
AMD的API默认是一个当多个用 | CMD的API严格区分,推崇职责单一 |
如何理解的reactJS和vueJS?
Vue和React存在着很多的共同点:
- 数据驱动试图
- 组件化
- 都使用Virtual DOM
共同点 | 详细 |
---|---|
数据驱动视图 | 在jquery时代,我们需要频繁的操作DOM来实现页面效果与交互;而Vue和React解决了这一痛点,采用数据驱动视图方式,隐藏操作DOM的频繁操作。所以我们在开发时,值需要关注数据变化即可,但是二者实现方式不尽相同 |
组件化 | React与Vue都遵循组件化思想,它们把注意力放在UI层,将页面分成一些细块,这些块就是组件,组件之间的组合嵌套就形成最后的网页界面。所以在开发时都有相同的套路,比如都有父子组件传递,都有数据状态管理,前端路由,插槽等。 |
Virtual DOM | Vue与React都使用了Virtual DOM+Diff算法,不管是Vue的Template模板+option api写法,还是React的Class或者Function写法最后都是生成render函数,而render函数执行返回VNode(虚拟DOM的数据结构,本质上是颗树) |
不同点
Vue和React两者虽然都是用于构建用户界面的框架,但是也有很大的差异,首先二者核心的思想就不同
- 核心思想不同
- 组件写法差异
- diff算法不同
- 响应式原理不同
1.核心思想不同
Vue定位就是尽可能降低前端开发的门槛,让更多的人能够更快速地上手开发。这就有了vue的主要特点:灵活易用的渐进式框架,进行数据拦截/代理,它对侦测数据的变化更敏感,更精确。
React从一开始的定位就是提出UI开发的新思路。React推崇函数式编程(纯组件),数据不可变以及单向数据流,当然需要双向的地方页可以手动实现,比如借助onChange和setState来实现。
由于两者核心思想的不同,所以导致Vue和React在后续设计产生了许多的差异
2.组件写法差异
React推荐的做法是JSX+inline style,也就是把HTML和CSS全都写进JavaScript中,即all in js
Vue推荐的做法是template的单文件组件格式(简单易懂,从传统前端转过来易于理解),即html,css,JS写在同一个文件(vue也支持JSX写法)
这个差异一定程序上也是由于二者核心思想不同而导致的
3.diff算法不同
两者流程思路上是类似的:
- 不同的组件产生不同的DOM结构。当type不相同时,对应DOM操作就是直接销毁老的DOM,创建新的DOM
- 同一层次的一组子节点,可以通过唯一的key区分
React的Diff算法核心
-
react首先对新集合进行遍历,for( name in nextChildren)。
-
通过唯一key来判断老集合中是否存在相同的节点。如果没有的话创建
-
如果有的话,if (preChild === nextChild )
-
会将节点在新集合中的位置和在老集合中lastIndex进行比较
-
如果if (child._mountIndex < lastIndex) 进行移动操作,否则不进行移动操作。
-
如果遍历的过程中,发现在新集合中没有,但在老集合中有的节点,会进行删除操作
-
Vue的Diff算法核心
-
旧children和新children各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。
-
如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明旧children和新children至少有一个已经遍历完了,就会结束比较。
Vue2的核心Diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。
4.响应式原理不同
Vue
- Vue依赖收集,自动优化,数据可变
- Vue递归监听data的所有属性,直接修改
- 当数据改变时,自动找到引用组件重新渲染
React
React基于状态机,手动优化,数据不可变,需要setState驱动新的state替换老的state。当数据改变时,以组件为根目录,默认全部重新渲染,所以React中会需要shouldComponentUpdate这个声明周期函数方法来进行控制
5.其他不同点
Vue为了更加简单易用,引入了指令,filter等概念以及大量的option API,比如watch,computed等都是非常好用的。
而React的API比较少,如果你的JS基础比较好,上手也是比较容易的
性能优化
性能优化常用的方式方法
懒加载,cdn加速,打包之类的,防抖节流
网页内容 | 服务器 | JavaScript | css | Cookie | 移动客户端 | 图片 |
---|---|---|---|---|---|---|
减少http请求次数 | 使用CDN | 将脚本置底 | 将样式表置顶 | 减少Cookie大小 | 保持单个内容小于25kb | 优化图像 |
减少DNS查询次数 | 添加Expires或Cache-Control报文头 | 使用外部Javascript和css文件 | 避免css表达式 | 页面内容使用无cookie域名 | 打包组建成符合文档 | 优化CSS Sprite |
避免页面跳转 | Gzip压缩传输文件 | 精简Javascript和css | 用<\link>代替@import | 使用小且可缓存的favicon.ico | ||
缓存Ajax | 配置ETags | 去除重复脚本 | 避免使用Filters | |||
延迟加载 | 尽早flush输出 | 减少DOM访问 | ||||
提前加载 | 使用GET Ajax请求 | 使用智能事件处理 | ||||
减少DOM元素数量 | 避免空的图片src | |||||
根据域名划分内容 | ||||||
减少iframe数量 | ||||||
避免404 |
设计模式
单例模式
关键点有4个:
- 1.私有构造函数
- 2.声明静态单例对象
- 3.构造单例之前要加锁
- 4.需要2次检查单例实例是否为空,分别在锁之前和所之后
为何要检测2次?
有可能延迟或者缓存的原因,造成构造了多个实例,违反了单例的初衷
构造函数能否公有化?
不,单例类的构造函数必须私有化,单例类不能被实例化,只能被静态调用
lock住的对象为什么要是object对象,可以是int型吗?
不行,锁住的必须是个引用类型,如果每个不同的线程在声明的时候值类型变量的地址都不一样,那么上个线程锁住的东西,下个线程进来会认为根本没有锁,相当于每次都锁了不同的们。而引用类型的变量地址是相同的,每个线程进来判断锁都是判断同一个地址,相当于锁在同一扇门,起到了锁的作用
工厂模式
核心功能:根据 需求 生产 产品
核心思想:解耦 需求 工厂 和 产品
实际上根据业务情景不同分为不同的实现方式。一般分为3中:简单工厂,工厂,抽象工厂
工厂模式中,一个工厂生产一个产品,所有产品派生于同一个抽象产品(或产品接口);而抽象工厂模式中,一个工厂生产多个产品,它们是一个产品族,不同的产品族的产品派生于不同的抽象产品(或产品接口)。
观察者模式
两个角色:观察者和被观察者
就像:守卫们盯着囚犯,一旦囚犯动,守卫们就必须马上采取行动。(守卫:观察者)。
- 关键点1:每个观察者需要被保存到被观察者的集合中,并且被观察者提供添加和删除的方式
- 关键点2:被观察者把自己传给观察者,当状态改变后,通过遍历或循环的方法诸葛通知列表中的观察者
- 关键点3:虽然解耦了观察者和被观察者的依赖,让各自的变化不怎么影响另一方的变化,但是这种解耦并不是很彻底,没有完全接触两者之间的耦合
- 关键点4:在事件中,订阅者和发布者之间是通过把事件处理程序绑定到委托,并不是把自身传给对方。所以解决了观察者模式中不完全解耦的问题
通过委托绑定方法来实现观察者模式,会有什么隐患?
有的,通过+=方法绑定到委托,很容易忘记-=。 如果只绑定不移除,这个方法会一直被引用。我们知道GC去回收的时候,只会处理没有被引用的对象,只要是还被引用的对象就不会被回收掉。所以如果在长期不关闭的系统中(比如监控系统),大量的代码使用+=而不-=,运行事件长以后可能会内存溢出。
事件,委托,观察者之间的关系
委托是一种类型,事件是一种特殊的委托,观察者模式是一种设计模式,事件的机制是观察者模式的一种实现,其中订阅者和发布者通过委托实现协同工作。
安全
什么是XSS漏洞,怎么防御?
全称:Cross SiteScript
跨站脚本攻击
简写:CSS看起来和我们熟知的CSS是一样的,有些温柔了,就把C换成了X,增强文字对这种攻击的强度描述
XSS是一种网站应用程序的安全漏洞攻击,是代码注入的一种,它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。这种类型的漏洞由于被黑客用来编写危害性更大的网络钓鱼攻击而变得广为人知。
XSS攻击的主要目是是,想办法获取目标攻击网站的cookie,因为有了cookie相当于有了session,有了这些信息就可以在任意能接进互联网的pc登录该网站,并以其他人的身份登录,做一些破坏。
XSS的危害
- 1.盗取各类用户账号,如及其登录账号,用户网银账号,各类管理员账号
- 2.控制企业数据,包括读取,篡改,添加,删除企业敏感数据的能力
- 3.盗窃企业重要的具有商业价值的资料
- 4.非法转账
如何防御?
方法 | 解释 |
---|---|
HttpOnly防止劫取Cookie | HttpOnly最早由微软提出,至今已经成为一个标准。浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。目前主流浏览器都支持,HttpOnly解决是XSS后的Cookie支持攻击。 |
输入检查 | 例如网站注册经常用户名只允许字母和数字的组合,或者邮箱电话,我们会在前端用js进行检查,但在服务器端代码必须再次检查一次,因为客户端的检查很容易绕过。 |
输出检查 | 大多人都知道输入需要做检查,但却忽略了输出检查。 |
处理富文体 | 设置好白名单,严格控制标签 |
防御DOM Based XSS | DOM Based XSS是从javascript中输出数据到HTML页面里。 |
什么是CSRF攻击?怎么防御?
CSRF:Cross Site Request Forgery
(跨站点请求伪造)。
CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
如何防御?
- CSRF 漏洞进行检测的工具,如 CSRFTester、CSRF Request Builder…
- 验证 HTTP Referer 字段
- 添加并验证 token
- 添加自定义 http 请求头
- 敏感操作添加验证码
- 使用 post 请求