Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从 ECMAScript 规范解读 this #22

Open
chiyan-lin opened this issue Oct 9, 2021 · 4 comments
Open

从 ECMAScript 规范解读 this #22

chiyan-lin opened this issue Oct 9, 2021 · 4 comments

Comments

@chiyan-lin
Copy link
Owner

chiyan-lin commented Oct 9, 2021

var length = 10;
function fn() {
  console.log(this.length);
}

var obj = {
  length: 5,
  method: function (fn) {
    fn();
    arguments[0]();
    fn.call(obj, 12);
  },
};
obj.method(fn);

先来看一个函数执行,第一眼看到 fn() 的时候,直觉告诉我他是 10 ,但是冷静地瞎分析(在method函数里面执行函数,那么this应该就是执行这个函数的this)还是给出了 5 。

所以,我真的懂 this 吗。

什么是 this

this 是 JavaScript 的关键字之一,作为函数的特殊变量,指代函数当前的运行环境。

js 执行底层是怎么确定这个 this 的指向的

Reference

ECMAScript 规范中还有一种只存在于规范中的类型,它们的作用是用来描述语言底层行为逻辑。这些规范是在语言底层内部使用,作为开发我们接触不到这些类型,Reference 就其中一个类型。这里可以理解为一个interface。

我们在定义变量或者函数的时候,对应声明的变量和函数会生成对应的 Reference。Reference包括 base,propertyName,strict 三个属性,GetBase,IsPropertyReference 这两重要方法。 比如

var foo = {
    bar: function () {
        return this;
    }
};
 
foo.bar(); // foo

// bar对应的Reference是:
BarReference = {
    base: foo,
    propertyName: 'bar',
    strict: false
};

var foo = function () {
    return this;
};

foo();

// bar对应的Reference是:
fooReference = {
    base: Function Environment Record,
    propertyName: 'bar',
    strict: false
};

从规范还有例子来看,一个函数的执行,其对应的 Reference 的base就是函数 Reference 的 propertyName 左边的函数执行环境,如果在执行的时候左边的变量没有定义,那么 base 就会取 Function Environment Record 这个特殊的对象。

确定this的值

根据规范中的描述

  1. 计算 MemberExpression「简单理解 MemberExpression 其实就是()左边的部分」 的结果赋值给 ref
  2. 判断 ref 是不是一个 Reference 类型
    • 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
    • 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref),而ImplicitThisValue始终返回 undefined
    • 如果 ref 不是 Reference,那么 this 的值为 undefined

规范就是这么设置 this 的,ok 有了规范,那我们就可以来看下一开始那个问题了。

在执行 fn() 的时候,MemberExpression 是 foo 函数,fn Reference 的 base 是 Function Environment Record,命中第二点,返回的 this 就是 undefined,在非严格模式下,console.log(this.length); 中的 this 就指向 window 。

var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}

//示例1: MemberExpression 是 foo.bar ,Reference 的 base 是 foo ,执行 getBase this 就是 foo
console.log(foo.bar()); // 2
//示例2:同 1
console.log((foo.bar)()); // 2
//示例3: 执行了 = 运算符,表达式返回 GetValue 所以 MemberExpression 是 GetValue 的值,不是 Reference,所以 this 是 undefined
console.log((foo.bar = foo.bar)()); // 1
//示例4:同 3, || 运算符也是返回的 GetValue
console.log((false || foo.bar)()); // 1
//示例5:同 3, ,运算符也是返回的 GetValue
console.log((foo.bar, foo.bar)()); // 1
//示例6
var _bar = foo.bar
console.log(_bar()); // 1

总结

以前总是理解 this 为调用函数的对象,虽然在很多场景下这个理解没有问题,但是看到上面的 (false || foo.bar)() 是不是就没办法解释了,所以关于 this 的具体指向,还是要从规范下手,才能最正确地解释他的指向。

@chiyan-lin
Copy link
Owner Author

mqyqingfeng/Blog#7

@chiyan-lin
Copy link
Owner Author

@chiyan-lin
Copy link
Owner Author

@chiyan-lin
Copy link
Owner Author

es5规范:http://yanhaijing.com/es5/#book

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant