let的作用是声明变量,和var差不多。

let是ES6提出的,在了解let之前,最好先熟悉var的原理。

JavaScript有一个机制叫“预解析”,也可以叫“提升(Hoisting)机制”。很多刚接触JavaScript的人都会被这个机制弄混。比如:

// var 的情况
console.log(a); // 输出undefined
var a = 2;

在预编译阶段,JavaScript引擎会将上面的a函数修改成下面的写法:

var a;  //声明且初始化为undefined
console.log(a);
a=2;

我们把上面的 var 变成 let ;

// let 的情况
console.log(a); // 报错ReferenceError
let a = 2;
//相当于在第一行先声明bar但没有初始化,直到赋值时才初始化

由此我们得出:

变量提升现象:浏览器在运行代码之前会进行预解析,首先解析函数声明,定义变量,解析完之后再对函数、变量进行运行、赋值等。

-不论var声明的变量处于当前作用域的第几行,都会提升到作用域的头部。 
-var 声明的变量会被提升到作用域的顶部并初始化为undefined,而let声明的变量在作用域的顶部未被初始化

在ES6中对块级作用域做了进一步强化,从而使变量在生命周期内能被更好的控制。

块级声明用于声明在指定块的作用域之外无法访问的变量。
“块级作用域”也可以称为“词法作用域”。

  • 块级作用域存在于
  • 函数内部

块中(字符 { 和 } 之间的区域)
比如 if 和 for 在ES6中也被定义成一个块级。

let声明的用法与var相同,用let代替var来声明变量,就可以把变量的作用域限制在当前代码块中。

而且let声明不会被提升(在预解析的过程中,不会把声明变量放在所有代码的之前),因此开发者通常会将let声明语句放在封闭代码块的顶部,以便整个代码块都可以访问。

var a = 123;
if (true) {
a = 456; // ReferenceError
let a;
}
console.log(a); //输出值为123,全局 a 与局部 a 不影响

上面这小段代码,先声明全局变量 a = 123; 按照我们以往的思维,如果if 判断语句中没有 let a;则最后会输出 456;但是if 判断语句作为块作用域,内部在未声明变量的时候直接给 a 赋值为 456;因此会报错。

由此可见,用let来声明变量比var更严紧。

let的另一个特性是禁止在同一个作用域下重复声明。

var a = 10;
let a = 20;
// 抛出语法错误
// Uncaught SyntaxError: Identifier 'a' has already been declared
// 很直接的告诉开发者变量a已经被定义过了。

不管之前用var还是let声明,只要后面再重复声明同一个变量,都会报错。

let a = 10;
var a = 20;
// 也会报错

上面是的报错是因为在同一个作用域下,用let重复声明。

熟悉JavaScript的开发者都知道,var是可以重复声明的,而后面声明的操作会覆盖前面的声明。

如果不在同一个作用域下,是可以用let来重复声明相同名的变量。

let a = 30;
if (true) {
let a = 40;
console.log(a);
// 输出40
}
console.log(a); // 输出30

同时 let 还有一个功能是防止变量泄露,

用var声明

for (var i=0; i<10; i++) {}
console.log(i);
// 输出 10

用let声明

for (let i=0; i<10; i++) {}
console.log(i);
// 抛出错误:Uncaught ReferenceError: i is not defined

最后我们总结出:let

  • 在同一个作用域下,不可以被重复声明
  • 可以重新赋值
  • 可以防止变量泄露

接下来我们看一个 var 和 let 的实战练习:

我们先看一个正常的for循环,普通函数里面有一个for循环,for循环结束后最终返回结果数组

function foo(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = i;
}
return arr;
}
console.log(foo())
//输出结果为 [0,1,2,3,4]

有时我们需要在for循环里面添加一个匿名函数来实现更多功能,看下面代码

//循环里面包含闭包函数
function foo(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
console.log(foo()); //执行5次匿名函数本身 --> [ [Function], [Function], [Function], [Function], [Function] ]
console.log(foo()[1]);     //执行第2个匿名函数本身 --> [Function]
console.log(foo().length); //最终返回的是一个数组,数组的长度为5 --> 5
console.log(foo()[0]()); //数组中的第一个数返回的是5 --> 5

上面这段代码就形成了一个闭包:

闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。

在for循环里面的匿名函数执行 return i 语句的时候,由于匿名函数里面没有i这个变量,所以这个i他要从父级函数中寻找i,而父级函数中的i在for循环中,当找到这个i的时候,是for循环完毕的i,也就是5,所以这个 foo 得到的是一个数组[5,5,5,5,5]。

那我们怎么才能输出 [1,2,3,4,5],在上述中我们说了在闭包内部函数调用 i 时没有找到转而向上层父级去找,那我们在调用内部函数时将值传给内部函数不就可以不需要去父级找了,在 JavaScript 函数中有有匿名函数的自我执行,即 在函数体外面加一对圆括号,形成一个表达式,在圆括号后面再加一个圆括号,里面可传入参数。

(function(){
console.log(123);
})();
//输出 123
var a = 123;
(function(b){
console.log(b);
})(a);
//输出 123

根据上面的执行结果我们可以将目标函数改造如下:

function foo(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = (function(num){
return num;
})(i);
}
return arr;
}
console.log(foo()); // [ 0, 1, 2, 3, 4 ]
console.log(foo()[1]); // 1
console.log(foo().length); // 5
console.log(foo()[0]); // 0

这样在每次调用 foo 的时候内部的匿名函数都会自我执行,并且将 i 传入匿名函数进行返回。

我们现在来看下面的代码:

function foo(){
var arr = [];
for(var i=0;i<5;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
console.log(foo()[0]()); // 5
function foo(){
var arr = [];
for(let i=0;i<5;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
console.log(foo()[0]()); // 0

在上面的代码中,我们分别用 var 和 let 来声明 i 的值,获得的结果却是不一样的,当为 var 时我们在上面的时候已经解释过了,但是为什么当 var 换为 let 之后会变呢,只是由于使用 let 声明块级变量,这样每次循环时就会在自己的作用域内找 i 的变量,而不是去全局找 i 的变量。

我们再来看一道面试时会经常问到的题目:

for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
},1000);
} // 5,5,5,5,5 for(let i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
},1000);
} // 0,1,2,3,4

在setTimeout的时候,匿名函数function(){console.log(i);}会被声明创建,当匿名函数执行的时候,会查找当前运行环境的 i 的值。
var声明的 i ,运行环境的 i 的值为5,但是let声明的 i,运行环境中 i 的值是每一个循环创建匿名函数时候的 i。
所以得到了0-4的值。

let替换var,可以很好的解决闭包的问题。

javascript ES6 新特性之 let的更多相关文章

  1. javascript ES6 新特性之 扩展运算符 三个点 ...

    对于 ES6 新特性中的 ... 可以简单的理解为下面一句话就可以了: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中. 作用类似于 Object.assign() ...

  2. JavaScript ES6 新特性详解

    JavaScript ES6 带来了新的语法和新的强大功能,使您的代码更现代,更易读 const ,  let and var 的区别: const , let 是 ES6 中用于声明变量的新关键字. ...

  3. JavaScript ES6新特性介绍

    介绍 ES6:ECMScript6 首先,一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? ECMAScript是一个国际通过的标准化脚本语言: JavaScript ...

  4. javascript ES6 新特性之 Promise,ES7 async / await

    es6 一经推出,Promise 就一直被大家所关注.那么,为什么 Promise 会被大家这样关注呢?答案很简单,Promise 优化了回调函数的用法,让原本需要纵向一层一层嵌套的回调函数实现了横向 ...

  5. javascript ES6 新特性之 class

    在之前的文章中我们讲过原型,原型链和原型链继承的文章,在 ES6 中为我们提供了更为方便的 class,我们先来看一下下面的例子: function Person(name) { //构造函数里面的方 ...

  6. javascript ES6 新特性之 解构

    解构的作用是可以快速取得数组或对象当中的元素或属性,而无需使用arr[x]或者obj[key]等传统方式进行赋值 var arr = [1, 2, 3]; //传统方式 var a = arr[0], ...

  7. ES6新特性概览

    本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...

  8. Atitit js版本es5 es6新特性

    Atitit js版本es5 es6新特性 Es5( es5 其实就是adobe action script的标准化)1 es6新特性1 Es5( es5 其实就是adobe action scrip ...

  9. ES6新特性:Proxy代理器

    ES6新特性:Proxy: 要使用的话, 直接在浏览器中执行即可, node和babel目前还没有Proxy的polyfill;,要使用的话,直接在浏览器中运行就好了, 浏览器的兼容性为:chrome ...

随机推荐

  1. SQL反模式学习笔记4 建立主键规范【需要ID】

    目标:建立主键规范 反模式:每个数据库中的表都需要一个伪主键Id 在表中,需要引入一个对于表的域模型无意义的新列来存储一个伪值,这一列被用作这张表的主键, 从而通过它来确定表中的一条记录,即便其他的列 ...

  2. Android进阶:五、RxJava2源码解析 2

    上一篇文章Android进阶:四.RxJava2 源码解析 1里我们讲到Rxjava2 从创建一个事件到事件被观察的过程原理,这篇文章我们讲Rxjava2中链式调用的原理.本文不讲用法,仍然需要读者熟 ...

  3. 向mysql中导入向导时如表xlsx

    如果出现这种问题那么是因为没有打开这个文件,如果想导入这个文件需要到开这个文件,然后再导入

  4. [SQLite]SQLite URI配置

    脱离新手文档使用SQLAlchemy配置sqlite,才发现sqlite的URI指定有点特别. https://github.com/mitsuhiko/flask-sqlalchemy/issues ...

  5. 2018-2019-2 网络对抗技术 20162329 Exp3 免杀原理与实践

    目录 免杀原理与实践 一.基础问题回答 1.杀软是如何检测出恶意代码的? 2.免杀是做什么? 3.免杀的基本方法有哪些? 二.实验内容 1. 正确使用msf编码器 2. msfvenom生成如jar之 ...

  6. Linux服务器限制ssh登录,查看登录日志

    网络上的服务器很容易受到攻击,最惨的就是被人登录并拿到root权限.有几个简单的防御措施: 1. 修改ssh服务的默认端口 ssh服务的默认端口是22,一般的恶意用户也往往扫描或尝试连接22端口.所以 ...

  7. NPOI操作创建Excel

    一.下载NPOI类库 使用Nuget在线搜索NPOI,下载安装 二.代码开撸 var workBook = new HSSFWorkbook(); #region 设置样式 IFont font = ...

  8. 201771010126 王燕《面向对象程序设计(Java)》第九周学习总结

    实验九 异常.断言与日志 实验时间 2018-10-25 1.实验目的与要求 (1) 掌握java异常处理技术: 异常积极处理方法:使用try子句捕获异常 异常小计处理方法:抛出throw异常类 (2 ...

  9. vue font-icon 图标

    1.vue 游览器左上角小图标 把.ico文件放在根目录下的static文件夹下,然后link标签引入 <link rel="shortcut icon" href=&quo ...

  10. Vue(MVVM)、React(MVVM)、Angular(MVC)对比

    前言 昨天阿里内推电面一面,面试官了解到项目中用过Vue,就问为什么前端框架使用Vue而不适用其他的框架,当时就懵了.因为只用过Vue,不了解其他两个框架,今天就赶紧去了解一下他们之间的区别.大家发现 ...