console.log(a)//undefined
var a=2;
console.log(a)//2
开始前先看上面这段代码,如果你知道变量提升/函数提升的概念,能够正确说出答案,可以跳过。
在JavaScript中,有变量提升的概念,在遇到var定义的变量时,会将该变量提升到作用域顶部,上述代码就变成了
var a=undefined
console.log(a)//undefined
a=2;
console.log(a)//2
同时函数声明也会发生提升,这里需要区分函数声明和函数表达式,函数声明会发生提升,而且是函数整体
发生提升,这一点与var
不同。也就是说,只要在同一个作用域内,随处都可以对函数声明进行调用。而函数表达式不会发生提升。
hello();//hello
sayhello();//报错
function hello(){
console.log('hello');
}
var sayhello=function(){
console.log('sayhello');
}
//函数声明提升后相当于
function hello(){
console.log('hello');
}
hello();
sayhello();
var sayhello=function(){
console.log('sayhello');
}
那么如果一个变量同时用函数声明和var进行声明,这时候打印出来的是什么呢?
console.log(hello);//ƒ hello(){ console.log('hello'); }
function hello(){
console.log('hello');
}
var hello="hanmeimei";
console.log(hello);//ƒ hello(){ console.log('hello'); }
var hello="hanmeimei";
function hello(){
console.log('hello');
}
可以看到,无论是var变量声明在前,还是var变量声明在后,打印出来的都是function。因此,函数声明优先级高于var变量声明。
此时JS解析时,如果同时遇到函数声明和var声明,此时选择函数声明。下面这段代码,在解析时,会将函数声明提升到头部,等遇到var hello="hanmeimei";
再对hello重新赋值。
console.log(hello);//ƒ hello(){ console.log('hello'); }
var hello="hanmeimei";
console.log(hello);//hanmeimei
function hello(){
console.log('hello');
}
console.log(hello);//hanmeimei
//函数声明提升后
function hello(){
console.log('hello');
}
console.log(hello);//ƒ hello(){ console.log('hello'); }
var hello="hanmeimei";
console.log(hello);//hanmeimei
console.log(hello);//hanmeimei
下面这段代码,在遇到hello=function hello(){console.log(lilei);}
又会重新将hello赋值为function
console.log(hello);//ƒ hello(){ console.log('hello'); }
var hello="hanmeimei";
console.log(hello);//hanmeimei
function hello(){
console.log('hello');
}
hello=function hello(){
console.log(lilei);
}
console.log(hello);// hello(){console.log(lilei);}
hello="end";
console.log(hello);//end
必须注意的一点是!不管是函数提升还是变量提升,都是提升到当前作用域头部,在函数作用域外部无法访问。而函数内却可以访问到函数外部作用域的变量
var a=2;
var b=10;
hello()
function hello(){
console.log(a);//undefined
say();//say
var a="hello";
function say(){
console.log('say');
}
console.log(a);//hello
console.log(b);//10
}
say();//报错
这和js的词法环境有关,没想到小小的一个var,let我越写越多……哭了
无论何时调用函数,都会创建一个新的执行环境,被推入执行上下文栈。此外,还会创建一个相关的语法环境。当查找一个变量时,会先在当前环境内查找。在当前环境内找不到变量时,就会去它的外部环境查找。当调用hello函数时,此时hello函数的外部环境为全局环境。
在hello环境中查找变量a时,
---->>会先在hello环境进行查找,此时找到a=“hello”即完成查找。
在hello环境中查找变量b时,
---->>在hello环境进行查找,此时未找到b。
------>>>>在外部环境中查找b,找到b=10,完成查找。
面试官:知道var let const吗?说一说它们的区别
我:var 会发生变量提升,let const不会,const指定后不能改变
好,那来看看下面这题
var a = 10;
(function () {
console.log(a);//undefined
a = 5;
console.log(a);//5
console.log(window.a)//10
var a = 20;//变量提升
console.log(a);//20
})()
console.log(a)//10
(function () {
console.log(b);//报错
let b = 9;
})()
考点:变量提升
在var a=20
处发生了变量提升,var a提升到**当前作用域头部**
而let b=9
不会发生变量提升,此时会报错
面试官:那么我改一下呢?去掉var呢?
var a = 10;
(function(){
console.log(a);//输出一,a=10
a = 5;
console.log(a);//输出二,a=5
console.log(window.a)//输出三,a=5
a = 20;//修改处
console.log(a);//输出四,a=20
})()
console.log(a)//输出五,a=20
考点:全局变量
去掉var之后,默认为全局变量。
- 在函数体外用var声明为全局变量。
- 在函数体内用var声明为局部变量。
- 无论函数内外,不用var/let/const声明为全局变量。
此时,函数内外共享同一个全局变量a,在输出一
处就可以获得全局变量a=10。
a = 5;
此处,改变了全局变量a的值,改变也影响了函数外。在输出三
处就可以获得全局变量a=5。输出四,输出五同理。
面试官:我再改一下,给它添上let呢?
var a = 10;
(function(){
console.log(a);//报错
a = 5;
console.log(a);
console.log(window.a)
let a = 20;//修改处
console.log(a);
})()
console.log(a)
考点:局部变量,临时死区
此时函数也会报错,这时候你可能会产生疑惑:为什么在let之前的输出语句,不能访问到全局变量a呢?
《深入理解ES6》这本书提到:虽然ECMAScript标准并没有明确提到TDZ(temporal dead zone),但人们却常用它来描述let和const不提升效果。JavaScript引擎在扫描代码发现变量声明时,要么将它提升到作用域顶部(var声明),要么将声明放到TDZ中(let和const声明)。访问TDZ中变量会触发运行时错误。只有执行过变量声明语句后,变量才会从TDZ中移出,才能正常访问。
面试官:那么,我把开头的var改成let呢?
let a = 10;//修改处
(function(){
console.log(a);//undefined
a = 5;
console.log(a);//5
console.log(window.a)//undefined
var a = 20;//和第一题相同
console.log(a);//20
})()
console.log(a)//10
这时候非常疑惑的一点就是:!!!let定义的全局变量a,竟然在window上访问不到,查了网上的资料,说是ES5和ES6变量声明的区别导致,附上原文链接
为什么会出现这种问题,就需要知道ES6与ES5变量声明方面的区别了:
ES5声明变量只有两种方式:var和function。
ES6有let、const、import、class再加上ES5的var、function共有六种声明变量的方式。
还需要了解顶层对象:浏览器环境中顶层对象是window,Node中是global对象。
ES5中,顶层对象的属性等价于全局变量。(敲黑板了啊)
ES6中,有所改变:var、function声明的全局变量,依然是顶层对象的属性;let、const、class声明的全局变量不属于顶层对象的属性,也就是说ES6开始,全局变量和顶层对象的属性开始分离、脱钩。
————————————————
版权声明:本文为CSDN博主「fang_ze_zhang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fang_ze_zhang/article/details/83419022
而通过 const
定义的变量与 let
变量类似,但不能重新赋值。这句话有一定的迷惑性。
const a=[1,2,3];
a.push(4);
a[2]=8;
console.log(a);//[1, 2, 8, 4]
这时候可以看到,对a数组添加数字和改变内容都是可以的。而当我们给a赋予一个新数组,改变a的引用时候,就会发生报错。
a=new Array()//报错
let b=[1,2,3];
a=b;//报错
a=[];
关键字 const 有一定的误导性。
它没有定义常量值。它定义了对值的常量引用。
因此,我们不能更改常量原始值,但我们可以更改常量对象的属性。
(() => {
let x, y;
try {
throw new Error();
} catch (x) {
(x = 1),(y = 2);
console.log(x);//1
}
console.log(x);//undefined
console.log(y);//2
})();
用catch接受x之后,x就是属于catch作用域的。