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

稜角沙盒
• 阅读 1133

这几天因为对于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修饰的变量的搜索顺序的理解

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

按照这个思路,发现所有对象的原型链都最终指向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修饰的变量的搜索顺序的理解
下面做个实验,证明确实是二维查找。
使用嵌套的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修饰的变量的搜索顺序的理解


最后要特别说明的是,函数对象本身和函数的上下文不是同一个东西,比如上图中,我并不知道匿名函数上下文的__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
    }
}());
点赞
收藏
评论区
推荐文章
Dax Dax
4年前
js高频手写大全
1.手写instanceofinstanceof作用:判断一个实例是否是其父类或者祖先类型的实例。instanceof在查找的过程中会遍历左边变量的原型链,直到找到右边变量的prototype查找失败,返回false2.实现数组的map方法3.reduce实现数组的map方法4.手写数组的reduce方法reduce()方法接收一个函数作为累
Souleigh ✨ Souleigh ✨
4年前
JS - 作用域
一、作用域作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合换句话说,作用域决定了代码区块中变量和其他资源的可见性举个例子function myFunction(){    let inVariable  "函数内部变量";}myFunction();//要先执行这个函数,否则根本不知
Karen110 Karen110
3年前
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript有函数作用域:这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域:它们只能在函数中访问。JS://codeherecann
徐小夕 徐小夕
4年前
如何用不到200行代码写一款属于自己的js类库
前言JavaScript的核心是支持面向对象的,同时它也提供了强大灵活的OOP语言能力。本文将使用面向对象的方式,来教大家用原生js写出一个类似jQuery这样的类库。我们将会学到如下知识点:闭包:减少变量污染,缩短变量查找范围自执行函数在对象中的运用extend的实现原理如何实现跨浏览器的事件监听原型链与继承接下来我会对类库
菜园前端 菜园前端
2年前
为你解惑JS作用域和作用域链知识
原文链接:变量作用域一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都是可以访问的。然而在函数内声明的变量只能在函数体内访问,它们是局部变量,作用域是局部性的。函数参数也是局部变
御弟哥哥 御弟哥哥
4年前
彻底理解js的作用域链
在之前的文章(https://www.helloworld.net/p/G4dFV7tALU4J)中我已经介绍了执行上下文的变量对象。在这一篇文章我要介绍执行上下文的作用域链了。执行上下文.作用域链(scopechain)作用域链与变量对象有着密不可分的关系,因为作用域链就是变量对象的数组!其中第
Jacquelyn38 Jacquelyn38
4年前
你所知道的JS变量作用域
变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内
御弟哥哥 御弟哥哥
4年前
彻底理解js的执行上下文,以及变量对象
在js中,执行上下文(ExecutionContext)是非常重要的一种对象,它保存着函数执行所需的重要信息,其中有三个属性:变量对象(variableobject),作用域链(scopechain),this指针(thisvalue),它们影响着变量的解析,变量作用域和函数this的指向。上下文栈(ExecutionContextS
Stella981 Stella981
3年前
JavaScript学习总结(十七)——Javascript原型链的原理
一、JavaScript原型链ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。在JavaScript中,用__proto__属性来表示一个对象的原型链。当查找一个对象的属性时,JavaScript会向上遍历原型
Stella981 Stella981
3年前
JavaScript 基于原型链的继承
JavaScript对象是动态的属性“包”(指其自己的属性)。JavaScript对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。遵循ECMAScript标准,someObject.Prototype
Wesley13 Wesley13
3年前
Java连载7
一、变量1.注意点:在同一个“作用域”中,变量名不能重名,但是变量可以重新赋值。2.什么是作用域?答:描述的是变量的有效范围,在范围之内是可以被访问的,只要出了作用域就无法访问(也就是在大括号里面才行)3.关于变量的分类(1)局部变量:在方法体中声明的变量;(2)成员变量:在方法体外声明的变量。4.在不同的作用域中,变量名是可
稜角沙盒
稜角沙盒
Lv1
黄河远上白云间,一片孤城万仞山。
文章
4
粉丝
0
获赞
0