执行上下文
当 JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。
可执行代码
这就要说到 JavaScript 的可执行代码(executable code)的类型有哪些了?
其实很简单,就三种,全局代码、函数代码、eval代码。
举个例子,当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做”执行上下文(execution contexts)”。
对于每个执行上下文,都有三个重要属性:
- 变量对象(Variable object,VO)
- 作用域链(Scope chain)
- this
执行上下文的创建阶段,会分别生成变量对象,建立作用域链,确定this指向,谨记this的指向,是在函数被调用的时候确定的。也就是执行上下文被创建时确定的。且在函数执行过程中,this一旦被确定,就不可更改了
this 并不是取决于他所在的位置,而是取决于他所在的Function是如何调用的
构造函数中的 this ,就是指向即将实例化的那个对象。谨记!
全局上下文
在全局执行上下文中this都指代全局对象。
在浏览器里面this等价于window对象,如果你声明一些全局变量,这些变量都会作为this的属性。
1 | // 通过this绑定到全局对象 |
函数上下文,函数中的this
- demo注意这里的
1
2
3
4
5
6
7
8
9
10
11var a = 20;
var obj = {
a: 10,
c: this.a + 20,
fn: function () {
console.log(this)
return this.a;
}
}
console.log(obj.c);
console.log(obj.fn());
obj: {
c: this.a + 20
}单独的{}不会形成新的作用域,因此这里的this.a,由于并没有作用域的限制,它仍然处于全局作用域之中。所以这里的this其实是指向的window对象。
直接调用
this指向全局变量, this === window
1 | function foo(){ |
作为对象的一个方法
this指向调用函数的对象
1 | var person = { |
稍不留心写成了
1 | var person = { |
这是因为下面要说的箭头函数
箭头函数
所有的箭头函数都没有自己的this,都指向外层,
1.没有自己的this、super、arguments和new.target绑定。
2.不能使用new来调用。
3.没有原型对象。
4.不可以改变this的绑定。
5.形参名称不能重复。
MDN中的解释
An arrow function does not create its own this, the this value of the enclosing execution context is used.
箭头函数会捕获其所在上下文的this值,作为自己的this值。
1 | function Person(name){ |
哪些场景下不能使用箭头函数
1.在一个对象上,定义一个指向函数的属性,当方法被调用时,方法内的this指向方法所属的对象
1 | const calculator = { |
2.定义原型方法
3.定义事件回调函数
4.定义构造函数
作为一个构造函数
this被绑定到正在构造的实例中
1 | function Person(name){ |
this绑定优先级
优先级是new 调用 > call、apply、bind 调用 > 对象上的函数调用 > 普通函数调用。
1 |
|
最后一个执行报错
这是因为函数内部有两个不同的方法:[[Call]]和[[Constructor]]。
当使用普通函数调用时,[[Call]]会被执行。当使用构造函数调用时,[[Constructor]]会被执行。call、apply、bind和箭头函数内部没有[[Constructor]]方法。
总结
- new 调用:绑定到新创建的对象,注意:显示return函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象。
- call 或者 apply( 或者 bind) 调用:严格模式下,绑定到指定的第一个参数。非严格模式下,null和undefined,指向全局对象(浏览器中是window),其余值指向被new Object()包装的对象。
- 对象上的函数调用:绑定到那个对象。
- 普通函数调用: 在严格模式下绑定到 undefined,否则绑定到全局对象。