函数的作用域、全局作用域
kiss原则 keep it simple stupid
let 块级作用域
一、什么叫块?
一个{}就是一个块,比如
if{}
function(){}
for(var i = 0; i < 10; i++){}
二、let的特征
1
、
l
e
t
在同一作用域下不可重复声明
\color{#008B8B}1、let在同一作用域下不可重复声明
1、let在同一作用域下不可重复声明
var可以重复声明,后面的会覆盖前面的
var a = 1;
var a = 2;
let重复声明会报错,在全局作用域,函数作用域都是
let a = 1;
let a = 2;
这种写法会和上面报一样的错误,
function test(){
let a = 1;
var a = 2;
}
这样写也会报错,因为形参在函数作用域中相当于一个临时变量,在函数预编译的时候已经被定义了
function test(a){
let a = 10;
}
test(12);
也就说明了在同一作用域下,只要是用声明过的变量,就不能在用let重新声明,如果在不同的作用一下,就不会出现问题
function test(a){
{
//有一个单独的作用域
let a = 1;
}
console.log(a)//10
}
test(10)
2
、
l
e
t
不会提升,因此会产生一个暂时性死区(提升范围之前就叫暂时性死区(
t
e
m
p
o
r
a
l
d
e
a
d
z
o
n
e
))
\color{#2E8B57}2、let 不会提升,因此会产生一个暂时性死区(提升范围之前就叫暂时性死区(temporal dead zone))
2、let不会提升,因此会产生一个暂时性死区(提升范围之前就叫暂时性死区(temporaldeadzone))
let声明的变量没有提升也就是说let声明的变量,在声明之前使用它是不允许的,只能在代码执行到变量声明之后执行
function test(){
console.log(a);
let a = 1;
}
test();
打印undefined
var a = a;
console.log(a);//undefined
报引用错误
let b = b;
console.log(b);//报错
当y被赋值给x的时候y还没有被定义,也是暂时性死区造成的问题,因为函数参数默认赋值的时候,形式参数不存在变量提升问题,就相当于形式参数是let声明的
function test(x=y, y=2){
console.log(x, y);
}
test();
let 不会提升就会有一个问题 typeof的问题,typeof 不安全,如果使用 typeof 检测在暂时性死区中的变量
在使用var的时候typeof一个为声明的变量a,会打印undefined
typeof a
如果这样写
console.log(typeof a);//报错
let a;
会报错
3
、只能在当前的块级作用域下生效
\color{#808000}3、只能在当前的块级作用域下生效
3、只能在当前的块级作用域下生效
因为不在同一作用域下,就会报错
{
let a = 1;
}
console.log(a);
if(1){
let a = 1;
}
console.log(a);
for循环的块级作用域下的问题
//这样不会报错,因为一直在循环,不会走到打印a的代码
for(;1;){
let a = 3;
}
console.log(a);
报错,在全局作用域下访问不到i
for(let i = 0; i < 5; i++){
}
console.log(i);
第二种写法,因为第一次for循环,全局定义了i=0;循环完以后i变成10,在第二个循环中执行arr中的函数又重新定义了i=0,前面的值就被覆盖了,当arri执行的时候,每一次循环i的值变一次,所以打印出了0-5
var arr = [];
for(var i = 0; i < 5; i++){
arr[i] = function(){
console.log(i);
}
}
//这样写打印五个5
for(var j = 0; j < 5; j++){
arr[j]();//打印五个5
}
// 这样写,打印0-5
for(var i = 0; i < 5; i++){
arr[i]();
}
在for循环中(){}是否是同一作用域?
let会产生块级作用域,因此for循环中的()和{}不是同一作用域
for(var i = 0; i < 5; i++){
i = 'a'
console.log(i);//只打印一次a
}
for(let i = 0; i < 5; i++){
var i = 'a'
console.log(i);//还是只打印一次a
}
for(let i = 0; i < 5; i++) {
var i = 'a';
console.log(i); // SyntaxError: Identifier 'i' has already been declared
}
for(let i = 0; i < 5; i++){
let i = 'a'
console.log(i);//打印5个i
}
for循环中使用let
不是let创建了块级作用域,而是由于for的initialization block中用了let,触发了一种作用域创建机制
特殊机制
初始化照样做,
而每一次迭代的时候会发生以下事情:
1、系统会为for loop body创建新的词法作用域
{
arr[i] = function(){
console.log(i);
}
}
2、在词法作用域中新声明一个i(let i)
3、将上一次迭代或者初始化的值赋给这个新的i变量
4、对新的词法作用域内进行i操作 i++
所以我们可以将for中使用let声明变量执行循环的过程写成以下形式
var arr = [];
var memo;
{
let i = 0;
arr[i] = function(){
console.log(i);
}
memo = i + 1;
}
{
let i = memo;
arr[i] = function(){
console.log(i);
}
memo = i + 1;
}
{
let i = memo;
arr[i] = function(){
console.log(i);
}
memo = i + 1;
}
{
let i = memo;
arr[i] = function(){
console.log(i);
}
memo = i + 1;
}
{
let i = memo;
arr[i] = function(){
console.log(i);
}
memo = i + 1;
}
arr.forEach(cb=>cb())
块级作用域中函数声明的方式
ES5中规定函数只能在顶层作用域和函数作用域中生成,不建议在块级作用域中使用函数声明的方式,而是采用函数表达式
块级作用是没有返回值的,如果想要块级作用域有返回值可以使用do{}
let本质上就是为了JS增加一个块级作用域