前言
小白的第一篇文章,上班大概半年左右,打算重新体系的学一下
为什么要开始写文章?
- 我总觉得有些事要勇敢的踏出第一步(比如写点自己的东西)
- 身边的大佬都很忙,很难的到他们的指点,我学历也不好,大家又太卷,想巩固知识点的同时借助网络提升自己
- 给别人讲的过程是学习的过程也是知识加深的过程
什么是this?
一句话: 函数运行时的环境
如何判断this的指向?
- 以函数调用时this指向window
- 以某个对象的一个方法(属性)调用,this指向这个对象
- 构造函数调用,this指向创建的新对象
- 以call形式调用的时候,this指向传入的对象,如果传入为null或者undefined,this指向window
- 以表达式的方式调用this指向window
这东西只要是干前端的应该是差不都背的滚瓜烂熟的
附上一些习题基本涵盖了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?
- 计算 MemberExpression 的结果赋值给 ref
- 判断 ref 是不是一个 Reference 类型
- 确定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()
- MemberExpression 计算的结果是 foo.bar
- 查看规范 11.2.1 Property Accessors,我们得知该表达式返回了一个 Reference 类型!
- 我们得出以下判断
var Reference = {
base: foo,
name: 'bar',
strict: false
};
- 判断IsPropertyReference方法的返回值,经过前面我们知道 base value 是一个对象,结果返回 true。
- this的值就为 GetBase(ref) 经过前面我们知道这个值就为base value,获得 base value 值,这个例子中就是foo,所以 this 的值就是 foo ,示例1的结果就是 2!
对于foo2()
- 这个例子中MemberExpression就是foo2,foo2是一个标识符
- 我们查看规范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
}
- 此处我们知道base value 返回了 envRec 那么我们就需要判断 ImplicitThisValue(ref) 是什么
- 此时我们再看规范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。