资讯专栏INFORMATION COLUMN

【前端工程师手册】JavaScript之作用域

Paul_King / 2099人阅读

摘要:函数作用域和块作用域前面讲了是词法作用域,那么什么时候会创建作用域呢主要是基于函数级别的作用域,也就是每一个函数都会创建一个作用域。函数会被当作函数表达式而不是一个标准的函数声明来处理。

什么是作用域

来一段《你不知道的JavaScript-上卷》中的原话:

几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个 值进行访问或修改,这些变量住在哪里?换句话说,它们储存在哪里?最重要的是,程序需要时如何找到它们?这些问题说明需要一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。这套规则被称为作用域

总之就是作用域就是一套规则,这个规则规定了程序如何去找到变量

词法作用域

先看一个例子:

function func1() {
    console.log(index)
}

function func2() {
    var index = 10
    func1()
}

var index = 100
func2()    // 100

为啥是100而不是10呢???
因为JavaScript是词法作用域
词法作用域简单地说就是:函数的作用域在声明的时候就决定好了。和词法作用域相对的是动态作用域,动态作用域关注函数从何处调用
上面的代码中,声明func1时,它就处于全局作用域中,所以index就是100,即使执行func1时也是。

函数作用域和块作用域

前面讲了JavaScript是词法作用域,那么什么时候会创建作用域呢???
JavaScript主要是基于函数级别的作用域,也就是每一个函数都会创建一个作用域。为什么说主要呢?因为with和try-catch语句也实现了块作用域,当然了用的很少。

var flag = true;
if (flag) {
    var num = 10
}
console.log(num)    // 10

如果有块级作用域的话,此时打印num应该报错

function funcLevelScope() {
    var index = 10
}

console.log(index);    // ReferenceError

因为是函数级别的作用域,所以在函数外面访问不到函数内部的变量

如何模拟块作用域

当然了,第一反应就是在代码外面加上包装函数不就行了,比如这样子:

function scope() {
    var flag = true;
    if (flag) {
        var num = 10
    }
}
console.log(num)    // ReferenceError

但是此时的问题就是多了一个函数包装,且我们可以随意的去运行scope函数,这可能会造成不好的影响。
自执行函数解决了这个问题:

(function scope() {
    var flag = true;
    if (flag) {
        var num = 10
    }
})()
scope()    // ReferenceError
console.log(num)    // ReferenceError

包装函数的声明以 (function... 而不仅是以 function... 开始。尽管看上去这并不 是一个很显眼的细节,但实际上却是非常重要的区别。函数会被当作函数表达式而不是一个标准的函数声明来处理。

作用域内声明提升

变量声明提升
首先明确的是

var x = 10

像这样一句代码可以分为声明和赋值两句:

var x
x = 10

明确这个概念再继续学习

console.log(x)    // undefined
var x = 10

为什么不直接报错呢?
因为上面的代码片段等于:

var x    // 声明提前
console.log(x)
x = 10

注意到变量的声明提前到了当前作用域的最前面
函数声明提升

hoist()    // hoist!
function hoist() {
    console.log("hoist!")
}

为啥函数会成功执行了,因为上面的代码片段相当于:

function hoist() {
    console.log("hoist!")
}
hoist()

函数声明被提升到了作用域的最前面
那么函数表达式会被提升吗??

hoist()    // TypeError
var hoist = function () {
    console.log("hoist")
}

因为这个相当于:

var hoist
hoist()    // TypeError
hoist = function () {
    console.log("hoist")
}

变量声明提升和函数声明提升的优先级
先说结论:函数会首先被提升,然后才是变量
看例子:

console.log(x)
var x = 10
function x() {
    console.log("func x")
}

显然此时打印的x是一个函数
这是因为上面的代码片段相当于:

function x() {
    console.log("func x")
}
var x
console.log(x)    // f x(){...}
x = 10

所以打印的是函数

参考资料:
JavaScript深入之词法作用域和动态作用域
你不知道的JavaScript-上卷

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94868.html

相关文章

  • 前端程师手册JavaScript闭包

    摘要:闭包确实是一个说烂了的概念,校招社招都会被问到,今天总结一番。先下定义,闭包是函数和该函数的词法作用域的组合。在这个栗子里,函数以及它对变量的引用就构成了闭包。闭包和作用域对于闭包和作用域的关系,我的理解是闭包其实就是作用域的延伸。 闭包确实是一个说烂了的概念,校招社招都会被问到,今天总结一番。先下定义,闭包是函数和该函数的词法作用域的组合。其实这个定义是比较教条的,可以直白的理解为闭...

    CarlBenjamin 评论0 收藏0
  • 前端程师手册JavaScript作用拾遗

    摘要:昨天总结了一些作用域的知识前端工程师手册之作用域,但是发表完发现忘记了一些东西,今天拾个遗。循环完毕之后,,且此时生成了个匿名函数,由于这个匿名函数处在同一个词法作用域中,所以他们引用同一个,所以当他们执行时,自然而然就会打出。 昨天总结了一些作用域的知识【前端工程师手册】JavaScript之作用域,但是发表完发现忘记了一些东西,今天拾个遗。昨天说到了JavaScript中没有块级作...

    flyer_dev 评论0 收藏0
  • 前端程师手册】this拾遗关于箭头函数的种种

    摘要:之前总结了的一些常见绑定情况前端工程师手册之的笔记,但是还有一些没有说到,今天继续学习一下。参考资料箭头函数你不知道的上卷 之前总结了this的一些常见绑定情况(【前端工程师手册】JavaScript之this的笔记),但是还有一些没有说到,今天继续学习一下。 es6箭头函数 先说结论:箭头函数没有自己的this,它是根据外层(函数或者全局,后面会说到箭头函数作为某个对象的方法时的情况...

    oogh 评论0 收藏0
  • 前端程师手册】总结一下前端存储

    摘要:如果不指定,默认为当前文档的主机不包含子域名。标识指定了主机下的哪些路径可以接受该路径必须存在于请求中。具体的使用参考教程和类似,但是可以直接使用来操作,具体的参看参考聊一聊前端存储那些事儿聊一聊 cookie 为什么会有cookie http协议的无状态,所谓无状态即是服务器并不是知道这次的请求和上次的请求是不是同一个client发来的,就好比你经常去一家超市买东西,老板并不记得你是...

    array_huang 评论0 收藏0
  • 基础知识 - 收藏集 - 掘金

    摘要:本文是面向前端小白的,大手子可以跳过,写的不好之处多多分钟搞定常用基础知识前端掘金基础智商划重点在实际开发中,已经非常普及了。 JavaScript字符串所有API全解密 - 掘金关于 我的博客:louis blog SF专栏:路易斯前端深度课 原文链接:JavaScript字符串所有API全解密 本文近 6k 字,读完需 10 分钟。 字符串作为基本的信息交流的桥梁,几乎被所有的编程...

    wdzgege 评论0 收藏0

发表评论

0条评论

Paul_King

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<