当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文。
对于每个执行上下文,都有三个重要属性
- 变量对象
- 作用域链
- this
this 是什么
this既不指向函数自身,也不指向函数的词法作用域。它实际是在函数被调用时才发生的绑定,this具体指向什么,取决于你是怎样调用的函数。
this 的四种绑定规则
- 默认绑定
- 隐式绑定
- 显示绑定
- new 绑定
- 箭头函数
默认绑定
function foo(){
console.log(this.a);
}
var a = 2;
foo();
打印结果为2.
因为foo()是直接在全局调用的,进行了默认绑定,this指向了window。
注意:在严格模式下,全局对象将无法使用默认绑定,执行会保undefined错误
function foo(){
'use strict';
console.log(this.a);
}
var a = 2;
foo();
// TypeError: Cannot read property 'a' of undefined
隐式绑定
有时候,函数的调用是在某个对象上触发的
function foo() {
console.log(this.a);
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
obj.foo();
obj.foo 打印结果是3,foo通过obj调用,此时this指向obj对象,访问obj的属性a。如果obj没有属性a,也不会向上访问全局的a,此时打印为undefined。
多层调用链
function foo(){
console.log(this.a);
}
var a = 2;
var obj1 = {
a: 4,
foo: foo
};
var obj2 = {
a: 3,
obj1: obj1
};
obj2.obj1.foo();
obj2.obj1.foo 打印结果为4。
这里调用链不只一层,存在obj1、obj2两个对象,但foo中的this会指向最近一次的调用者 ---- obj1。
隐式丢失(函数别名)
function foo(){
console.log(this.a);
}
var a = 2;
var obj = {
a: 3,
foo: foo
};
var bar = obj.foo;
bar();
打印结果是2。尽管obj隐式绑定了foo,但将其赋值给bar,而bar在全局调用,所以访问全局的a。
显式绑定
通过call() 、apply()、bind()来实现。
function foo(){
console.log(this.a);
}
var a = 2;
var obj1 = {
a: 3
}
var obj2 = {
a: 4
}
foo.call(obj1);
foo.apply(obj2);
foo.bind(obj1)();
打印结果是3,4。
因为这里显式的申明了要绑定的对象,所以this就被绑定到了obj1和obj2
new 绑定
如果函数没有返回其他对象,那么函数中的this指向new 返回的这个对象。
function Foo(a){
this.a = a;
}
var a = 2;
var bar1 = new Foo(3);
console.log(bar1.a); // 3
var bar2 = new Foo(4);
console.log(bar2.a); // 4
箭头函数
箭头函数本身不存在this,this指向上一级对象
看一个例子
function Person(){
this.age = 0;
setTimeout(function(){
this.age ++;
console.log(this,this.age); // this指向window
},1000)
}
let person = new Person();
如果我们想在计时器中访问Person的成员属性,通常会这样做
function Person(){
this.age = 0;
let self = this;
setTimeout(function(){
self.age ++;
console.log(self,self.age); // this指向Person
},1000)
}
let person = new Person();
使用箭头函数可以达到这样效果,将this指向上一级对象
function Person(){
this.age = 0;
setTimeout(() => {
this.age ++;
console.log(this,this.age); // this指向Person
},1000)
}
let person = new Person();
网友评论