Skip to content

JavaScript深入之词法作用域和动态作用域 #3

Open
@mqyqingfeng

Description

@mqyqingfeng
Owner

作用域

作用域是指程序源代码中定义变量的区域。

作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。

JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。

静态作用域与动态作用域

因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。

而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。

让我们认真看个例子就能明白之间的区别:

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();

// 结果是 ???

假设JavaScript采用静态作用域,让我们分析下执行过程:

执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

假设JavaScript采用动态作用域,让我们分析下执行过程:

执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。

前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。

动态作用域

也许你会好奇什么语言是动态作用域?

bash 就是动态作用域,不信的话,把下面的脚本存成例如 scope.bash,然后进入相应的目录,用命令行执行 bash ./scope.bash,看看打印的值是多少。

value=1
function foo () {
    echo $value;
}
function bar () {
    local value=2;
    foo;
}
bar

这个文件也可以在 Github 博客仓库中找到。

思考题

最后,让我们看一个《JavaScript权威指南》中的例子:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

猜猜两段代码各自的执行结果是多少?

这里直接告诉大家结果,两段代码都会打印: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,对作者也是一种鼓励。

Activity

JSupot

JSupot commented on May 11, 2017

@JSupot

之前一直不太理解什么是词法作用域,受教了

MissCuriosity

MissCuriosity commented on May 27, 2017

@MissCuriosity

JS的上下文真的是个很神奇的东西,我看的是汤姆大叔的深入理解JS,这本书里面解释的也挺详细的,让我感觉看了之后稍微懂了,但是后面又会忘。
在看你文末两段代码的时候我其实觉得挺奇怪的,因为return f,我认为会返回function f() {};后面再看了一眼,原来checkscope()();也是无奈。

mqyqingfeng

mqyqingfeng commented on May 27, 2017

@mqyqingfeng
OwnerAuthor

@MissCuriosity 我也看过汤姆大叔的深入理解 JavaScript 系列,深受启发和影响。return f 确实会返回 function f(){},只是因为 checkscope()(),返回的 f 函数又被执行了~

yangshun352607664

yangshun352607664 commented on May 27, 2017

@yangshun352607664

作者的案列太过简单,应该写那种嵌套比较深的的

201 remaining items

tsingw

tsingw commented on Sep 8, 2022

@tsingw
tsingw

tsingw commented on Oct 11, 2022

@tsingw
Ronchan0805

Ronchan0805 commented on Jan 18, 2023

@Ronchan0805

补充: 函数的作用域基于函数创建的位置;函数内this指向基于函数运行的位置

rongda

rongda commented on Jan 18, 2023

@rongda
tsingw

tsingw commented on Jan 18, 2023

@tsingw
wmasfoe

wmasfoe commented on Mar 28, 2023

@wmasfoe

蒙蔽了,老和 this 弄混。🙈

rongda

rongda commented on Mar 28, 2023

@rongda
tsingw

tsingw commented on Mar 28, 2023

@tsingw
kkkelicheng

kkkelicheng commented on Oct 13, 2023

@kkkelicheng

下面有一段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先定义了,但是第一种那种写法如何理解呢。

tsingw

tsingw commented on Oct 13, 2023

@tsingw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @codetest@rongda@juventusforever@slogeor@Ghohankawk

        Issue actions

          JavaScript深入之词法作用域和动态作用域 · Issue #3 · mqyqingfeng/Blog