前端必须掌握的作用域!!!

原文:https://note.noxussj.top/?source=nowcoder

变量作用域

一个变量的作用域(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。

链表结构如下

alt

链表代码如下

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
}

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

原文:https://note.noxussj.top/?source=nowcoder

全部评论

相关推荐

10-09 00:50
已编辑
长江大学 算法工程师
不期而遇的夏天:1.同学你面试评价不错,概率很大,请耐心等待;2.你的排名比较靠前,不要担心,耐心等待;3.问题不大,正在审批,不要着急签其他公司,等等我们!4.预计9月中下旬,安心过节;5.下周会有结果,请耐心等待下;6.可能国庆节前后,一有结果我马上通知你;7.预计10月中旬,再坚持一下;8.正在走流程,就这两天了;9.同学,结果我也不知道,你如果查到了也告诉我一声;10.同学你出线不明朗,建议签其他公司保底!11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
dongsheng66:如果想进大厂的话,在校经历没必要占这么大篇幅,可以把专业技能单独放一个专栏写,可以加个项目经历
点赞 评论 收藏
分享
点赞 1 评论
分享
牛客网
牛客企业服务