深入理解JavaScript中的this关键字的指向

470 2 年前
this关键字与面向对象程序开发紧密相关,其完全指向由构造器新创建的对象。在ECMAScript规范中也是这样实现的,但正如我们将看到那样,在ECMAScript中,this并不限于只用来指向新创建的对象。

Javascript函数的几种调用方式:

1、普通函数调用

2、作为方法来调用

3、作为构造函数来调用

4、使用apply/call方法来调用

5、Function.prototype.bind方法

6、es6箭头函数

对于每个执行上下文,都有三个重要属性

  • 变量对象(Variable object,VO) 【详情
  • 作用域链(Scope chain) 【详情
  • this

this与上下文中可执行代码的类型有直接关系,this值在进入上下文时确定,并且在上下文运行期间永久不变。

引用类型(Reference type)

ECMAScript 的类型分为语言类型规范类型。

语言类型是开发者直接使用 ECMAScript 可以操作的。其实就是我们常说的Undefined, Null, Boolean, String, Number, 和 Object。

规范类型包括:Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, 和 Environment Record。它们的作用是用来描述语言底层行为逻辑。

跟this相关的规范类型是Reference,也就是 “只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的 js 代码中。

Reference 的构成,由三个组成部分,分别是:

  • base value
  • referenced name
  • strict reference

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

var foo = 1;

// 对应的Reference是:
var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};

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

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

而且规范中还提供了获取 Reference 组成部分的方法,比如 GetBaseIsPropertyReference

1、GetBase:返回 reference 的 base value。

2、IsPropertyReference

3、GetValue从 Reference 类型获取对应值的方法

GetValue 返回对象属性真正的值,但是要注意:

调用 GetValue,返回的将是具体的值,而不再是一个 Reference.

如何确定this的值

引用类型的值只有两种情况:

1、处理一个标示符时
2、处理一个属性访问器时

标识符是变量名,函数名,函数参数名和全局对象中未识别的属性名。

属性访问器它有两种变体:点(.)语法(此时属性名是正确的标示符,且事先知道),或括号语法([])。

在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用括号()的左边是引用类型,this将设为引用类型值的base对象(base value),在其他情况下(与引用类型不同的任何其它属性),这个值为null。不过,实际不存在this的值为null的情况,因为当this的值为null的时候,其值会被隐式转换为全局对象。 注:第5版的ECMAScript中,已经不强迫转换成全局变量了,而是赋值为undefined。

函数调用和非引用类型

当调用括号的左边不是引用类型而是其它类型,这个值自动设置为null,结果为全局对象。

(function () {
  alert(this); // null => global
})();

在这个例子中,我们有一个函数对象但不是引用类型的对象(它不是标示符,也不是属性访问器),相应地,this值最终设为全局对象。

var value = 1;

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

//示例1
console.log(foo.bar());
//示例2
console.log((foo.bar)());
//示例3
console.log((foo.bar = foo.bar)());
//示例4
console.log((false || foo.bar)());
//示例5
console.log((foo.bar, foo.bar)());

1、第一个例子很明显———明显的引用类型,结果是,this为base对象,即foo。

2、在第二个例子中,组运算符并不适用,得到仍是一个引用类型。这就是this值为什么再次设为base对象,即foo。

3、第三个例子中,与组运算符不同,赋值运算符调用了GetValue方法。返回的结果是函数对象(但不是引用类型),这意味着this设为null,结果是global对象。

4、第四个和第五个也是一样——逗号运算符和逻辑运算符(OR)调用了GetValue 方法,相应地,我们失去了引用而得到了函数。并再次设为global。

分类栏目
© 2018邮箱:11407215#qq.comGitHub沪ICP备12039518号-6