继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

JS 中你不知道的 this

BIG阳
关注TA
已关注
手记 474
粉丝 73
获赞 458
this 误区

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

调用位置

在理解 this 的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的位置)。

function baz() {     // 当前调用栈是:baz
     // 因此,当前调用位置是全局作用域
     console.log( "baz" );
     bar(); // <-- bar 的调用位置 }function bar() {    // 当前调用栈是 baz -> bar
    // 因此,当前调用位置在 baz 中
     console.log( "bar" );
     foo(); // <-- foo 的调用位置 }function foo() {    // 当前调用栈是 baz -> bar -> foo 
    // 因此,当前调用位置在 bar 中
     console.log( "foo" );
}
baz(); // <-- baz 的调用位置
四种绑定规则
  1. 默认绑定

独立函数内调用。可以把这条规则看作是无法应用其他规则时的默认规则。

非严格模式

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

声明在全局作用域中的变量就是全局对象的一个同名属性。调用 foo() 时,this.a 被解析成了全局变量 a。

严格模式

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

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

总结:this 的默认绑定规则在非严格模式下绑定到全局对象,严格模式下绑定到 undefined。

  1. 隐式绑定

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

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

foo 函数被当作引用属性添加到 obj 中,因此你可以说函数被调用时 obj 对象拥有或者包含它。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。

对象属性引用链中只有最顶层或者说最后一层会影响调用位置。

function foo() { 
    console.log( this.a );
}var obj2 = { 
    a: 666,    foo: foo 
};var obj1 = { 
    a: 999,    obj2: obj2 
};
obj1.obj2.foo(); // 666

隐式丢失情况

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() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

function foo() { 
    console.log( this.a );
}function doFoo(fn) {    // fn 其实引用的是 foo 
    fn(); // <-- 调用位置!}var obj = { 
    a: 2,    foo: foo 
};var a = "oops, global"; // a 是全局对象的属性 doFoo( obj.foo ); // "oops, global"

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值。

  1. 显式绑定

JavaScript 提供的绝大多数函数以及你自己创建的所有函数都可以使用 call(..) ,apply(..) 和 bind(..) 方法,用来直接指定 this 的绑定对象。

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

通过 foo.call(..),我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。

  1. new绑定

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

  • 创建(或者说构造)一个全新的对象。

  • 这个新对象会被执行[[原型]]连接。

  • 这个新对象会绑定到函数调用的this。

  • 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

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

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

优先级
function foo() { 
    console.log( this.a );
}var obj1 = { 
    a: 2,    foo: foo 
};var obj2 = { 
    a: 3,    foo: foo 
};
obj1.foo(); // 2obj2.foo(); // 3obj1.foo.call( obj2 ); // 3obj2.foo.call( obj1 ); // 2

显式绑定优先级高于隐式绑定。

function foo(something) { 
    this.a = something;
}var obj1 = { 
    foo: foo
};var obj2 = {};
     
obj1.foo( 2 );console.log( obj1.a ); // 2
     obj1.foo.call( obj2, 3 );console.log( obj2.a ); // 3var bar = new obj1.foo( 4 ); 
console.log( obj1.a ); // 2console.log( bar.a ); // 4

new 绑定比隐式绑定优先级高。

由于 new 和 call/apply 无法一起使用,因此我们不用关心他们优先级关系。

best practice

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

  • 由 new 调用则绑定到新创建的对象。

  • 由 cal l或者 apply 或者 bind 调用则绑定到指定的对象。

  • 由上下文对象调用(隐式绑定)则绑定到那个上下文对象。

  • 默认绑定:在严格模式下绑定到 undefined,否则绑定到全局对象。

优先级关系:
显示绑定 > 隐式绑定 > 默认绑定
new绑定 > 隐式绑定 > 默认绑定

箭头函数

ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。

function foo() {    // 返回一个箭头函数 
    return (a) => {        //this 继承自 foo()
        console.log( this.a ); 
    };
}var obj1 = { 
    a:2};var obj2 = { 
    a:3};var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3 !

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

全局上下文

在全局执行上下文中(在任何函数之外),this 指的是全局对象,无论它是否处于严格模式。

console.log(this === window); // truea = 37;console.log(window.a); // 37this.b = "MDN";console.log(window.b)  // "MDN"console.log(b)         // "MDN"
参考链接

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

你不知道的JavaScript(上卷)



作者:Inlight先森
链接:https://www.jianshu.com/p/4d6ec506e4d8


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP