你知道的和你可能不知道的this

131 阅读5分钟

前言

小白的第一篇文章,上班大概半年左右,打算重新体系的学一下

为什么要开始写文章?

  1. 我总觉得有些事要勇敢的踏出第一步(比如写点自己的东西)
  2. 身边的大佬都很忙,很难的到他们的指点,我学历也不好,大家又太卷,想巩固知识点的同时借助网络提升自己
  3. 给别人讲的过程是学习的过程也是知识加深的过程

什么是this?

一句话: 函数运行时的环境

如何判断this的指向?

  1. 以函数调用时this指向window
  2. 以某个对象的一个方法(属性)调用,this指向这个对象
  3. 构造函数调用,this指向创建的新对象
  4. 以call形式调用的时候,this指向传入的对象,如果传入为null或者undefined,this指向window
  5. 以表达式的方式调用this指向window

这东西只要是干前端的应该是差不都背的滚瓜烂熟的

附上一些习题基本涵盖了this指向的全部问题

this指向练习题

以es角度解读this

一些前置的知识点

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

而规范类型中有一个 Reference 类型。他是this指向的关键

什么是Reference?

它用来解释诸如 delete、typeof 以及赋值等操作行为的类型。

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

  • base value
  • referenced name
  • strict reference

一个个来解释:

  • base value 就是属性所在的对象或者就是 EnvironmentRecord(环境记录)。
  • referenced name 就是属性的名称。
  • strict reference 就是当前是否为严格模式。

举个例子:

var foo = 1;
//  对应的Reference是:
var fooReference = {
    base: EnvironmentRecord,
    name: 'foo'strcit: false
}
var foo = {
    bar: function () {
        return this; 
    } 
};
foo.bar();
// bar对应的Reference是:
var BarReference = {
    base: foo, // 属性所在的对象是foo
    propertyName: 'bar',
    strict: false
};

接下来说他的一些方法

  • GetBase:reference 的 base value
  • IsPropertyReference:判断 base value 如果是一个对象就放回true
  • GetValue: Reference 类型获取对应值

如何确定this?

  1. 计算 MemberExpression 的结果赋值给 ref
  2. 判断 ref 是不是一个 Reference 类型
  3. 确定this
    • 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
    • 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)
    • 如果 ref 不是 Reference,那么 this 的值为 undefined

我们的问题变成了如何确定 MemberExpression 的结果

确定 MemberExpression 的结果

举个例子

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

foo(); // MemberExpression 是 foo

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

foo()(); // MemberExpression 是 foo()

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

foo.bar(); // MemberExpression 是 foo.bar

简单来说 :*** MemberExpression 的结果就是()左边的内容 ***

好了我们的问题变成了 *** 如何确定 MemberExpression 的结果是否为 Reference 类型 *** 这部分在es地址上有一大堆规范来确定是否为 Reference 类型

在这里附上地址连接方便大家查阅 : es5.github.io/#Type

var value = 1;

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


var foo2 = function(){
    console.log(this)
}

//示例1
console.log(foo.bar());
//示例2
console.log(foo2());

对于foo.bar()

  1. MemberExpression 计算的结果是 foo.bar
  2. 查看规范 11.2.1 Property Accessors,我们得知该表达式返回了一个 Reference 类型!
  3. 我们得出以下判断
var Reference = {
  base: foo,
  name: 'bar',
  strict: false
};
  1. 判断IsPropertyReference方法的返回值,经过前面我们知道 base value 是一个对象,结果返回 true。
  2. this的值就为 GetBase(ref) 经过前面我们知道这个值就为base value,获得 base value 值,这个例子中就是foo,所以 this 的值就是 foo ,示例1的结果就是 2!

对于foo2()

  1. 这个例子中MemberExpression就是foo2,foo2是一个标识符
  2. 我们查看规范11.1.2知道 标识符被解析的时候会进行标识符解析(Identifier Resolution),结果始终是Reference的值,接着看看dentifier Resolution规范 查看规范10.3.1 Identifier Resolution 返回GetIdentifierReference方法的结果,再来看看GetIdentifierReference方法 此处返回了一个 base value 是 envRec 也就是 10.3.1中传入的 execution context’s LexicalEnvironment 根据这个过程,我们确定MemberExpression foo2的计算结果是一个Reference类型
    fooReference = {
        "base": LexicalEnvironment,
        "name": "test",
        "strict": false
    }
  1. 此处我们知道base value 返回了 envRec 那么我们就需要判断 ImplicitThisValue(ref) 是什么
  2. 此时我们再看规范10.2.1.1.6可以看到 base value值为EnvironmentRecord时,ImplicitThisValue的结构是undefined,对应到 JavaScript 代码中的 this为 undefined,非严格模式下,this 的值为 undefined 的时候,其值会被隐式转换为全局对象。

到此为止基本上就把es规范中的this说完了,接下来是我结合内存方面的知识做自己的理解,大家看看就行,错误的地方请指正。


我的思考(可能是错的欢迎指正) 我们在判断Reference的值的时候按照es网站上说的了一系列的规范和方法,繁琐也记不住。 但是这么想是否就简单了不少

举个例子

var value = 1;

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


var foo2 = function(){
    console.log(this)
}

//示例1
console.log(foo.bar());
//示例2
console.log(foo2());

对于示例1来说

  • 当我们确认了MemberExpression 计算的结果是 foo.bar
  • 我们来看foo.bar在内存中的位置
  • 毫无疑问foo在堆内存中,而foo.bar在foo的内部
  • 我们是不是可以粗略的说凡是MemberExpression 计算的结果在堆内存中他就是 Reference 类型!
这里大家的疑问是那示例二为什么this为window?

我的想法是他处在了最外面只能直接的去调用,那么他的this就只能是window,严格模式下this不允许指向window所以为undefined。

参考文献:github.com/mqyqingfeng…