this指向问题

53 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

请结合上一篇文章食用。

先了解一下reference是什么。

由三个部分组成:

  • base value ------属性所在的对象
  • referenced name ------属性的名称
  • strict reference ----是否严格

其中base value 就是属性所在的对象或者就是 EnvironmentRecord,它的值只可能是 undefined, an Object, a Boolean, a String, a Number, or an environment record 其中的一种。

举个例子:

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

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

其中,规范中提供了两个获取reference组成部分的方法,获取base.value的方法Getbase() ,和判断base的值是否为对象返回布尔值的方法IsPropertyReference(),前两个方法用于判断this指向。还有一个直接获取变量值的方法GetValue。

先说第三个小方法,比较简单:

var foo = 1;
var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};
GetValue(fooReference) // 1;

如何确定this的值? (用到了前两个方法)

1.计算MemberExpression的结果值给ref

但什么是MemberExpression?

举个例子:

 function foo(){
 console.log(this)
 }
  foo() //MemberExpression是foo,也就是ref为foo
  
 function foo(){
  return function(){
   console.log(this)
  }
 }
 foo()()//MemberExpression是foo()
 
 var foo = {
     bar :function(){
         return this;
         }
     }
 foo.bar()//MemberExpression是foo.bar

简单理解MemberExpression就是()左边的部分。

2.判断ref是不是一个Reference类型(也判断base value是否为对象)

var value = 1;
        var foo = {
            value: 2,
            bar: function () {
                return this.value;
            }
        }
        console.log( foo.bar());//2
        console.log((foo.bar)());//2
        console.log((foo.bar = foo.bar)());//严格模式还是非严格模式
        console.log((false || foo.bar)());//严格模式还是非严格模式
        console.log((foo.bar, foo.bar)());//严格模式还是非严格模式

示例一 console.log( foo.bar())首先写出它的Referrence:

var Reference = {
    base:foo,
    name:'bar,
    strict:'false
}

(判断是否为ref类型用到IsPropertyReference(ref); 如果返回值base value是一个对象那么就是true。然后this的值为GetBase(ref)。ok讲解完,继续。)

回归正题,写完reference后判断base.value的值,是foo,是一个对象,返回true。所以this的值为base的值foo。

所以,证明得出,this指向foo。所以return 出的value值为2。

示例二:console.log((foo.bar)());

MemberExpression为foo.bar,结果与分析示例一相同。

示例三console.log((foo.bar = foo.bar)());

示例四console.log((false || foo.bar)());

示例五console.log((foo.bar, foo.bar)());

由于非reference类型,所以严格模式this为undefined,非严格模式下指向window会输出1

再来例子:

function foo() {
    console.log(this)
}

foo(); 

判断MemberExpression(ref)为foo,然后写出Reference:

 var Reference ={
 
 base: EnvironmentRecord,
 name:"foo",
 strict:false
 
 }

base.value 非对象,返回false。用第三条判断,所以ImplicitThisValue(ref)的this指向为undefined(严格模式下)

** 我的测试环境全部非严格模式哦。**

image.png

综上所述,判断this指向整体步骤为,

1 .如果 ref 是 Reference,并且 判断base.value是否为对象,即判断IsPropertyReference(ref) 是否为 true, 若是,那么 this 的值为 GetBase(ref)----- --(对应示例1,2)

2 .如果 ref 是 Reference,并且 base value 值是 Environment Record,非对象,即IsPropertyReference(ref) 是否为 false, 那么this的值为 ImplicitThisValue(ref)==undefined(严格模式下)--------(对应最后一个例子。)

3 .如果 ref 不是 Reference(例如false || foo.bar),那么 this 的值为 undefined(严格模式)/window(非严格模式) -------- (对应示例3,4,5)

以上是this指向的原理,平常去判断this指向时只需记住:

  • 函数作为对象的属性调用 => 谁调用就是谁
  • 直接调用函数 => 全局是谁就是谁
  • call、apply、bind的函数调用场景 => 绑定谁就是谁
  • 函数的构造调用 => 新创建的对象

如果有不足,希望大家指正,接受批评。

今天学到了好多。