5.作用域、作用域链、闭包(★★★★)
作用域
作用域指的是一个变量和函数的作用范围,在ES6中,只有全局作用域和局部作用域,但是没有块级作用域,并且局部变量的优先级高于全局变量。
1.变量提升
将var声明的变量提升到所在作用域的顶部。(赋值不提升)
2.函数提升
将函数声明式创建的函数提升到文件的最开始。
3.没有块级作用域
作用域链
1.查找变量时,先从 当前上下文变量对象中查找
2.没找到,从父级执行上下文的变量对象中查找
3.一直找到全局上下文的变量对象(全局对象)
作用域链是一个对象列表,他的作用就是保证了变量对象的有序访问。这样由多个执行上下文的变量对象构成的叫作用域链。 作用域链开始的地方:当前代码执行环境的变量对象,常被称之为“活动对象”(AO),变量的查找会从第一个链的对象开始,如果对象中包含变量属性,那么就停止查找,如果没有就会继续向上级作用域查找,直到找到全局对象中,如果再找不到就会报ReferenceError。
闭包
1.闭包就是个处于其他函数内部的函数。
2.在对外部函数的变量进行引用,内部函数就叫闭包 。
3.这个闭包(内部函数)在外调用时,其所在外部函数的作用域不会被销毁。
4.闭包中会将外部函数的自由对象添加到自己作用域中,所以可以通过内部函数访问外部函数的属性,这就是js模拟私有变量的一种方式。
缺点:
1.闭包在占内存多,用多了会使内存消耗和处理速度有负面影响。
2.作用链的影响,闭包只能取得内部函数最后一个值。(
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0](); // 3
data[1](); // 3
data[2](); // 3
————————————————
i不会被重复定义,在迭代中不断更新,闭包只取到最后一个值
闭包用途
访问函数内部变量 防止函数内部的变量执行完程序之后被销毁,使其一直保存在内存当中,这也是他的缺点。过度使用可能导致内存泄漏解决闭包副作用
由于作用链的影响闭包只能取得内部函数的最后一个值,如果内部函数在一个循环中,那么变量的值始终为最后一个,代码如上不再重复,要解决这个问题,有三个办法:
(1)方法一:立即执行函数
(2)方法二:返回一个匿名函数赋值
(3)方法三:使用ES6的let
详细请参考
6.箭头函数、普通函数作用和区别(★★★★★)
普通函数 | 箭头函数 |
---|---|
不是匿名函数 | 是匿名函数 |
能用于构造函数,创建实例 | 不能用于构造函数,使用new调用会报错,因为箭头函数没有constructor ,不具有new.target。 |
有prototype(原型) | 没有prototype(原型) |
this总是指向调用它的对象,如果用作构造函数,this指向创建的对象实例。 | this继承自外层第一个普通函数的this。如没有,this都会指向window(全局对象)。也可以说箭头函数本身没有this,但是它在声明时可以捕获其所在上下文的this供自己使用,this一旦被捕获,就不再发生变化 |
call() , bind() , apply()可以改变this的指向 | 任何方法都改变不了其指向,如 call() , bind() , apply() |
调用后都具有一个arguments对象,用来存储实际传递的参数。 | 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替。 |
可以yield命令,用作 Generator 函数 | 不可以使用yield命令,因此箭头函数不能用作 Generator 函数 |
7.call、apply、bind作用和区别(★★★★★)
作用:
call() apply() bind()都是用来重定义this指向的对象,都是改变函数的执行上下文!
区别:
方法 | 使用方法 |
---|---|
.call() | 第一个参数是this需要指向的对象;第2,3…个参数是以逗号隔开 |
.apply() | 第一个参数是this需要指向的对象;第2个参数是数组 |
.bind() | 第一个参数是this需要指向的对象;第2个参数是数组;不会马上指向 ,要加括号调用 |
8.new的过程(★★★★)
1.new操作符新建一个空对象person1
2.将这个对象的原型指向构造函数的prototype,person.proto=Person.prototype;
3.执行构造函数,Person.apply(obj)
4.返回这个对象 。