-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript深入之词法作用域和动态作用域 #3
Comments
之前一直不太理解什么是词法作用域,受教了 |
JS的上下文真的是个很神奇的东西,我看的是汤姆大叔的深入理解JS,这本书里面解释的也挺详细的,让我感觉看了之后稍微懂了,但是后面又会忘。 |
@MissCuriosity 我也看过汤姆大叔的深入理解 JavaScript 系列,深受启发和影响。 |
作者的案列太过简单,应该写那种嵌套比较深的的 |
谢谢😜 |
@yangshun352607664 哈哈,快来举一个例子~~~ |
核心就是: 函数的作用域在函数定义的时候就决定了 真正理解这句话的时候闭包什么的也就理解了。
|
@rccoder 感谢补充!非常赞同,词法作用域其实是非常重要的基础,所以才会作为第二篇去讲解。 |
在全局作用域中“定义”一个函数到时候,只会创建包含全局作用域的作用域链。 |
@suoz 你剧透了哈~ 😂 |
@rccoder 我搬运了下你的博客中这段的翻译,希望不要介意~
链接地址: rccoder 博客链接 |
我有一点想不通,在读作者 执行上下文的文章,感觉执行上下文是在函数调用时准备的,作用域规定了如何查找变量。可是查找的变量是通过执行上下文中的变量对象和作用域链查找的,这是动态作用域的原理啊,为什么JS又是静态作用域了。 |
@double-chen 动态作用域和静态作用域,决定的是作用域链的顺序 |
var a = 10;
var o = {
a:11,
b:{
fn:function(){
console.log(a);
}
}
}
o.b.fn(); 函数包裹函数那种作用域理解了,这样的又有点懵了 |
@yangshun352607664 结果是 10 ,因为变量 a 并不能读取到对象 o 的属性 a ,如果 console.log(o.a),就会打印 11,函数 fn 的作用域链为 [AO, Global.VO],而 Global.VO 中包括了变量 a 和变量 o。 |
var value = 1;
function bar(){
var value =2;
console.log(value)
}
bar() //2 为什么这回又返回2了呢? 我还是没理解 |
如果是这样的,function out(function(){console.log(xxx)})这种形式,那括号里的函数作用域是怎么样的? |
@mengxin-FE 词法作用域决定了变量查找的顺序,这个顺序是从函数内部开始,然后到函数定义的外层,函数内部已经有值,所以就会打印 2 |
写得让人很容易理解 |
所以,作用域是在一个代码块编译时就确定了,而作用域链是在函数执行时才确定,这样理解对吗? |
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
大佬,es6是如何对待{}的呢?{}生成一个新作用域么?为啥直接写{}的代码,里面的代码也会执行呢?是不是意味着{}是一个立即执行函数呢?
|
函数提升了,foo()执行之前还没被定义,函数声明和变量声明都会被提升,但是函数会先被提升,然后才是变量。 执行顺序是这样的: //1:
var val = 10;
//2: 此时函数提升
function foo(){
//此时val是全局作用域下的val引用,还没有被覆盖
console.log(val); // 10
}
//3:
foo();
//4:
var val = 20; |
这是来自QQ邮箱的假期自动回复邮件。
您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
|
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
对于let来讲,它将创建的变量绑定到所在的任意作用域中,也就是说,let为其声明的变量隐式的绑定了所在的块作用域。这样会导致一个问题,就是在修改和移动的时候,如果不去关注块作用域中绑定的变量,会造成代码的混乱。 ES6中新增的{...},用{...}去创建一个显式的块作用域可以解决上面讲的问题。而且let声明的变量只能在在这个块作用域里访问,有暂时性死区的特性(即:不会在块作用域中进行提升,声明前并不“存在”)。 是创建一个块作用域,和函数无关。 |
我是初学者对于一些概念不是太明白,谢谢这篇文章解答了我的解惑,期待您的更加优质的内容! |
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
受益 |
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
你个傻冒,看了这个,连最基本的都不会了,肯定是2呀,value是局部变量,局部优先,其次全局,之所以你会困惑,因为作者的例子是执行函数,函数的作用域不一样,你个憨憨 |
你好,我已经收到你的来信!
|
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
1 similar comment
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
补充: 函数的作用域基于函数创建的位置;函数内this指向基于函数运行的位置 |
你好,我已经收到你的来信!
|
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
蒙蔽了,老和 |
你好,我已经收到你的来信!
|
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
下面有一段JavaScript的代码: let activeEffect
function whenDepsChanged(update) {
const effect = ()=>{
activeEffect = effect
update()
}
effect()
} 请问,箭头函数中为什么能使用到effect?effect不是一个变量吗,等闭包定义后才赋值给这个effect变量啊。 function whenDepsChanged(update) {
const effect2 = () => {console.log("effect2")}
const effect = ()=>{
activeEffect = effect2
update()
}
effect()
} 如果换成上面这一种我理解,因为effect2先定义了,但是第一种那种写法如何理解呢。 |
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
作用域
作用域是指程序源代码中定义变量的区域。
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
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,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: