关于this的绑定规则

这篇文章主要介绍一下this的四种绑定规则。
本文内容源自:

你不知道的javascript



在讲this的绑定规则之前我们先总结一下this:

  • this 既不指向函数自身也不指向函数的词法作用域
  • this 实际上是在函数被调用时发生的绑定, 它指向什么完全取决于函数在哪里被调用。

既然this的绑定取决于在哪被调用,那么首先我们需要先找到this的调用位置,查看函数调用栈的倒数第二个元素,这里就是this的调用位置。找到调用位置之后就需要判断this使用了哪种绑定规则,下面就介绍一下this的四种绑定规则。

默认绑定

默认绑定是指在无法应用其他绑定时的默认规则。
首先看以下代码:

1
2
3
4
5
function foo() {
console.log( this.a );
}
var a = 2;
foo(); //2

foo()函数被调用时,this.a被解析成了全局变量a,这时候应用了this的默认绑定,为什么呢?因为foo() 是直接使用不带任何修饰的函数引用进行调用的, 因此只能使用
默认绑定, 无法应用其他规则。
但是如果是在严格模式下:

1
2
3
4
5
6
function foo() {
'use strict';
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined

之所以会有typeError,是因为在严格模式下全局对象将无法使用默认绑定, 因此 this 会绑定到 undefined;
这里有一个微妙但是非常重要的细节, 虽然 this 的绑定规则完全取决于调用位置, 但是只有 foo() 运行在非严格模式下时(在非严格模式下定义), 默认绑定才能绑定到全局对象; 跟foo()的调用位置是不是在严格模式下无关:

1
2
3
4
5
6
7
8
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo(); // 2
})();

注:不要同时使用严格模式或者非严格模式,要统一代码风格

隐式绑定

当函数的调用位置有上下文对象的时候会应用隐式绑定。

1
2
3
4
5
6
7
8
9
function foo(){
console.log(this.a);
}

var obj = {
a: 2,
foo: foo
}
obj.foo();//2

当foo()被调用时,obj成为了foo()的执行上下文对象,于是,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。
注:对象属性引用链中只有最顶层或者说最后一层会影响调用位置。

隐式绑定会有隐式丢失的问题:

1
2
3
4
5
6
7
8
9
10
11
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性

bar(); // "oops, global"

bar是obj.foo的一个引用,但实际指向的是foo函数本身,因此bar没有执行上下文,所以启用了默认绑定。

显式绑定

显示绑定比较简单,就是我们平时使用的call(…)和apply(…),这两个函数可以把对象绑定到执行函数的this上,所以称为显式绑定。

1
2
3
4
5
6
7
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2

显式绑定仍然无法解决我们之前提出的丢失绑定问题。

硬绑定

硬绑定是显式绑定的一个变种,但是可以解决丢失绑定的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的 bar 不可能再修改它的 this
bar.call( window ); // 2

bar的绑定总是在执行的时候绑定到obj上面,这是一种显式的强制绑定。在ES5中的bind()方法就是一种硬绑定。

new绑定

js中的new操作符实际上并不是“构造函数”,而是一种对函数的“构造函数调用”。
使用 new 来调用函数, 或者说发生构造函数调用时, 会自动执行下面的操作。

  1. 创建(或者说构造) 一个全新的对象。
  2. 这个新对象会被执行 [[ 原型 ]] 连接。
  3. 这个新对象会绑定到函数调用的 this。
  4. 如果函数没有返回其他对象, 那么 new 表达式中的函数调用会自动返回这个新对象。
    1
    2
    3
    4
    5
    function foo(a) {
    this.a = a;
    }
    var bar = new foo(2);
    console.log( bar.a ); // 2

使用 new 来调用 foo(..) 时, 我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this上。 new 是最后一种可以影响函数调用时 this 绑定行为的方法, 我们称之为 new 绑定。

优先级

new绑定>硬绑定>显式绑定>隐式绑定>默认绑定。其中默认绑定在严格模式下会绑定到undefined上,否则绑定到全局对象上

你要是觉得好的话,可以点一下哦!