分享人:陈树威
目录
1.背景介绍
2.知识剖析
3.常见问题
4.解决方案
5.编码实战
6.扩展思考
7.参考文献
8.更多讨论
当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution
context)。
对于每个执行上下文,都有三个重要属性
这里的 Reference 是一个 Specification Type,也就是 “只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的 js 代码中。
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
};
举个例子
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 其实就是()左边的部分。
2.判断 ref 是不是一个 Reference 类型。 关键就在于看规范是如何处理各种 MemberExpression,返回的结果是不是一个Reference类型。 举最后一个例子:
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)());
foo.bar()在示例 1 中,MemberExpression 计算的结果是 foo.bar,那么 foo.bar 是不是一个 Reference 呢? 查看规范 11.2.1 Property Accessors,这里展示了一个计算的过程,什么都不管了,就看最后一步:
Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
我们得知该表达式返回了一个 Reference 类型!根据之前的内容,我们知道该值为:
var Reference = {
base: foo,
name: 'bar',
strict: false
};
接下来按照 2.1 的判断流程走:该值是 Reference 类型,然后执行IsPropertyReference(ref),如果ref的base value是一个对象则返回true,那么 this 的值为 base value 即这个foo
实例3,4,5中,MemberExpression计算结果分别为(foo.bar=foo.bar),(false||foo.var),(f00,bar,foo,bar)
根据规范,逗号操作符,逻辑或操作符和赋值操作符都会执行:
3.Let rval be GetValue(ref).
因为使用了 GetValue,所以返回的不是 Reference 类型,this 为 undefined
this 为 undefined,非严格模式下,this 的值为 undefined 的时候,其值会被隐式转换为全局对象。
因此这些this都指向了全局对象
var value = 1;
var foo = {
value: 2,
bar: function () {
return this.value;
}
}
//示例1
console.log(foo.bar()); // 2
//示例2
console.log((foo.bar)()); // 2
//示例3
console.log((foo.bar = foo.bar)()); // 1
//示例4
console.log((false || foo.bar)()); // 1
//示例5
console.log((foo.bar, foo.bar)()); // 1
1.如何改变this的指向?
可以使用call或者apply的方法
// 一个对象可以作为call和apply的第一个参数,并且this会被绑定到这个对象。
var obj = {a: 'Custom'};
// 这个属性是在global对象定义的。
var a = 'Global';
function whatsThis(arg) {
console.log(this.a) // this的值取决于函数的调用方式
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'
以下代码的this的指向?
function Foo(){
getName = function(){
console.log(1);
};
return this
}
function getName(){
console.log(5);
}
Foo().getName();
参考一:JavaScript深入之从ECMAScript规范解读this
参考一:this
陈树威