Open mqyqingfeng opened 7 years ago
@lzuliuyun 很好奇的是词法分析(词法作用域)这个阶段是发生在js编译阶段,还是执行阶段
@xiaofan9 词法分析是在编译阶段决定的,也就是说,在执行之前就已经确定了作用域的范围。你可以阅读下作者的“JavaScript深入之变量对象”:https://github.com/mqyqingfeng/Blog/issues/5
大佬,你后面举的javascript权威指南的两个例子,看着就是闭包呀,最终打出‘local scope‘从闭包也可以解释的通,能否把闭包和词法作用域打通说一下呢,感觉还是有点迷啊,谢谢
基础的知识只有自己啃书才能理解
发自魏宇的 iPhone
On Aug 22, 2018, at 11:14, xie991283109 notifications@github.com wrote:
大佬,你后面举的javascript权威指南的两个例子,看着就是闭包呀,最终打出‘local scope‘从闭包也可以解释的通,能否把闭包和词法作用域打通说一下呢,感觉还是有点迷啊,谢谢
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.
@yuu95 看权威指南就行?
你好,,我想问下,,实际项目中一般尽量写成局部变量还是全局变量,,
@jiayousuda 建议局部
你说错了,JavaScript即是词法作用域又是动态作用域,动态作用域指的是匿名函数的this和argument
你好,其实在最早之前学习js时就有这样的疑问,JavaScript里的作用域是对象吗?感觉不是,但是有这样一句话"JavaScript里万物皆对象".最近读<你不知道的JavaScript>其中有这样一句话"在 JavaScript 内部,作用 域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过 JavaScript 代码访问,它存在于 JavaScript 引擎内部。"所以更加迷惑了,请问,你是怎么理解的?谢谢!
@yh284914425
var a = 10;
var o = {
a:11,
b:{
fn:function(){
console.log(a);
}
}
}
o.b.fn();
你这个例子全局看下来只有两个作用域。一个是全局作用域(Global), 一个是 o.b.fn
引用的这个函数作用域。
变量对象(VO)
: 变量对象是与执行上下文相关的数据作用域。它是与上下文关联的特殊对象,用于存储被定义在上下文
中的变量
和函数声明
。
而 Global的变量对象(VO)只有两个。
a: 10
b: object
在一个函数
的上下文中有变量对象(VO)
还有活动对象(AO)
和[[Scope]]作用域链。
活动对象(AO):
在一个函数上下文中,
变量对象(VO)被表示为活动对象(activation object) AO
, 活动对象(AO)相比变量对象(VO),还可以包含特殊对象arguments。
所以o.b.fn对应的函数中的活动对象是空的。因为既没有参数arguments,也没有变量声明,更没有函数声明。 那么在o.b.fn中打印 console.log(a)
。这个函数自身没有,就会去它的父作用域(Global | 全局作用域)
中查找。你再看一下它父作用域中的变量对象中只保存两个属性。a: 10 b:object。 所以打印10
。
个人见解,如有错误麻烦勘正。
JS也存在动态作用域吧,with,try/catch的catch子句好像就是动态的吧,包含eval的代码也不能由定义时确定。
@Nanchenk 我也产生了同样的疑惑
var m=1
function foo(){
console.log(m);//2
}
try{
var m=2
foo()
}catch(e){
console.log(e)
}
在try/catch中若是静态编辑的话,那按理foo输出来的m应该是1,但这里是2 。
@mqyqingfeng 冴羽大大,麻烦指导下
@wd2010 你这个结果完全没问题啊,在foo调用前,重新声明了m,并赋值为2,foo调用的时候拿到的确实是2
@xiaofan9 我被try/catch给迷惑了,es5中他不会产生块级作用域
@xiaofan9 我被try/catch给迷惑了,es5中他不会产生块级作用域
catch中的作用域就是块级的,你的例子不对没体现出来
@Nanchenk es5中catch作用域不是块级的,比如
(function(){
e="default";
try{
throw "test";
}catch(e){
var e,x=123;
console.log(e); //test
console.log(delete e); //false
e=456;
console.log(e); //456
};
console.log(x); //123
console.log(e); //default
console.log(window.e); //undefined
})();
@Nanchenk es5 没块级作用域的概念
` var x = 21;
var talk = function () { console.log(x); var x = 20; }; talk ();
` 这个怎么用词法作用域解释下呢
` var x = 21;
var talk = function () { console.log(x); var x = 20; }; talk ();
` 这个怎么用词法作用域解释下呢
这个是因为var 是具有变量提升的, 会在函数顶部 先声明变量 然后在去赋值 ,console 的位置在声明之后 赋值之前所以打印出来时undefined
es5 的 try 是没有块级作用域的 所以在 try 里面的 var 修改了外部的 m 的值 同样的情况下在 es6 下,由于产生了块级作用域,输出就符合预期了
const m=1
function foo(){
console.log(m);//1
}
try{
const m=2
foo()
}catch(e){
console.log(e)
}
关于静态作用域与动态作用域,《你不知道的JavaScript》上册讲的很详细
var value = 1; function bar(){ var value =2; console.log(value) } bar() //2
为什么这回又返回2了呢? 我还是没理解
哈哈 ,兄弟,我怀疑你这是看文章看的走火入魔了...
调用 bar, 进入 bar 的执行上下文向上查找 value, 先找到函数内部的变量 所以打印2
葛星 | |
---|---|
imaxing@126.com | 签名由网易邮箱大师定制
在2019年01月10日 18:21,张宁乐notifications@github.com 写道: var value =1;
functionbar(){
var value =2;
console.log(value)
}
bar() //2
为什么这回又返回2了呢? 我还是没理解
哈哈 ,兄弟,我怀疑你这是看文章看的走火入魔了...
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
@SageWu 你只执行function f() {return scope}; var scope; f();
这段代码你就会发现 打印出来的确实是undefined
@xiaofan9 谢谢,最终发现,竟然是我用的那工具缓存了之前的赋值。
当调用 f 的时候 已经执行了 scope ="local scope"; 这就相当于在调用 f 的时候 window 下已经有了scope并且值为'local scope’, 在 f 执行的时候内部查找没找到, 继续向上查找到 window 上下文 就找到了' local scope' |
葛星 | |
---|---|---|
imaxing@126.com | 签名由网易邮箱大师定制
在2019年01月15日 00:09,SageWunotifications@github.com 写道:
@thisisandy 这段代码确实是返回 "local scope",因为是根据函数创建的位置,然后向外查找变量,自然是 'local scope'
说错了…… 应该是 undefined,因为有函数提升和变量提升,相当于
functionf() {return scope}; var scope; f(); scope ="local scope";
我的运行结果是返回"local scope",这让我觉得很是奇怪,明明f()先于scope的赋值。
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
看了好几遍,基本上是看懂了……
函数声明提升,所function foo
在 var value = 1
之前导致undefined的吧,怎么会是1呢
函数声明提升,所
function foo
在var value = 1
之前导致undefined的吧,怎么会是1呢
为何我输出的结果是: 1 undefined
看了好几遍,基本上是看懂了……
基本懂了,那么很快就忘了
这个跟 context、this 蛮容易弄混的。this 跟 context 都是指运行时的上下文,是跟运行时有关的参数。所以,一般在 定义
的时候使用 that = this 来保留上下文
看了好几遍,基本上是看懂了……
基本懂了,那么很快就忘了
所以还得彻底搞懂才行啊~🤦♂️
此处的 undefined 是由于函数没有 return 返回值导致的...
在 2019年3月6日,下午1:19,Salvador Zhou notifications@github.com 写道:
https://user-images.githubusercontent.com/22002351/53856044-3df82000-400b-11e9-9767-0869a71d98e2.png 函数声明提升,所function foo在 var value = 1之前导致undefined的吧,怎么会是1呢
为何我输出的结果是: 1 undefined
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mqyqingfeng/Blog/issues/3#issuecomment-469972210, or mute the thread https://github.com/notifications/unsubscribe-auth/APuGepXekQN7kuhhf3nfcAONnqCRC-aIks5vT0_ZgaJpZM4NFWFZ.
个人觉得 javascript 只有词法作用域,执行上下文 this 不能称之为动态作用域
此处的 undefined 是由于函数没有 return 返回值导致的... … 在 2019年3月6日,下午1:19,Salvador Zhou @.***> 写道: https://user-images.githubusercontent.com/22002351/53856044-3df82000-400b-11e9-9767-0869a71d98e2.png 函数声明提升,所function foo在 var value = 1之前导致undefined的吧,怎么会是1呢 为何我输出的结果是: 1 undefined — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#3 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/APuGepXekQN7kuhhf3nfcAONnqCRC-aIks5vT0_ZgaJpZM4NFWFZ.
最后为何输出undefined这个我知道的,我在这边其实是为了告诉楼上那位朋友 @Saitmob ,那段代码最终输出结果为1,而非他认为的undefined。
此处的 undefined 是由于函数没有 return 返回值导致的... … 在 2019年3月6日,下午1:19,Salvador Zhou @.***> 写道: https://user-images.githubusercontent.com/22002351/53856044-3df82000-400b-11e9-9767-0869a71d98e2.png 函数声明提升,所function foo在 var value = 1之前导致undefined的吧,怎么会是1呢 为何我输出的结果是: 1 undefined — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#3 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/APuGepXekQN7kuhhf3nfcAONnqCRC-aIks5vT0_ZgaJpZM4NFWFZ.
最后为何输出undefined这个我知道的,我在这边其实是为了告诉楼上那位朋友 @Saitmob ,那段代码最终输出结果为1,而非他认为的undefined。
emmm,因为只有一个输出,所以。。明明之前一直都知道那个undefined并不是console.log出来的。。话说怎么会只有一个undefined啊?
此处的 undefined 是由于函数没有 return 返回值导致的... … 在 2019年3月6日,下午1:19,Salvador Zhou @.***> 写道: https://user-images.githubusercontent.com/22002351/53856044-3df82000-400b-11e9-9767-0869a71d98e2.png 函数声明提升,所function foo在 var value = 1之前导致undefined的吧,怎么会是1呢 为何我输出的结果是: 1 undefined — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#3 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/APuGepXekQN7kuhhf3nfcAONnqCRC-aIks5vT0_ZgaJpZM4NFWFZ.
最后为何输出undefined这个我知道的,我在这边其实是为了告诉楼上那位朋友 @Saitmob ,那段代码最终输出结果为1,而非他认为的undefined。
emmm,因为只有一个输出,所以。。明明之前一直都知道那个undefined并不是console.log出来的。。话说怎么会只有一个undefined啊?
我也觉得奇怪,不是应该正确地输出最终结果 1 嘛,咋会只有一个undefeated……
function foo() { console.log(value); }
function bar() { var value = 2; foo(); } var value = 1; bar(); 为什么这个也会打印1啊。
function foo() { console.log(value); }
function bar() { var value = 2; foo(); } var value = 1; bar(); 为什么这个也会打印1啊。
这个代码相当于: var value;
function foo() { console.log(value); }
function bar() {
var value = 2;
foo();
}
value = 1;
bar();
1是foo中打印出来的
foo是在调用bar中调用的
而bar是在value = 1之后调用的
所以打印出来是1
函数的作用域在创建的时候已经确定了
foo的作用域跟 bar同级 foo中 没有value 所以 沿着作用域链网上找 就找到 顶层的 value = 1
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
最后这个例子 返回的f是什么呀? checkscope后面为什么是接两个()?
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
最后这个例子 返回的f是什么呀? checkscope后面为什么是接两个()?
执行checkscope
最终输出checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; }
执行checkscope();
最终输出f(){ return scope; }
执行checkscope()();
最终输出"local scope"
因为每一个的最终执行结果都是对应的(大雾)
function foo() { console.log(value); } function bar() { value = 2; foo(); } var value = 1; bar(); 这个就会打印2了,这个的执行顺序是什么样的啊?
function foo() { console.log(value); } function bar() { value = 2; foo(); } var value = 1; bar(); 这个就会打印2了,这个的执行顺序是什么样的啊?
bar
, 全局value赋值为2foo
, 输出2如果是这样的,function out(function(){console.log(xxx)})这种形式,那括号里的函数作用域是怎么样的? 这种写法是错误的。声明函数只能是function out(a){},
JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了,函数的作用域是基于函数创建的位置
"根据书写的位置” 先记住这句话 🤣
@double-chen 动态作用域和静态作用域,决定的是作用域链的顺序
能具体说一下吗,这两个分着看还行,但是合起来就不理解了。
@double-chen 动态作用域和静态作用域,决定的是作用域链的顺序
作用域链存储在什么地方
function foo() { console.log(value); }
function bar() { var value = 2; foo(); } var value = 1; bar(); 为什么这个也会打印1啊。
因为词法作用域,在书写foo()函数的时候,全局变量var value = 1;已经存在了,foo()持有对value的引用,所以不论在哪里执行foo()函数都会输出全局变量中的value的值;第二次写的代码中bar()函数中未用var声明,所以定义了全局变量value = 2;修改了全局变量value的值,所以输出的结果为2;
闭包能理解,前面的静态作用域的例子不大能理解。。。为啥foo()中没找到value之后,就去最外层找了?不应该去bar()中找吗?求助大神们
作者的案列太过简单,应该写那种嵌套比较深的的
是用来出面试题嘛
作用域
作用域是指程序源代码中定义变量的区域。
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
静态作用域与动态作用域
因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。
而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
让我们认真看个例子就能明白之间的区别:
假设JavaScript采用静态作用域,让我们分析下执行过程:
执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。
假设JavaScript采用动态作用域,让我们分析下执行过程:
执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。
前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。
动态作用域
也许你会好奇什么语言是动态作用域?
bash 就是动态作用域,不信的话,把下面的脚本存成例如 scope.bash,然后进入相应的目录,用命令行执行
bash ./scope.bash
,看看打印的值是多少。这个文件也可以在 Github 博客仓库中找到。
思考题
最后,让我们看一个《JavaScript权威指南》中的例子:
猜猜两段代码各自的执行结果是多少?
这里直接告诉大家结果,两段代码都会打印:
local scope
。原因也很简单,因为JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。
而引用《JavaScript权威指南》的回答就是:
JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量 scope 一定是局部变量,不管何时何地执行函数 f(),这种绑定在执行 f() 时依然有效。
但是在这里真正想让大家思考的是:
虽然两段代码执行的结果一样,但是两段代码究竟有哪些不同呢?
如果要回答这个问题,就要牵涉到很多的内容,词法作用域只是其中的一小部分,让我们期待下一篇文章————《JavaScript深入之执行上下文栈》。
下一篇文章
JavaScript深入之执行上下文栈
深入系列
JavaScript深入系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。