解析this的四种绑定规则

导语:

本文主要归纳总结自《你不知道的JavaScript上卷》中的关于this的解析。

  • this既不指向函数自身也不止像函数的词法作用域;
  • this是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
  • this的绑定和函数声明的位置没有任何关系,只取决于函数的调用。

this的四种绑定规则

1.默认绑定

function foo(){
  console.log(this.a);
}
var a=2;
foo();  // 输出2,原因:foo在全局作用域里被调用,所以this绑定的是全局作用域。

function foo(){
  "use strict"
   console.log(this.a);
}
var a=2;
foo();  // TypeError:this is undefined.

若使用严格模式,则不能将全局对象用于默认绑定,因此this会绑定到undefined。

function foo(){
  console.log(this.a);
}
var a=2;
(function(){
   "use strict"
   foo();  //输出2。
})();

在严格模式下调用foo()则不影响默认绑定。

function foo(){
    console.log(this.a);
}
var a=2;
(function(){
    "use strict";
    foo();  //输出2。
})();

2.隐式绑定

考虑:调用位置是否有上下文对象,或者是否被某个对象拥有或包含。

1–>隐式绑定

function foo(){
    console.log(this.a);
}
var obj={
    a:2,
    foo:foo
}
obj.foo();  // 输出2 原因:foo()再被调用的时候,前面加上了对obj的引用,当函数引用有上下文对象时,函数中的this会绑定上去。
var obj1={
    a:42;
    obj:obj
}
obj1.obj.foo();  //输出2 原因:对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。

2–>隐式丢失

function foo(){
    console.log(this.a);
}
var obj={
    a:2,
    foo:foo
}
var bar=obj.foo;
var a="oops,global";
bar(); //输出oops,global
原因:虽然bar是obj.foo的一个引用,但实际它引用的是foo函数本身,因此bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

3–>回调函数丢失this绑定

function foo(){
    console.log(this.a);
}
function doFoo(fn){
    //fn其实引用的是foo
    fn();
}
var obj={
    a:2,
    foo:foo
};
var a="oops,global";
doFoo(obj.foo);  //输出"oops,global" 相当于fn=obj.foo;fn();
参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。

作为参数传入内置函数,也会发生隐式丢失
function foo(){
    console.log(this.a);
}
var obj={
    a:2,
    foo:foo
};
var a="oops,global";
setTimeout(obj.foo,100);////输出"oops,global"

3.显示绑定

使用call(..)和apply(..),他们的第一个参数是一个对象,在调用函数时将其绑定到this。

1–>显示绑定

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

如果你传入一个原始值(字符串类型、布尔类型)来当做this的绑定对象,这个原始值会被转换成他的对象形式。称为“装箱”。

问题:显示绑定无法解决我们之前的丢失绑定问题?但显示绑定的变种——硬绑定可以解决。

2–>硬绑定

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

硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值;

3–>bind

由于硬绑定是一种非常常用的模式,所以ES5提供了内置的方法:

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

4.new绑定

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

使用new来调用foo(…)时,我们会构造一个新对象并把它绑定到foo(…)调用中的this上。

坚持原创技术分享,您的支持将鼓励我继续创作!