this绑定规则笔记

in Notes with 0 comment

是的,这也是篇读书笔记,记录《你不知道的JavaScript》上卷的第二部分的this相关内容,有大量增删和个人理解。

this是什么

这里很难说this是什么,只能说是JavaScript的一种机制

this是在运行时就已经绑定了,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式

当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到

换言之,this是在函数被调用时就发生的绑定,它指向什么完全取决于函数在哪里被调用

调用位置

调用位置是函数在代码中被调用的位置,而不是声明的位置。寻找调用位置就是“函数被调用的位置”

分析调用栈就是为了到达当前执行位置所调用的所有函数

绑定规则

函数的执行过程中,调用位置如何决定this的绑定对象

找到调用位置,如何根据四条规则去作判断,这四条规则分别是默认绑定、隐式绑定、显式绑定、new绑定

默认绑定

这条规则可以看作为无法应用其他规则时的默认规则

例如

function foo(){
    console.log(this.a);
}
var a=2;
foo();

在代码中,foo()是直接使用不带任何修饰的函数引用进行调用的,只能使用默认绑定,无法应用其他规则

如果是严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefinded

隐式绑定

这条规则是考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或包含

例如

function foo(){
    console.log(this.a);
}

var obj = {
    a:2,
    foo:foo
}

obj.foo(); //2

当函数被引用用上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
因为调用foo()时 this 被绑定到obj,因此 this.a 和 obj.a是一样的

对象属性引用链中只有最顶层或者说是最后一层灰影响调用位置,如

function foo(){
    console.log(this.a);
}

var obj2 = {
    a:32,
    foo:foo
}

var obj1 = {
    a:2,
    obj2:obj2
}

obj1.obj2.foo(); //32

显式绑定

分析隐式绑定时,我们必须在一个对象内包含一个指向函数的属性,并通过这个属性间接引用这个函数,从而把 this 间接(隐式)绑定到这个对象上。如果我们不想在对象内部包含函数引用,而是想再某个对象上强制调用函数,我们可以使用函数的call()apply()方法

call()apply()的第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this。因为可以直指定this的绑定对象,称为显式绑定

例如

function foo(){
    console.log(this.a);
}
var obj = {
    a:2
};

foo.call(obj); //2

ES5也给我们提供了一种硬绑定的方法Function.prototype.bind,如下

function foo(something){
    console.log(this.a,something);
    return this.a + something;
}
var obj = {
    a:2
}
var bar = foo.bind(obj);

var b = bar(3); //2 3
console.log(b); //5

new绑定

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作

1、创建(构造)一个全新的对象
2、这个新对象会被执行[原型]链接
3、这个新对象会绑定到函数调用的this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

例如

function foo(a){
    this.a = a;
}
var bar = new foo(2);
console.log(bar.a);

优先级

默认绑定的优先级是四条规则中最低的

显式绑定的优先级大于隐式绑定

new绑定的优先级大于隐式绑定

new绑定的优先级大于显式绑定

判断this

如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置,找到后就可以按下面顺序应用四条规则去判断this的绑定对象

1、由 new 调用?绑定到新创建的对象
2、由 call 或者 apply (或者 bind )调用?绑定到指定的对象
3、由上下文对象调用?绑定到那个上下文对象
4、默认,严格模式下绑定到 undefined ,否则绑定到全局对象

this词法

ES6中有一种无法应用上面所述的四条规则的特殊函数类型:箭头函数

箭头函数并不是使用 function 关键字定义的,而是使用胖箭头=>定义的。

箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this

例如

function foo(){
    return(a)=>{
        console.log(this.a);
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call(obj1);
bar.call(obj2); //2

foo()内部创建的箭头函数会捕获调用时foo()的 this。由于foo()的 this 绑定到 obj1,bar 的 this 也会绑定到 obj1,箭头函数的绑定无法被修改(new 也不行)

内容不多,至此~

Responses