一、声明提升
1.变量声明提升
js分为预编译和执行阶段,变量提升在预编译阶段进行,函数声明优先于变量声明,且不会被变量声明覆盖,赋值的时候,会被盖掉。
使用var声明的变量,会被提升到当前作用域的最顶端先赋值一个undefined的值。总的来说,就是先被声明(赋值一个undefined),实际的赋值操作留在原地执行。
那么当前作用域和顶层作用域都生命了同一变量,就近原则,距离最近的变量优先,每个var都会将变量声明提前。只有在全局声明的变量,会被复制到window对象上一份。
2.函数声明提升
函数的种类:声明式函数、赋值型函数、匿名函数、自执行函数
声明式函数
赋值型函数
JS解析分为两个阶段:预编译和执行。
预编译:对当前js文件中所有的声明变量和函数进行处理,此时处理的只有【声明式函数】直接声明并且赋值,变量也只是进行声明,并未初始化和赋值。
重点: 1. 都是函数声明的情况下,下面会覆盖上面的函数
2. 执行在最前,声明函数 > 赋值函数
执行在最后,那么赋值型函数 > 声明式函数
3. js加载代码块是按照顺序的,如果【声明所在】的文件在【调用所在】的文件下面,那么调用会报错。
4.函数声明 > 变量声明,会优先把标识符给函数用。
5.如果变量在函数声明前被赋值,那么变量就不会被函数覆盖
3.变量提升VS函数提升
函数提升要比变量提升的优先级要高一些,且不会被变量声明覆盖,但是会被变量赋值之后覆盖
即使变量在if不通过的函数体里,也会被提升
二、JS作用域
1.局部作用域
局部变量和参数重名时,同等优先级,默认访问当前位置之前最近的值。
2.全局作用域
js文件内的任何地方直接对变量赋值,不适用var声明的变量,默认为全局变量。
var a = b = 10; => b = 10; var a = b;就是默认b为全局变量,a为局部变量
3.ES6块级作用域
三、经典面试题
四、移动端H5点击穿透
pc端的点击事件: mousedown -> click -> mouseup
h5的点击事件:手机上无鼠标,所以使用touch代替:touchstart -> touchmove -> touchend -> tap -> click 。
touchend结束后,会延迟300ms左右出发click事件。这是因为手机上有双击事件,如果300ms内发生了第二次click,就会直接出发dbclick,而不是click事件了。所有click 、dbclick都是在点击300ms之后又进行确认触发的。
那么点击穿透的意思就是:在弹层的关闭按钮触发【touchend / tap】事件之后,弹层立即关闭,但是300ms后,下面的元素会触发自己的click事件。
解决办法: 1. 遮挡,确保300ms左右,弹层没有完全消失。例如zepto的fadeOut,动画时间是300ms。
2. css3,pointer-events: none;元素不再是鼠标事件的目标,就是click事件不会触发了。意思就是在触发【touchend / tap】事件时,下面的元素pointer-events置成none,300ms之后再改回auto;
五、改变this的指向
this的指向只有在被调用的时候才能确定。永远指向最后被【调用】的对象
1. 改变this的指向
1.1 new一个对象实例,this指向该实例,优先级最高
1.2 显式绑定(硬绑定),优先级次之
call / apply: 参数从第二位开始,按顺序传入。立即执行。
bind:方法会返回执行上下文,不会立即执行,方法调用的时候才会执行。
硬绑定的this不可二次修改
1.3 默认绑定:优先级再次
函数独立调用,就是fun()
前面不会出现.调用的情况,此时fun内的this指向window全局。在严格模式下,默认绑定的this不会是全局对象,而是undefined。默认绑定只出现在非严格模式
1.4 隐式绑定:优先级最次
会把函数调用中的this,绑定到上下文对象
距离函数执行最近的那个上下文对象,会被绑定到this上。上面的例子就是obj离的最近,那么foo里面的this.a就是2
隐式丢失:函数的赋值,赋的是函数的引用,所以
bar在执行的时候,是直接执行fun()方法,所以this绑定的是全局
1.5 胖箭头函数
不遵循上面的四种原则,而是根据最近的外层函数的作用域来决定,this指向无法修改,new也不行哦。
六、AJAX的请求步骤
1. 创建XMLHttpRequest对象
2.创建HTTP请求
配置HTTP请求参数
3.配置响应回调
在发送之前,最好先配置一下http请求之后的回调函数。
http请求的状态变化会触发onreadystatechange事件,判断XMLHttpRequest.readyState的状态,等于4 的时候,数据接收完成。
4.获取返回的数据
5.发送请求
七、instanceOf
instanceOf是判断一个对象是不是由另一个对象new出来的实例,是怎么实行的呢?
会递归查找L的原型链,即:L.__proto__.__proto__.__proto__...,直到找到了R.prototype或者找到了顶层为止,所以instanceOf的运算规则就是,判断左侧的__proto__原型链上,是否存在R.prototype
八、遍历
8.1 forEach
回调中支持三个参数,arr.forEach(item, index, arr){ }
jq中的$.each / $.map,第一个和第二个参数是相反的。
$.each( index, item, arr) / $.map( index, item, arr)
8.2 map
回调函数一定要return值
8.3 filter
回调一定要返回若等于true 、false的值。值为true才能被留下。
8.4 for... in...循环出来的是下标
8.5 for...of... 循环出来的是value
九、回流和重绘
dom的样式会影响布局的,例如width / height,会引起回流,重新render.
dom的样式不会影响布局的,例如color / background-color。会引起重绘
回流一定会引起重绘,但重绘不一定会引起回流
回流的代价高于重绘的代价
十、进制转换
10.1 各种进制转到十进制
num = parseInt(arg, params);第一个参数是待转换的参数,第二个是当前的进制
10.2 十进制转其他进制
num.toString(param); 参数是需要转换成的进制
10.3 其他进制的互相转化,以十进制作为桥梁
十一、循环
11.1 for ... in ...
事实上遍历的属性名。
11.2 for ... of ...
只循环元素本身,修复了for ... in ... 的遗留问题。
11.3 forEach接受回调函数
十二、继承
因为js跟java / c..不同,继承的实现更是不一样。
12.1 原型链继承
利用原型链实现继承,超类的一个实例作为子类的原型。
缺点:* 原型对象的引用属性是所有实例共享,一改则全改,影响其他子类
* 单纯的原型链继承,无法向父类构造函数传参
当前的原型链:子实例 -> 父实例 -> 父类的prototype -> ...Object.prototype
确定实例与原型的关系,child instanceof parent。只要parent出现在了child的原型链上,就会返回true。
parent.prototype.isPrototypeOf(child),结果同上
12.2 构造函数继承
利用call 、apply将父类构造函数引入子函数,再子函数内,执行了父函数的构造代码。
缺点:* 只能继承父类构造函数的属性,父类原型上的属性以及方法都继承不到。
* 无法实现构造函数的复用
* 每个实例都有父类构造器函数的副本,太臃肿。
12.3 组合继承
原型链继承+构造函数继承
缺点: * 调用了两次父类构造函数,耗内存,屏蔽了原型中的同名属性
12.4 原型式继承
这继承方式也...不是跟原型链继承差不多么,就是一个创建继承的工厂函数而已...
12.5 寄生式继承
在原型继承套了一层壳子,对原型继承方法返回的对象进行深加工,二次返回。只继承父类上的属性。
Object.create(pro)方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。proto表示新建对象的原型对象,即该参数会被赋值到目标对象的原型上,返回在指定原型对象上添加新属性后的对象
12.6 寄生组合式继承
父类的属性方法 、 父类原型的属性方法分开继承。(寄生继承+构造函数继承)
首先继承父类原型上的属性,使用Object.creat(superType.prototype);
然后关联子类,作为子类的原型对象
十三、防抖和节流
13.1 防抖
多次触发同一事件后,事件处理函数只执行一次,并且是在触发操作结束时执行。
例如:scroll / resize / keyup
13.2 节流
在一直触发事件的情况下,希望n秒内执行一次,而不是在最后一次触发之后执行。
十四、你不知道的对象
14.1 6种主要数据类型
string / number / boolean / undefined / null / object / symbol
14.2 除对象外,其他统称为基本类型
string / boolean / number / null / undefined / symbol
typeof null; // object 由来已久的bug,null是基本类型中唯一一个假值类型
ps:函数的length属性是形参的个数
14.3 内置对象
String / Number / Boolean / Object / Array / Function / Date / RegExp / Error
14.4 复制对象
浅拷贝
引用对象的赋值,只是赋值引用
Object.assign({},obj1);
深拷贝
引用对象的赋值,会重新拷贝一份。JSON安全的对象来说,巧妙方法就是
JSON.parse(JSON.stringify(obj))
14.5 Object.defineProperty()
可对属性描述符进行设置
十五、JSON.stringify()
1.遇到undefined / function(){} / symbol会自动忽略,所以在深拷贝的时候,不能拷贝带有这几个值
十六、微任务 && 宏任务
微任务 => render => eventLoop => 下一个宏任务
微任务都是ES6语法规定的
宏任务都是W3C(浏览器)规定的
同步任务 -> 微任务队列 -> 渲染 -> eventLoop
微任务是js的,宏任务的window的api
十七、同源策略
只针对ajax,img,css,js无视同源策略
JSONP:
CORS:
十八、状态码
301:永久,跳转到response里的location
302:临时,跳转到response里的location
504:网关超时
十九、Restful api
不用url参数:/api/list/2