资讯专栏INFORMATION COLUMN

FE.ES-理解ECMA Javascript的this

Meils / 2495人阅读

摘要:捕获所有参数绑定当一个函数用作构造函数时使用关键字,它的被绑定到正在构造的新对象。使用来调用函数,或者说发生构造函数调用时,会自动执行下面的操作你不知道的创建或者说构造一个全新的对象。在箭头函数中,与封闭词法上下文的保持一致。

this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数的调用位置(也就是函数的调用方法)。

四条规则:(你不知道的JS) 1. 默认绑定

</>复制代码

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

无论是否在严格模式下,在全局执行上下文中(在任何函数体外部)this 都指代全局对象。(MDN)
在严格模式下,this将保持他进入执行上下文时的值,如果 this 没有被执行上下文(execution context)定义,那它将保持为 undefined。(MDN)

</>复制代码

  1. function foo() {
  2. "use strict";
  3. console.log( this.a );
  4. }
  5. var a = 2;
  6. foo(); // TypeError: this is undefined
2. 隐式绑定/丢失

当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象,且绑定只受最靠近的成员引用的影响。(MDN)

</>复制代码

  1. //隐式绑定
  2. function foo() {
  3. console.log( this.a );
  4. }
  5. var obj2 = {
  6. a: 42,
  7. foo: foo
  8. };
  9. var obj1 = {
  10. a: 2,
  11. obj2: obj2
  12. };
  13. obj1.obj2.foo(); // 42

</>复制代码

  1. //隐式丢失
  2. function foo() {
  3. console.log( this.a );
  4. }
  5. function doFoo(fn) {
  6. // fn 其实引用的是 foo
  7. fn(); // <-- 调用位置!
  8. }
  9. var obj = {
  10. a: 2,
  11. foo: foo
  12. };
  13. var a = "oops, global"; // a 是全局对象的属性
  14. doFoo( obj.foo ); // "oops, global"
3. 显示绑定

如果要想把 this 的值从一个上下文传到另一个,就要用 call 或者apply 方法。(MDN)
调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

</>复制代码

  1. var obj = {
  2. count: 0,
  3. cool: function coolFn() {
  4. if (this.count < 1) {
  5. setTimeout( function timer(){
  6. this.count++; // this 是安全的
  7. // 因为 bind(..)
  8. console.log( "more awesome" );
  9. }.bind( this ), 100 ); // look, bind()!
  10. }
  11. }
  12. };
  13. obj.cool(); // 更酷了。
硬绑定

创建一个包裹函数,传入所有的参数并返回接收到的所有值。
硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改 this 。

</>复制代码

  1. // 简单的辅助绑定函数
  2. function bind(fn, obj) {
  3. return function() {
  4. return fn.apply( obj, arguments );
  5. };
  6. }
软绑定

给默认绑定指定一个全局对象和 undefined 以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力。

</>复制代码

  1. Function.prototype.softBind = function(obj) {
  2. var fn = this;
  3. var curried = [].slice.call( arguments, 1 );// 捕获所有 curried 参数
  4. var bound = function() {
  5. return fn.apply(
  6. (!this || this === (window || global))?obj : this
  7. curried.concat.apply( curried, arguments )
  8. );
  9. };
  10. bound.prototype = Object.create( fn.prototype );
  11. return bound;
  12. };
4. new 绑定

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。(MDN)
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作(你不知道的JS)

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

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

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

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

</>复制代码

  1. function foo(a) {
  2. this.a = a;
  3. }
  4. var bar = new foo(2);
  5. console.log( bar.a ); // 2
四条规则优先级

new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定

函数是否在 new 中调用( new 绑定)?如果是的话 this 绑定的是新创建的对象。

</>复制代码

  1. var bar = new foo()

函数是否通过 call 、 apply (显式绑定)或者硬绑定调用?如果是的话, this 绑定的是指定的对象。
另外:如果绑定 null 或者 undefined ,实际应用的是默认绑定规则。

</>复制代码

  1. var bar = foo.call(obj2)

函数是否在某个上下文对象中调用(隐式绑定)?如果是的话, this 绑定的是那个上下文对象。

</>复制代码

  1. var bar = obj1.foo()

如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined ,否则绑定到全局对象。

</>复制代码

  1. var bar = foo()

其中:间接引用函数会应用默认绑定规则

</>复制代码

  1. function foo() {
  2. console.log( this.a );
  3. }
  4. var a = 2;
  5. var o = { a: 3, foo: foo };
  6. var p = { a: 4 };
  7. o.foo(); // 3
  8. (p.foo = o.foo)(); // 2

例外 1. 箭头函数

箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this 。
在箭头函数中,this与封闭词法上下文的this保持一致。(MDN)
箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和self = this 机制一样。
箭头函数的绑定无法被修改。

2. nodejs

</>复制代码

  1. setTimeout(function() {
  2. console.log(this)
  3. //浏览器中:window
  4. //nodejs中:Timeout实例
  5. }, 0)
其他解释

https://www.zhihu.com/questio...
func(p1, p2) 等价于
func.call(undefined, p1, p2)

obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)

如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

例子

</>复制代码

  1. var number = 50;
  2. var obj = {
  3. number: 60,
  4. getNum: function () {
  5. var number = 70;
  6. return this.number;
  7. }
  8. };
  9. alert(obj.getNum());
  10. alert(obj.getNum.call());
  11. alert(obj.getNum.call({number:20}));

参考资料:
深入理解JavaScript系列(13):This? Yes,this!
MDN-this
《你不知道的JS上卷》阅读小记之setTimeout的this指向问题Script
JavaScript 的 this 原理-阮一峰
如何理解 JavaScript 中的 this 关键字?-方应杭-知乎
JS this 相关问题

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99348.html

相关文章

  • FE.ES-理解ECMA Javascriptthis

    摘要:捕获所有参数绑定当一个函数用作构造函数时使用关键字,它的被绑定到正在构造的新对象。使用来调用函数,或者说发生构造函数调用时,会自动执行下面的操作你不知道的创建或者说构造一个全新的对象。在箭头函数中,与封闭词法上下文的保持一致。 this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数的调用位置(也就是函数的调用方法)。 四条规则:(你不知道的JS) 1. 默认绑定 func...

    elliott_hu 评论0 收藏0
  • FE.ES-理解ECMA Javascript原型

    摘要:但是,它是用构造器调用来这样做的。本质是因为让函数调用变成了构造器调用假设有一个需要继承的一个类型添加一个属性如何检查类继承自省错误方法只能查询的祖先。 名词解析 字面量对象字面量 var a = {};数组字面量 var arr = [1,2,3];正则表达式字面量 var reg = /[a-z]/g; 属性描述符 configurable:fasle是一个单向操作,同时阻止使...

    ssshooter 评论0 收藏0
  • FE.ES-理解ECMA Javascript原型

    摘要:但是,它是用构造器调用来这样做的。本质是因为让函数调用变成了构造器调用假设有一个需要继承的一个类型添加一个属性如何检查类继承自省错误方法只能查询的祖先。 名词解析 字面量对象字面量 var a = {};数组字面量 var arr = [1,2,3];正则表达式字面量 var reg = /[a-z]/g; 属性描述符 configurable:fasle是一个单向操作,同时阻止使...

    Charles 评论0 收藏0
  • FE.ES-理解ECMA Javascript原型

    摘要:但是,它是用构造器调用来这样做的。本质是因为让函数调用变成了构造器调用假设有一个需要继承的一个类型添加一个属性如何检查类继承自省错误方法只能查询的祖先。 名词解析 字面量对象字面量 var a = {};数组字面量 var arr = [1,2,3];正则表达式字面量 var reg = /[a-z]/g; 属性描述符 configurable:fasle是一个单向操作,同时阻止使...

    刘东 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<