为你解惑JS作用域和作用域链知识

菜园前端
• 阅读 352

原文链接:https://note.noxussj.top/?source=helloworld


变量作用域

一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在 JavaScript 代码中的任何地方都是可以访问的。然而在函数内声明的变量只能在函数体内访问,它们是局部变量,作用域是局部性的。函数参数也是局部变量,它们只能在函数体内访问。

在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就会被局部变量所覆盖。

var scope = 'global'

function checkscope() {
    var scope = 'local'

    return scope
}

checkscope() // 'local'

函数作用域和声明提前

JavaScript 的函数作用域是指在函数内声明的所有变量在函数体内任何地方都是可以访问的,这意味着变量在声明之前就可以访问了。JavaScript 的这个特性被非正式的称为声明提前(hoisting),即 JavaScript 函数内声明的所有变量(不包含赋值)都被提升至函数体的顶部。声明提前这步操作是在 JS 引擎预编译时进行的,也就是在代码开始运行之前。

function test() {
    var i = 2

    if (true) {
        var j = 0

        for (var k = 0; k < 10; k++) {}

        console.log(k) // 10
    }

    console.log(j) // 0
}

test()

以上代码,在不同位置定义了变量 i、j、k,但是它们都在同一个函数作用域内。由于声明提前的特性,这三个变量在函数体内任何地方都是可以访问的。

声明提前后相当于下图的效果:

function test() {
    var i
    var k
    var j

    i = 2

    if (true) {
        j = 0

        for (k = 0; k < 10; k++) {}

        console.log(k) // 10
    }

    console.log(j) // 0
}

test()

作用域链

如果将一个局部变量看做是自定义实现的对象的属性的话,那么可以换个角度来解读变量作用域。每一段 JavaScript 代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象或者链表,这组对象定义了这段代码作用域中的变量。在这里涉及到了链表的概念,本来也是打算留在数据结构算法再讲的,但是这里用到了就简单介绍一下什么是链表。

链表是有序的数据结构,链表中的每个部分称为节点。每个节点都包含两个属性,一个是 value 主要存放当前节点的数据,另外一个是 next 主要用来指向下一个节点。

作用域链查找过程

var x = 10
var y = 10

function a() {
    var y = 20

    function b() {
        var y = 30

        console.log(x)
    }

    b()
}

a()

从上面的代码我们可以得出,全局对象 window 上存在 x 和 y 属性。当前存在三个作用域,一个是全局作用域,一个作用域 a、另外一个是作用域 b。

链表结构如下

为你解惑JS作用域和作用域链知识

链表代码如下

var window = {
    value: {
        x: 10,
        y: 10
    },
    next: null
}

var a = {
    value: {
        y: 20
    },
    next: window
}

var b = {
    value: {
        x: 30
    },
    next: a
}

当 JavaScript 需要查找变量 x 的值的时候(这个过程称为变量解析 variable resolution),它会从链表的第一个对象开始查找,如果这个对象有一个名为 x 的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为 x 的属性,则会继续查找下一个对象(通过链表节点的 next),以此类推(链表的最后一个对象就是全局对象)。如果作用域链上没有任何一个对象含有 x 的属性,那么就会认为这段代码的作用域链上不存在 x,并最终抛出一个引用错误异常 Uncaught ReferenceError: x is not defined。

作用域链创建过程

当定义一个函数时,它实际上会保存一个作用域链(每个函数都有独立的作用域链)。

正常形式

var x = 10
var y = 10

function a() {
    var x = 20
}

链表形式

var window = {
    value: {
        x: 10,
        y: 10
    },
    next: null
}

var a = {
    value: null,
    next: window
}

例如上图,我们定义了函数 a,这个时候我们还没有进行调用。函数 a 的作用域链为:a -> window

正常形式

var x = 10
var y = 10

function a() {
    var x = 20
}

a()

链表形式

var window = {
    value: {
        x: 10,
        y: 10
    },
    next: null
}

var a = {
    value: {
        x: 20
    },
    next: window
}

当调用这个函数时,它就会创建一个新执行上下文环境(对象)来存储它的局部变量,并将这个对象添加至刚才所保存的作用域链上。

点赞
收藏
评论区
推荐文章
Souleigh ✨ Souleigh ✨
3年前
JS - 作用域
一、作用域作用域,即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合换句话说,作用域决定了代码区块中变量和其他资源的可见性举个例子function myFunction(){    let inVariable  "函数内部变量";}myFunction();//要先执行这个函数,否则根本不知
Dax Dax
3年前
JS核心原理理解闭包
前置概念在正式看闭包之前,我们先来学习一下前置知识,那就是JS中的作用域,我们知道,在ES5之中,作用域分为两种:全局作用域和函数作用域,随着ES6的到来,新增了块级作用域,想更好的理解闭包,那么搞清楚作用域是首要条件全局作用域我们知道,对于变量而言,我们一般会分成两类:全局变量和局部变量,一般定义在最外围环境的为全局变量,定义在函数当中的为局部变量,在we
Karen110 Karen110
3年前
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript有函数作用域:这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域:它们只能在函数中访问。JS://codeherecann
Jacquelyn38 Jacquelyn38
3年前
你所知道的JS变量作用域
变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内
Jacquelyn38 Jacquelyn38
3年前
重学JavaScript第1集|变量提升
变量提升就好比JavaScript引擎用一个很小的代码起重机将所有var声明和function函数声明都举起到所属作用域(所谓作用域,指的是可访问变量和函数的区域)的最高处。这句话的意思是:如果在函数体外定义函数或使用var声明变量。则变量和函数的作用域会提升到整个代码的最高处,此时任何地方访问这个变量和调用这个函数都不会报错;而在函数体内定义函数或使用va
Stella981 Stella981
3年前
Javascript 变量 var与不var的区别
1.在函数作用域内加var定义的变量是局部变量,不加var定义的就成了全局变量。使用var定义var a  'hello World';function bb(){    var a  'hello Bill';    console.log(a);   }bb()   // 'hello Bill'conso
Stella981 Stella981
3年前
Python的四种作用域及调用顺序
↑关注星标 ~从此不迷路,后台回复【礼包】送你Python自学资料作用域又可以被称为命名空间,指变量起作用的范围。Python变量作用域可以分为四种,分别为局部作用域、嵌套作用域、全局作用域、内置作用域。作用域英文简写局部作用域LocalL嵌套作用域EnclosedE全局作用域Global
Wesley13 Wesley13
3年前
Java连载7
一、变量1.注意点:在同一个“作用域”中,变量名不能重名,但是变量可以重新赋值。2.什么是作用域?答:描述的是变量的有效范围,在范围之内是可以被访问的,只要出了作用域就无法访问(也就是在大括号里面才行)3.关于变量的分类(1)局部变量:在方法体中声明的变量;(2)成员变量:在方法体外声明的变量。4.在不同的作用域中,变量名是可
Wesley13 Wesley13
3年前
C语言面试题大汇总之华为面试题 Eddy整理
1、局部变量能否和全局变量重名?  答:能,局部会屏蔽全局。要用全局变量,需要使用"::";局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。2、如何引用一个
小万哥 小万哥
1年前
Python 作用域:局部作用域、全局作用域和使用 global 关键字
变量只在创建它的区域内可用。这被称为作用域。局部作用域在函数内部创建的变量属于该函数的局部作用域,并且只能在该函数内部使用。示例:在函数内部创建的变量在该函数内部可用:pythondefmyfunc():x300print(x)myfunc()函数内部的函