this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。
this提供了一种更优雅的方式来隐式‘传递’一个对象的引用,可以使API更为简洁和复用。
this的作用域
this是在运行时进行绑定的,并不是在编写时绑定,其上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式(即函数在哪里调用)。
默认绑定
在非严格模式下,独立函数调用时,this默认是指向全局对象的。
var a = 0;
function foo(){
console.log(this.a);
}
foo(); //0
如果使用严格模式( strict mode ),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefined
var a = 0;
function foo(){
'use strict'
console.log(this.a);
}
foo(); //TypeError: Cannot read property 'a' of undefined
隐式绑定
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
obj.foo(); //2
函数foo()在执行时,其所属对象为obj。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。当执行obj.foo()时,this指向obj。
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
var obj1 = {
a:3,
obj:obj
}
obj1.obj.foo(); //2
显式绑定
JavaScript 提供的绝大多数函数以及你自己创建的所有函数都可以使用 call(..) 和 apply(..) 方法,来将函数调用时的this执行绑定的对象上。
var a = 0;
function foo(){
console.log(this.a);
}
var obj = {
a:2
}
var obj1 = {
a:3,
foo:foo
}
obj1.foo.call(obj); //2
new
在 JavaScript 中,构造函数只是一些使用 new 操作符时被调用的函数(普通的函数)。
构造函数在被调用时,会执行以下操作:
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行
原型
连接。 - 这个新对象会绑定到函数调用的 this 。
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
其中第3步,则改变了函数调用的this指向,即新对象
function foo(m){
this.a = m;
}
var obj = {}
var obj1 = foo.bind(obj);
obj1(2);
console.log(obj.a); //2
var obj2 = new obj1(3);
console.log(obj.a); //2
console.log(obj2.a); //3
this绑定的四条判断规则
判断函数在运行中的this的绑定,就需要找到这个函数的直接调用位置。然后按下面四条规则来判断this的绑定对象。
- 由 new 调用?绑定到新创建的对象。
- 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。
- 由上下文对象调用?绑定到那个上下文对象。
- 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。
关于箭头函数
ES6中的箭头函数是根据外层(函数或者全局)的作用域来决定this的。
var obj = {
say: function () {
setTimeout(function () {
console.log(this);
}, 0)
}
}
obj.say(); // window
我们知道,超时函数的回调中的this是一般是指向全局的,如果我们想在回调中使用obj的对象或者方法时,可以通过声明一个变量that代替this在回调中使用。
var obj = {
say: function () {
let that = this;
setTimeout(function () {
console.log(that);
}, 0)
}
}
obj.say(); // obj
ES6可以使用箭头函数来实现同样的效果。
var obj = {
say: function () {
setTimeout(() => {
console.log(this);
}, 0)
}
}
obj.say(); // obj