关于JavaScript中访问不带有this修饰的变量的搜索顺序的理解

这几天因为对于JavaScript中的作用域链和原型链有点混淆,当访问一个不带有this修饰的变量时,我想知道它的搜索顺序,因为作用域链的链结点也是一个变量对象,那么当在这个变量对象中查找变量时会不会沿着它的原型链查找呢?这样就有两种可能:

  1. 先查找作用域链前端的变量对象,然后再查找它的原型,然后再查找作用域链中下一个变量对象,然后再查找它的原型;

  2. 一直查找作用域链中的变量对象,直到window对象,再查找它的原型。

然而在使用with语句做实验时,发现了下面的现象,因此本篇文章是我对下面的事实作出的猜测和理解,望指正!

考察下列代码:

Object.prototype.s=10;
(function(){
    var s=25;
    var obj={};
    obj.__proto__={};
    obj.__proto__.__proto__={s:15};
    with(obj){
      console.log(s);//输出15
    }
}());

上述代码中,在Object.prototype中定义了一个属性s=10,在匿名函数中定义一个变量s=25,其中又有一个对象obj,在obj的二级原型链中定义一个属性s=15,然后,使用with将这个对象obj挂在作用域链顶端,输出s,但是它输出了15.
对此我做出的猜测是:在搜查变量中实际上是一个二维的过程而不是一维的,它构造出的二维链是这样子的:
关于JavaScript中访问不带有this修饰的变量的搜索顺序的理解_第1张图片

所以上述过程中,先从obj(即作用域链顶端的变量对象开始搜查),因为obj本身没有s,则沿着obj的原型链搜查,在二级原型链中找到了s=15,从而停止搜查,返回s=15的值,故而输出15.
关于JavaScript中访问不带有this修饰的变量的搜索顺序的理解_第2张图片

按照这个思路,发现所有对象的原型链都最终指向Obje.prototype,所以为了验证这个猜想,做下述实验,即删除s=15这个语句

Object.prototype.s=10;        //保留Object.prototype中的s
(function(){
    var s=25;
    var obj={};
    obj.__proto__={};
    obj.__proto__.__proto__={};//删除了s:15
    with(obj){
      console.log(s);//输出10
    }
}());

上述代码输出了10,这就是说,沿着第一个作用域链结点的原型链找,最终在Objec.prototype中找到了s,从而停止查找,那么函数中s=25没有遍历到
关于JavaScript中访问不带有this修饰的变量的搜索顺序的理解_第3张图片
下面做个实验,证明确实是二维查找。
使用嵌套的with语句,在作用域链顶端挂两个对象,其中第二个对象是一个函数对象,而s正是在Function.prototype中,如果上面的猜想可行,那么应该能正确输出s,而事实上确实如此

Function.prototype.s=5;//给Funct.prototype添加s
var obj={};
var func=function(){};
(function(){
    var s=25;        //s=25  
              with(func){    //嵌套
                  with(obj){
                      console.log(s);    //输出5
                  }
                  
              }
}());

上述输出的正是5,其过程如下所示:
关于JavaScript中访问不带有this修饰的变量的搜索顺序的理解_第4张图片


最后要特别说明的是,函数对象本身和函数的上下文不是同一个东西,比如上图中,我并不知道匿名函数上下文的__proto__指向哪里,但是我认为它不会指向Function.prototype,因为如果Function.prototype在匿名函数上下文的继承链中,那么下面代码应该能正常输出:

Function.prototype.s=5;//给Funct.prototype添加s
var obj={};
(function(){ 
    with(obj){
       console.log(s);    //输出????
    }
}());

也就是说,因为obj本身没有s,一直找到Object.prototype中也没有s,转而从作用域链的下一节点也即匿名函数上下文寻找,同时匿名函数中也没有s,如果Function.prototype在匿名函数上下文的继承链中,那么应该能找到s=5,但是很遗憾,上面输出的是
Uncaught ReferenceError: s is not defined
若改成下面这样则能正常输出15,因为s本身就在匿名函数的上下文中

Function.prototype.s=5;//给Funct.prototype添加s
var obj={};
(function(s=15){ //参数
    with(obj){
       console.log(s);    //输出15
    }
}());

你可能感兴趣的:(javascript)