前端必考系列(一):var let const

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作用域的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值