【前端工程师手册】JavaScript之作用域
【前端工程师手册】JavaScript之作用域
什么是作用域
来一段《你不知道的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
所以打印的是函数
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
go 笔记
go 笔记 defer defer 的参数绑定是在 defer 时,而不是在执行时,和 go 是一样的。 for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) } 会输出4 3 2 1 0 make && new make 只能应用于 slice,map,channel,返回的不是指针。 以 slice 为例,一个 slice 是引用 copy,但是每个 slice 结构体战三个字。 channel range 用法,以及如何idiomatic的使用 closure func handle(queue chan *Request) { for r := range queue { process(r) } } func Serve(queue chan *Request) { for req := range queue { req := req // Create new instance of req for the goroutine. sem <- 1 go func() { process(req)...
- 下一篇
【前端工程师手册】JavaScript作用域拾遗
【前端工程师手册】JavaScript作用域拾遗 昨天总结了一些作用域的知识【前端工程师手册】JavaScript之作用域,但是发表完发现忘记了一些东西,今天拾个遗。 昨天说到了JavaScript中没有块级作用域,其实在es6中是有的。 es6中的块级作用域 先举个栗子: var foo = true; if (foo) { let bar = foo * 2; bar = something( bar ); console.log(bar); } console.log( bar ); // ReferenceError 这个是let最直观的作用,在一对大括号中创建了块级作用域,bar会在大括号中的代码执行完毕后销毁。 再举个栗子: for(var i = 1;i <= 5;i++) { setTimeout(function() { console.log(i) }, i*1000) } // 每隔一秒打印一个6,共打印5次 如果说这段代码的初衷是间隔1秒打印出1、2、3、4、5的话,结果是令人大跌眼镜的,真正的结果是每隔1秒打印一次6,打印5次. 为什么会这样子?首先是因...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS关闭SELinux安全模块
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Red5直播服务器,属于Java语言的直播服务器