作用域链/闭包/变量提升
作用域链
- 每个执行上下文的变量环境中,都包含了一个
引用outer,指向外部的执行上下文。当一段代码使用了一个变量时,js引擎会先在当前的执行上下文中查找,如果没有找到,js引擎继续在outer所指向的js引擎中查找,这个查找的链条就叫是作用域链。
闭包
- 定义
- 根据词法作用域的规则,内部函数总是可以访问外部函数的中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数
引用外部函数的变量仍然保存在内存中,这些变量的集合就是闭包 - 闭包的外部作用域是在其
定义的时候已决定,而不是执行的时候。 - 变量的生命周期取决于闭包的生命周期。被闭包引用的外部作用域中的变量将一直存活直到闭包函数被
销毁。如果一个变量被多个闭包所引用,那么直到所有的闭包被垃圾回收后,该变量才会被销毁。
- 根据词法作用域的规则,内部函数总是可以访问外部函数的中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数
- 使用场景
- 在异步任务例如
timer定时器,事件处理,Ajax请求中被作为回调 - 被外部函数作为返回结果返回,或者返回结果对象中引用该内部函数
- 维护一些私有变量
- 在异步任务例如
一段JavaScript代码是如何执行的
- JavaScript执行机制是
先编译,再执行
- 所有在编译阶段 会生成
执行上下文(变量环境和词法环境)和可执行代码 - 在执行上下文中会有一个
变量环境对象 - 函数提升要比变量提升的
优先级要高一些,且不会被变量声明覆盖,但是会被变量赋值之后覆盖。
变量提升
- JavaScript 代码执行过程中,需要先做变量提升。而之所以需要实现变量提升,是因为 JavaScript 代码在执行之前需要
先编译。在编译阶段,变量和函数会被存放到变量环境中,变量的默认值会被设置为undefined;在代码执行阶段,JavaScript 引擎会从变量环境中去查找自定义的变量和函数。如果在编译阶段,存在两个相同的函数,那么最终存放在变量环境中的是最后定义的那个,这是因为后定义的会覆盖掉之前定义的。
原型/原型链
原型
- 定义:
JS 原型是指为其它对象提供
共享属性访问的对象。在创建对象时,每个对象都包含一个隐式引用指向它的原型对象或者 null。- 每个函数function都有一个prototype,即
显式原型 - 每个实例对象都有一个__proto__,可称为
隐式原型 - 对象的隐式原型的值为其对应
构造函数的显式原型的值
- 每个函数function都有一个prototype,即
原型链
-
定义:原型链是由原型对象组成,每个对象都有
__proto__属性,指向创建该对象的构造函数的原型,__proto__将对象连接起来组成原型链,是一个用来实现继承和共享属性的有限的对象链属性查找机制:当查找对象的属性时,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,找到则输出,不存在时,再继续沿着原型链往上一级查找,直至最顶级的原型对象 Object.prototype,如果还是没有找到,则输出undefined属性修改机制:只会修改实例对象本身的属性,如果不存在,则进行添加改属性,如果需要修改原型的属性时,则可以用obj.prototype.x = 1,但是这样会造成所有继承与该对象的实例属性的改变
-
如何利用原型实现继承
- 一种是通过
Object.create (proto[, propertiesObject])或者Object.setPrototypeOf (obj:要设置其原型的对象, prototype:该对象的新原型)显式继承另一个对象,将它设置为原型。 - 另一种是通过
constructor构造函数,在使用new关键字实例化时,会自动继承constructor的prototype对象,作为实例的原型。
- 一种是通过
-
继承的几种方式
- 可以看我之前的总结 juejin.cn/post/689277…
this/call/apply/bind/箭头函数
this
-
定义
- this 关键字执行为当前执行环境的
ThisBinding - this的指向是
调用时决定的,而不是创建时决定的
- this 关键字执行为当前执行环境的
-
调用位置
-
全局上下文(非严格模式下)
- this等价于window对象
var === this. === winodw.
-
函数上下文
-
在函数内部,this的值取决于函数
被调用的方式。
-
直接调用
this指向全局变量。 -
call()、apply()
this指向绑定的对象上。 -
bind()
this将永久地被绑定到了bind的第一个参数。 -
箭头函数
- 箭头函数适合与
this无关的回调,定时器,数组的回调 - 没有自己的
this,arguments,super或new.target。 - 它的所谓的this是捕获其所在上下文的 this 值,作为自己的this值,并且由于没有属于自己的this
this是静态的,this始终指向函数声明时所在作用域的this的值,不能用apply和call改变this指向- 箭头函数表达式更适用于那些本来需要
匿名函数的地方,并且它不能用作构造函数。 - 对于
methods和watch中的方法官方不推荐使用箭头函数,理由是:“箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向Vue 实例,this.a 将是undefined”。
- 箭头函数适合与
-
作为对象的一个方法 this指向
调用函数的对象 -
在
严格模式下的函数调用下, this指向undefined -
作为一个构造函数
this被绑定到正在构造的
新对象。 -
作为一个DOM事件处理函数
this指向触发事件的元素,也就是始事件处理程序所绑定到的
DOM节点。 -
HTML标签内联事件处理函数
this指向所在的
DOM元素 -
jQuery的this
在许多情况下JQuery的this都指向
DOM元素节点。 -
map的
第二个参数,改变this的指向
-
-
call/apply/bind有什么区别
- call:改变this指向,第二个参数为
参数列表,函数执行 - apply:改变this指向,第二个参数为
数组,函数执行 - bind:改变this指向,第二个参数为
参数列表,返回函数
如何实现call、apply、bind
- call
- apply
- bind