浅谈let和var的区别javascript
首先来看一看MDN对于var的解释:java
1: 变量声明,不管发生在何处,都在执行任何代码以前进行处理。(变量提高)
2: 声明变量的做用域限制在其声明位置的上下文中,而非声明变量老是全局的
3: 若是你从新声明一个JavaScript变量,它将不会丢失其值
... ...
复制代码
我只摘抄了一部分特性,可是这一部分特性就已经可以让咱们明白var与let的区别了,在谈var与let的区别以前,咱们先简单看一下var的这些特性。面试
首先来看一道面试题:请问下面的代码会打印出什么样的结果?浏览器
var a = 99;
f();
console.log(a);
function f(){
console.log(a);
var a = 10;
console.log(a);
}
复制代码
这道题的考点是做用域及变量提高,首先咱们先将全部的变量进行提高:bash
var a;
function f(){
var a;
console.log(a);
a = 10;
console.log(a);
}
a = 99;
f();
console.log(a);
复制代码
结果为:函数
undefined
10
99
复制代码
咱们首先将全局变量a与函数f以及函数内部的局部变量a进行了变量提高,执行函数f时,因为函数f内部的第一个console.log打印的a在函数f这个做用域已经声明过了,但还并未赋值因此首先会打印出来undefined;在函数f内部的第二个console.log打印的结果则是赋值之后的a,且这个a仍然是函数这个做用域中的a,因此打印出来的结果为 10;在 代码最后的console.log中的a则是当前做用域即全局变量的a,这个a声明过且赋值为99,因此最后一个结果是99。若是你彻底理解了这道题目,那么相信你天然会了解var的变量提高以及做用域。学习
咱们首先来看一个MDN的例子:ui
function x() {
y = 1; // 在严格模式(strict mode)下会抛出 ReferenceError 异常
var z = 2;
}
x();
console.log(y); // 打印出结果1
console.log(z); // ReferenceError: z 未在 x 外部声明
复制代码
在函数x的内部,咱们未对y变量进行任何声明而是直接对其进行了赋值,这样作,咱们一不当心就声明了一个全局变量,咱们的本意是在函数x的内部声明一个局部变量,可是在函数x的外部使用console.log也是能打印出来y的值,其实上面的代码至关于:spa
var y = 1;
function x(){
var z;
y = 1;
z = 2;
}
x();
console.log(y);
console.log(z);
复制代码
3: 若是你从新声明一个JavaScript变量,它将不会丢失其值
复制代码
关于第三点解释,咱们用本身的话来翻译一下翻译
var 能够重复对一个变量进行声明,可是不管怎么声明它都是那个当前做用域的那个变量
复制代码
听起来有些难懂,咱们不妨看一下代码:
HTML中有:
<div id=parent></div>
=====================>我是分割线<=====================
在script标签中:
var parent = document.getElementById('parent');
试问:
console.log(parent);
console.log(window.parent);
这两个结果在浏览器的控制台中分别打印出什么?
复制代码
答案为:
打印出的结果均为:id为parent的div元素
复制代码
实际上,咱们声明了一个已经声明过的全局属性,window.parent全局属性为:若是有父窗口,即返回父窗口;若是没有则返回当前窗口。在本例中,咱们对parent再次声明,而且咱们对声明的变量进行了赋值,这也至关于对原有的全局属性parent进行了覆盖。也就是说:
var a = 1;
var a = 2;
var a = 3;
var a = 4;
console.log(a);
复制代码
这段代码没有语法错误,不过它至关于:
var a;
a = 1;
a = 2;
a = 3;
a = 4;
console.log(a);
复制代码
咱们仍是先看一下MDN对于let的解释:
1: let的做用域是块,而var的做用域是函数
2: 一个做用域中用let重复定义一个变量将引发 TypeError
... ...
复制代码
let的解释,我也只是摘抄了一部分,若是你想更加详细地了解,能够搜索MDN~~~
let
声明的变量只在其声明的块或子块中可用,这一点,与var
类似。两者之间最主要的区别在于var
声明的变量的做用域是整个封闭函数。 示例以下:
{
var dobby = 666;
}
console.log(dobby); // 打印出666
{
let kim = 666;
}
console.log(kim); // 报错ReferenceError,kim is not defined
复制代码
MDN上的例子更加生动形象,我就借来用一下了 : -)
function varTest() {
var x = 1;
if (true) {
var x = 2; // 一样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // 不一样的变量
console.log(x); // 2
}
console.log(x); // 1
}
复制代码
从上面的例子能够看出来,let的做用域,咱们回过头再来看一看这个问题:
HTML中有:
<div id=parent></div>
=====================>我是分割线<=====================
在script标签中:
var parent = document.getElementById('parent');
复制代码
若是咱们非要使用parent这样一个变量(固然不推荐,由于全局属性可耻!),让它表示id为parent的div元素,而且咱们但愿全局属性window.parent不受影响,咱们就可使用let~
{
let parent = document.getElementById('parent');
console.log(parent);
}
console.log(window.parent);
// 除此以外,也可使用当即执行函数,由于再次强调var的做用域是函数
(function(){
var parent = document.getElementById('parent');
console.log(parent);
}).call();
复制代码
与var不一样,let没有变量提高,没有变量提高,没有变量提高。仍是借用一下MDN的好例子 :-)
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined
var bar = 1;
let foo = 2;
}
复制代码
咱们来看一下阮一峰的解释:
ES6 明确规定,若是区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就造成了封闭做用域。凡是在声明以前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量以前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)
复制代码
其实简单一句话归纳就是:let不存在变量的提高,这样是和var的区别之一
直接上示例:
{
let dobby = 1;
let dobby = 2;// TypeError thrown.
}
复制代码
没什么好解释的 :-)
for (var i = 0; i <10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
复制代码
与
for (let i = 0; i <10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
复制代码
两个代码打印的结果是什么?
这是一道已经被玩烂的面试题,由于我尚未学习到JS的事件循环机制,若是真的让我说出个之因此然来,个人确没法进行详细的说明,可是在后续,我会继续将这道题单独写一篇博客,好好深刻研究其中的机制与奥秘~
var 循环的结果为:10个10
let 循环的结果为:0,1,2,3,4,5,6,7,8,9
复制代码
其实抛开上面的诸多知识点,咱们也能够简单先进行一波分析,对于var循环来说,咱们能够先这样写:
var i;
for (i = 0; i <10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
复制代码
因为var的变量提高机制,咱们实际上至关于声明了一个全局变量,再回顾下这个代码:
var a;
a = 1;
a = 2;
a = 3;
a = 4;
console.log(a);
复制代码
在var循环中,其实咱们打印的i 就是全局的惟一的那个变量,因此会打印出10个10;而对于let的循环则不一样,咱们回顾一下let的做用域,当i在for循环这个块使用时,则不会收到外部的一些影响。
var 和 let 究竟有哪些不一样呢?
若有错误,还请指出,不甚感激~