不了解作用域链可以先康康这两篇文章,冴羽的比较详细。汤姆大叔的比较细致,会更深入一些。
冴羽的《JavaScript深入之作用域链》 juejin.cn/post/684490… github.com/mqyqingfeng…
汤姆大叔的 www.cnblogs.com/TomXu/archi…
这里将一下具名函数表达式的作用域链问题
问题1,调用具名函数表达式
console.log(text);
(function text() {
...
})()
//text is not defined
//在这里函数表达式外部访问不到text的函数表达式
//是因为函数表达式不会保存进全局变量对象中,并且函数表达式调用时才会创建
问题2,具名函数表达式内部调用
text=1;
(function text() {
text=2
var text1=3
console.log(text) //输出text function() ...
console.log(text1)//输出3
})();
console.log(text) //输出1
这里就有两个问题了
- 为什么在text表达式内改变了text,但是全局输出的时候还是1?
- text表达式的text保存在哪里,是保存在text表达式的活动对象中么?
为什么在text表达式内改变了text,但是全局输出的时候还是1
这里先简单解释一下。因为在text表达式内text是表达式的名称,且不会被重新更改。
console.log(text) //输出text function() ...
function text() {
console.log(text)//输出text function() ...
text=2
}
text()
console.log(text) //2
为什么函数就能被更改呢
globalContext.VO={
text:text function() ...
}
应为函数在执行上下文时就被保存在全局变量中
在执行函数后
globalContext.VO={
text:2
}
全局变量被修改
这里就引出第二个问题
text表达式的text保存在哪里,是保存在text表达式的活动对象中么,为什么不能被修改
这里可以先看一下汤姆大叔的文章,函数表达式在定义的时候会创建一个特殊对象用来保存唯一值——函数表达式的名称
这个辅助对象在作用域链上的优先级在函数表达式的AO后面
先看一下函数的作用域链
[text.AO,globalContext.VO]
再看一下函数表达式的作用域链
[text.AO,{特殊对象},globalContext.VO]
现在再看:
var text=1;
(function text() {
text=2
console.log(text) //输出text function() ...
})();
console.log(text) //输出1
在text内text没有var,因此没有AO没有生成变量。
所以指向的是text表达式,且这个特殊对象ReadOnly只读。
所以text=2无效。
var text=1;
(function text() {
var text=2
console.log(text) //输出2
})();
在text=2前加上var,那么会再创建text表达式时创建text变量。
因为作用域链的优先级,所以会先访问AO上的text。
text.AO={
arguments:{
length:0
},
text:2
}
特殊对象={
text:text function() ...
}
SpiderMonkey
"特殊"对象的创建方式。标准说"像调用new Object()"创建这个对象。
这个实现就是SpiderMonkey。因此,在SpiderMonkey中,扩展Object.prototype的属性会干扰函数的局部作用域:
1,函数foo的作用域链中有一个特殊对象,用于保存函数的标识符。
这个特殊对象就是{ foo: <function object> }。
2,当通过作用域链解析x时,首先解析的是foo的活动变量。(foo.AO)
如果没有找到x,则继续搜索作用域链中的下一个作用域。
(对于函数来说下一个作用域是父级的作用域,直到全局变量)
3,而具名表达式,下一个是保存函数标识符的那个特殊对象{ foo: <function object> }
由于该对象继承自Object.prototype,所以可以通过原型链找a。
而这个a的值也就是Object.prototype.a的值('WSQ')。
因为已经找到了,所以就直接返回。外部的a = '2333'就不会解析
Object.prototype.a = 'WSQ';
(function(){
(function foo(){
console.log(foo); // foo的函数表达式
console.log(a); // 显示:WSQ
})();
})();
现在版本阅览器SpiderMonkey改变了上述行为,原因可能是认为那是一个安全漏洞。
特殊对象不再继承Object.prototype了。
如果你使用Firefox 3或者更低版本,还可以体验这种行为。
这里遇到个问题
这里先输出了一下text,证明全局变量中没有text属性
在Object.prototype上定义了一个text属性
在查找text时最终查找的了Object.prototype的原型链上
window.__proto__.__proto__.__proto__.__proto__===Object.prototype,
window的四层__proto__指向Object.prototype,最终通过原型链查找到text属性