讲讲构造函数内的作用域链

219 阅读4分钟

不了解作用域链可以先康康这两篇文章,冴羽的比较详细。汤姆大叔的比较细致,会更深入一些。

冴羽的《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属性