昨天对函数作用域进行了简单介绍
简单回顾
看一个栗子
let d = 4;
(function(c) {
let a = 1,
b = 2
foo2()
function foo2() {
let a = 11
foo3()
function foo3() {
console.log(`a:${a}`); //=>11
console.log(`b:${b}`); //=>2
console.log(`c:${c}`); //=>3
console.log(`d:${d}`); //=>4
console.log(`e:${e}`); //Uncaught ReferenceError: e is not defined
}
}
})(3)
上面函数就是下面的简写形式
let d = 4;
function foo1(c) {
//相同部分省略
}
foo1(3)
当然也可以这么写
let d = 4;
(function (c) {
let a = 1,
b = 2;
(function () {
let a = 11;
(function () {
console.log(`a:${a}`); //=>11
console.log(`b:${b}`); //=>2
console.log(`c:${c}`); //=>3
console.log(`d:${d}`); //=>4
console.log(`e:${e}`); //Uncaught ReferenceError: e is not defined
})()
})()
})(3)
理论上是可行的,但代码可读性太差
函数创建后立即执行,并传入一个参数=>3
变量的查找是从内向外逐层查找,如果到最外层依然找不到就会抛一个not defined的错
这样就形成了一个作用域链,内层同名变量会覆盖外层的,(有没有想到原型链?)
块级作用域
先普及一个概念:
ECMAScript和JavaScript关系:
ECMAScript是一个国际通过的标准化脚本语言。可以简单理解为:ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展。
什么是块级作用域?
任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
在es6标准出来之前,javascript是不存在块级作用域的
一个栗子
{
var name = "郭靖";
console.log(name);//=>郭靖
}
console.log(name);//=>郭靖
再看一个不那么hello world的栗子
function foo() {
var hisName = "郭靖"
for (var i = 0; i < 10; i++) {
// 啥也不干
}
console.log(i);//=>10
console.log(hisName);//=>"郭靖"
}
foo();
//console.log(i);//=>Uncaught ReferenceError: i is not defined
console.log(hisName); //=>Uncaught ReferenceError: hisName is not defined
通过上面的栗子我们可以发现
在foo内部,在for循环外部,成功的打印出i,这说明使用var声明的变量是不存在块级作用域的
在foo外部,我们打印i或者hisName,都会报Uncaught ReferenceError的错误,i和hisName是foo内部的私有变量,这说明使用var声明的变量存在函数作用域
我们上面介绍了js块级作用域的概念,两个花括号内部定义的内容,外部无法访问的,而javascript又存在函数作用域,
好了,我们可以通过javascript的函数作用域来模拟实现块级作用域
还是上面的栗子
function foo() {
(function () {
var hisName = "郭靖"
for (var i = 0; i < 10; i++) {
// 啥也不干
}
})()
console.log(i);//Uncaught ReferenceError: i is not defined
console.log(hisName);//上面报错,这里就不会执行,这里就算执行,也会报错
}
foo();
// console.log(i);//=>Uncaught ReferenceError: i is not defined
// console.log(hisName); //=>Uncaught ReferenceError: hisName is not defined
这不正是我们昨天讨论的内容吗?立即执行函数,函数执行后,内部变量会被销毁(闭包情况暂不考虑,后面的文章会详细介绍),所以外部就无法访问啦
所以又说到昨天的内容了,为了防止命名冲突我们一般这么写
(function () {
var herName = "黄蓉"
})()
console.log(herName);//Uncaught ReferenceError: herName is not defined
一个简易块级作用域诞生了
ES6中块级作用域
我们现在写代码,如果项目允许使用ES6,那就很少会有人通过函数作用域来实现块级作用域了,因为ES6支持块级作用域
let
说起let,就会情不自禁的将let和var进行比较,关于let和var的介绍的文章多如牛毛,不再赘述
说几个明显的区别
var变量声明提升
看几个栗子,有助于理解
hisName = "郭靖"
var hisName
console.log(hisName);
undefined?
最后输出“郭靖”
上面的代码经过js引擎编译处理,变成了这样
var hisName
hisName = "郭靖"
console.log(hisName);
继续看栗子
console.log(hisName);
var hisName = "郭靖"
最后输出“undefined”
上面的代码经过js引擎编译处理,变成了这样
var hisName
console.log(hisName);
hisName = "郭靖"
javascript代码并不是一行一行往下执行的,分为2个步骤:
- 编译(词法解释/预解释)
- 执行
理解了没?如果理解了,继续看一个栗子
var hisName = "郭靖";
function hero() {
console.log(hisName);
var hisName = "洪七公";
}
hero();
console.log(hisName);
先不关心输出什么,我排一下顺序
var hisName = "郭靖";
function hero() {
var hisName
console.log(hisName);
hisName = "洪七公";
}
hero();
console.log(hisName);
如果顺序排队了,基本就没什么问题
执行hero函数,打印hisName,声明了,但是没有复制,所以是undefined
到这里,你可能会有一个疑问,js存作用域链,hero内部的hisName是undefined,它不会继续像外层作用域继续查找吗?
当然不会啦,只要声明过,如果没赋值,就存在一个默认值=>undefined
hero()后面的console.log(hisName);//=>郭靖,为什么?
因为函数作用域的作用,hero内部的变量,外部无法访问(暂不考虑闭包),变量首先查找的是同级作用域,同级作用域不存在就会继续向外层查找,但不会向内层查找
通过这三个栗子,我相信,你已经搞懂了javascript的变量提升
let块级作用域
前面说在ES6标准出来之前,是不存在块级作用域的,需要函数作用域进行配合模拟实现块级作用域
let 存在块级作用域,let 不存在变量提升(必须先声明,声明前不能进行赋值等相关操作)
{
hisName = "郭靖";//在这里级报错了Uncaught ReferenceError: hisName is not defined
let hisName
console.log(hisName);
}
再来一个栗子
let hisName = "黄药师";
let herName = "黄蓉"; {
let hisName = "洪七公"; {
let hisName = "郭靖";
console.log(hisName);//=>郭靖
console.log(herName);//=>黄蓉
}
console.log(hisName);//=>洪七公
}
console.log(hisName);//=>黄药师
输出也没什么悬念
//=>郭靖
//=>黄蓉
//=>洪七公
//=>黄药师
let 暂时性死区
暂时性死区,听起来好迷糊,其实没什么,
因为let不存变量提升,使用let命令声明变量之前,该变量都是不可用的
其实还是关于变量提升的问题
const?
关于const在块级作用域上,和let保持一致,不在此赘述
END
最新评论
这小生活不错呀
不错,必须顶一下!
看着你还在坚持,很好
看来忙了也没时间更新博客了
NIce。学习了。。。。
网站不错!!!!
简洁实用,好文章!
不错,过来支持一下