JS中作用域和作用域链的疑问,我理解的到底是哪个地方错了,图文比较长+啰嗦

MikeDDT009 2019-03-08 05:56:13
啰嗦的前言:
首先为什么要问这个问题是之前的一个疑问“为什么JS中许多变量能够随意使用”的后续,当上一个问题有人说到多了解作用域的时候,这两天就仔细看了下《JavaScritp高级程序设计》,先是运算符部分知道了不知道类型就瞎搞,太多的自动类型转换,只能说自己设计的那么类型要知道,接着看到了要明确了解的作用域部分,总共讲解作用域也就1张纸,排除例子也就只有1/2纸即1页内容,想着还简单,但是在少量例子的情况下,自己实际理解到的是错误的,但是又不明白原因,百度查下基本说法都是和书上大同小异的,例子也和书上差不多,也没有解决自己遇到的问题,于是开贴询问下:


理解如下:
1.每一个函数都是一个执行环境,代码的执行无非就是全局环境嵌套另外一个环境,环境再嵌套一个环境类似如下:

"use strict";

//定义全局方法,作为执行环境的起点,环境编号1
function firstMeth(){
//firstMeth环境里定义一个secondMeth的方法,环境编号2
function secondMeth(){
var num = 1;
//secondMeth环境里定义一个thirdMeth方法,环境编号3
function thirdMeth(){
console.log(num);
}
//执行thirdMeth方法
thirdMeth();
}
//执行secondMeth方法
secondMeth();
}

//全局环境开始执行firstMeth,环境编号0
firstMeth();

以上根据代码逐行阅读,可以知道有4个运行环境分别为0,1,2,3,本身是嵌套,我把分解为下图方式进入不同环境:


在合格时候就有一个作用域链,将它当成一个队列来看吧,数据是先进后出,就描述成一个这样的结构,(写在前面的是最后进入)的这是当前正在执行到了thirdMeth环境里那么作用域链的结构如下【环境3的变量对象,环境2的变量对象,环境1的变量对象,环境0的变量对象(或者全局对象window)】,当我在thirdMeth里面没有定义但是访问num变量的时候,就会根据作用域链取出变量对象寻找num变量,根据先进后出从依照如下顺序过程知道在某一变量对象里面找到num为止(如果没有找到就说明是undefined),环境3的变量对象→环境2的变量对象→环境1的变量对象→环境0的变量对象,所以能达到这种没有声明但是却能访问前面变量的效果,解答了我之前的一个疑问。

这次的疑问,前面是说的自己的理解,这里才算是问题,不打算看前面可以直接从这里开始往后看。
自己根据上面理解推断了一个想法:“代码解释逐行解释,任一功能都是由多个环境顺序构成(也可以说是方法的组合调用构成)”,然后就测试了如下代码片段:

"use strict";
/*
* 目的:验证自己理解的方法组成的作用域链
* 实验步骤:1.创建一个拥有两个方法的全局对象
* 2.此对象方法1实现内容为声明一个num变量,并赋值为1,调用方法2
* 3.此对象方法2实现内容为输出num
*/
//声明一个对象
var obj = {};
//为obj对象添加方法
obj.func1 = function() {
var num = 1;
//调用obj对象的func2方法
this.func2();
};
//为obj对象添加方法
obj.func2 = function() {
//输出num
console.log(num)

};
//调用obj对象的func1方法
obj.func1();


最后运行结果为num=undefined。这结果完全把我搞糊涂+懵逼了。本身想着这次设计的流程应该会达成以下结果:
有3个执行环境,全局环境0,func1环境,func2环境,形成的作用域链该是【func2变量对象,func1变量对象,全局变量对象】,当在func2变量对象里没有找到num,于是往上到func1变量对象找num,找到了,输出1,测试结束。
完全没相对会是undefined,搞的自己一脸懵逼,本来还打算再测试通过传参进去的方法,再调用最后组成如下图的作用域链:



然后反反复复的重复阅读这一小节的内容,还是没搞明白为什么,于是看看有没知道情况的。



...全文
246 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
MikeDDT009 2019-03-09
  • 打赏
  • 举报
回复
引用 1 楼 天际的海浪 的回复:
js中作用域链是静态的,也就是说,一个函数的上一级作用域取决于函数声明(定义)的位置,而不是函数调用的位置!!!
你的func1和func2函数都是在全局作用域中声明(定义)的,两个函数的上一级作用域都是全局作用域。


按照你的说法这样理解可对?
当解析JS的时候,由于JS的内容从一开始就是写定不可变的,那么在里面定义的不论变量还是方法本身就是一个固定层级,那么总是一个固定作用域链。
而程序的运行必然是会从全局环境进入一个定义好的方法里面往后延申直到又回到全局环境继续解析下一条入口,相当于定义好的方法必然有某一个被使用,不然是没有意义的
以下举几个例子:
情况1:定义一个对象a,它有一个属性pro,这个属性类型是一个对象(叫b吧),在为b添加一个方法meth
a.这是在全局环境赋值,这就是声明在了全局环境后?
a.pro.meth = function(){};

b.这是在一个方法体里面访问对象a然后给它添加方法,那它算是声明在这个方法体环境?也就是以后任何地方使用b的meth方法它的作用域链总是【meth的变量对象,addMeth的变量对象,全局变量对象】这种形式?
    function addMeth(){
a.pro.meth = function(){
//statements
}
}


情况2:实参问题,当对一个带参方法meth传入一个匿名方法的时候,参数方法算做是声明在meth方法?
以下反而没取到近的num=2,而是1

"use strict";

function test() {

var num = 1;

function test1() {

function test2() {

function test3(args) {
var num = 2;
args();
}
//执行test3,最里面的环境,这里传入了要给匿名函数
test3(function() {

console.log(num);

});

}
test2();
}

test1();

}

test();

情况3:在一个方法中调用另外一个方法的时候,被调用的方法又是按照它本身被声明的位置有独自的作用域链:
如下

"use strict";


function test(){

//statements

}

function test1(){

test();
}

test1();


test1方法被调用,那么在进入test1环境后,它本身作用域链是如下【test1变量对象,全局变量对象】,而当进入test环境后,当前作用域链是【test变量对象,全局变量对象】这样?
相对本身使用起来也就是某某某对象点点点访问,这作用域链实在给我感觉相当的混乱啊

天际的海浪 2019-03-09
  • 打赏
  • 举报
回复
你的三个例子都对。其实这种层级由内向外访问的关系,和其它语言(比如c语言)的语句块变量一样,只不过js把它用到了函数上。 只要记住函数的作用域链取决于函数声明的位置就不会混乱了。而且利用函数的作用域链还可以形成一种非常独特的保存私有数据的方式,那就是传说中的闭包。你如果有兴趣可以去看看闭包的应用。
古灬风 2019-03-08
  • 打赏
  • 举报
回复
你说的环境什么的表示不太懂,但是你的变量num第一个例子中,相当于在他(你说的环境)的下一级,打印时还有人认识他,undefined那个例子中num都干出去了,大环境都不认识他了,你再打印,他也不认识,你要是在顶级环境里定义,他还能打印出来,以上是个人理解,
天际的海浪 2019-03-08
  • 打赏
  • 举报
回复
js中作用域链是静态的,也就是说,一个函数的上一级作用域取决于函数声明(定义)的位置,而不是函数调用的位置!!! 你的func1和func2函数都是在全局作用域中声明(定义)的,两个函数的上一级作用域都是全局作用域。

87,903

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧