首先说下this的定义,this是函数作用域中的一个关键字,在ECMAScript规范中实在这么解释this的:this 关键字执行为当前执行环境的 ThisBinding。换句话说就是它用来指向调用此函数的对象。
    ECMAScript分为语言类型和规范类型
    ECMAScript语言类型是开发者直接使用ECMAScript可以操作的,也就是七种基本类型:String、Number、Null、Undefined、Boolean、Object、Symbol(es6新增基本类型)
    ECMAScript规范类型相当于meta-value,是用算法来描述ECMAScript语言类型的。规范类型包括:Reference、List、Completion、Property Descriptor、Property Identifier,Lexical Environment、Environment Record
    这其中Reference跟this的指向有密切的联系
    Reference:在ECMAScript标准中被描述为a resolve name binding (已完成的命名绑定)
    那javascript解析器是如何去解析this的呢,ECMAScript的官方描述如下:

    1. 1. Let ref be the result of evaluating MemberExpression.
    2. 2. Let func be GetValue(ref).
    3. 3. Let argList be the result of evaluating Arguments, producing an internal list of argument values (see 11.2.4).
    4. 4. If Type(func) is not Object, throw a TypeError exception.
    5. 5. If IsCallable(func) is false, throw a TypeError exception.
    6. 6. If Type(ref) is Reference, then
    7. a.If IsPropertyReference(ref) is true, then
    8. i.Let thisValue be GetBase(ref).
    9. b. Else, the base of ref is an Environment Record
    10. i.Let thisValue be the result of calling the Implicit This Value concrete method of GetBase(ref).
    11. 7. Else, Type(ref) is not Reference.
    12. a.Let thisValue be undefined.
    13. 8. Return the result of calling the [[Call]] internal method on func, providing thisValue as the this value and providing the list argList as the argument values.

    这八句话啥意思呢,其实我也想知道😂、不过我找到一张图来解释这八句话,图示:

    167cec74a1455c7b.jpg
    这张图将解析的每一个步骤进行了分析。红色指代关键的步骤,如果第三步getValue(ref)得出的结果无法通过第五步的判断的话,也就是getValue(ref)不是一个对象,那this指向毫无意义,此时编译器会抛出类型异常
    第二步:计算MemberExpression的值并且赋给Ref:也就是计算()左边内容的结果,并赋值给Ref
    第六步:判断Ref是否为Reference类型
    第七步:判断ref是否是属于引用类型:官方解释:通过执行IsPropertyReference(V)来判断,如果base value是个对象或HasPrimitiveBase(V)是true,那么返回true,否则返回false。HasPrimitiveBase(V):如果base value是Boolean、Number、String,那么返回true。也就是ref这个引用是基于哪个对象。如果是基于一个对象或者Boolean、String、Number那就返回true,否则返回false。

    由于base value和IsPropertyReference都是和Reference有关系的,所以来详细解释一下Reference的构成:
    官方的解释比较复杂,简化来说就是Reference有三个组成部分,分别是:
    1、base value
    2、reference name
    3、strict reference

    其中base value就是属性所在的对象或者就是EnvironmentRecord,它的值只可能是undefined、Object、Boolean、String、Number、environment record其中的一种。而且规范中还提供了获取Reference组成部分的方法,比如GetBaseIsPropertyReference
    1、GetBase:返回Reference的base value
    2、IsPropertyReference:如果base value是一个对象,就返回true

    1. var foo = {
    2. bar: function() {
    3. return this
    4. }
    5. }
    6. foo.bar() // foo
    7. // bar对应的Reference是:
    8. var barReference = {
    9. base: foo, // base value
    10. propertyName: 'bar',
    11. strict: false
    12. }

    GetValue:返回对象真正的值,而不是获取Reference。上面例子中getValue(barReference) = function() { return this }
    针对具体使用场景进行分析:
    1、直接调用

    1. let a = 'm'
    2. function test() {
    3. console.info(this.a)
    4. }

    根据图中步骤进行分析:
    一、test()的Ref就是test的引用
    二、判断test是否为引用类型,返回true
    三、判断Ref是否是属性引用类型,返回false,因为它并没有定义在某个引用类型内部,也就是不是对象的属性
    四、进入到第九步:this => ImpliciThisValue(Ref),在Environment Record都返回undefined,而非严格模式会将undefined指向window对象

    2、在对象内部调

    1. function test() {
    2. console.info(this.a)
    3. }
    4. let parent = {
    5. a: 's',
    6. test: test
    7. }
    8. parent.test() // s

    根据图中步骤进行分析:
    一、test()的Ref是parent.test的引用
    二、判断parent.test是否为引用类型,返回true
    三、判断Ref是否为属性引用类型,返回true
    四、进入到第八步GetBase(Ref)是parent,所以this指向parent

    3、构造函数new一个新对象

    1. let a = 'm'
    2. function Foo() {
    3. console.info(this)
    4. }
    5. let c = new Foo()
    6. c.a = 's'

    new 关键字的调用区别于一般的函数调用
    一、一个继承自Foo.prototype的新对象被创建
    二、使用指定的参数调用构造函数Foo,并将this绑定到新创建的对象

    4、箭头函数的this指向(待探索)

    :::info 本文档内容引用自:dendoink的掘金文冴羽的博客 :::