参考阮一峰的ECMAScript 6 入门
前言
ECMAScript 6.0(ES6)是JavaScript语言的下一代标准,在 2015 年 6 月正式发布。它的目标是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
- ECMAScript与Javascript的关系
ECMAScript是Javascript的规范,Javascript是ECMAScript的实现。 - ES6与ECMAScript 2015的关系
ES6是ECMA的为JavaScript制定的第6个版本的标准,标准委员会最终决定,标准在每年的 6 月份正式发布一次,作为当年的正式版本。ECMAscript 2015 是在2015年6月份发布的ES6的第一个版本,ECMAscript 2016 是ES6的第二个版本。所以ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。
一、let与const
ES6之前的作用域有全局作用域、函数作用域、eval作用域,ES6新增了块级作用域,用一对花括号来使用。
-
ES6新增了let命令,用法类似于var,但是let所声明的变量只作用在当前块级作用域内
{ var b = 2; let a = 1; } console.log(b); // 2 console.log(a); // 报错
可知,let声明的变量只在它所在的代码块有效。
-
for循环很适合let命令
var a = []; for(var i=0;i<10;i++){ a[i] = function(){ console.log(i); }; } a[6](); // 10
变量i是var命令声明的,在全局范围内都有效,数组a的函数内部的
console.log(i)
,使用的i是指向全局的i,是同一个i,这样在运行a[6]()
输出的是循环之后的最后一轮值10。var a = []; for(let i=0;i<10;i++){ a[i] = function(){ console.log(i); } } a[6](); // 6
使用let声明的变量只在块级作用域即本轮循环有效,每一次循环i都是一个重新声明的变量。阮老师提到,JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
小题目:生成十个按钮,每个按钮点击的时候弹出1-10
闭包实现:for(var i=1;i<=10;i++){ (function(i){ var btn = document.createElement('button'); btn.innerText = i; btn.onclick = function(){ alert(i); }; document.body.appendChild(btn); })(i); }
let命令实现:
for(let i=1;i<=10;i++){ var btn = document.createElement('button'); btn.innerText = i; btn.onclick = function(){ alert(i); }; document.body.appendChild(btn); }
这里出现了一个问题:报错 Uncaught TypeError: Cannot read property ‘appendChild’ of null ,原因是无法获取插入的节点。.js在head里面引入,在获取节点时,节点还没有加载,解决办法:.js改为在body里面引入
-
let或者const不允许在相同作用域内重复声明一个变量
let a = 10; let a = 12; // 'a' has already been declared
-
let不存在变量提升
console.log(a); // 输出undefined var a = 1;
var命令会发生变量提升现象,在变量声明之前使用,值为undefined。为了纠正这种现象,规定let声明的变量一定要在声明后使用,否则报错。
console.log(a); // 报错ReferenceError let a = 1;
-
暂时性死区
var a = 1; { a = 2; // 报错ReferenceError let a = 3; }
虽然存在全局变量a,但是在块级作用域内又声明了局部变量a,块级作用域对let(const)声明的变量从一开始就形成了封闭作用域,即使外部存在变量a也取不到它的值,同时又不能变量提升,就报错了。
-
const声明一个常量,常量必须在声明的时候赋值,const与let有类似的特性。然而,常量为引用类型的时候,虽然不可改变变量的地址,但是可以修改该引用类型的值。
const arr = [1]; arr.push(2); console.log(arr);// 1,2 arr = []; // 让arr指向一个新的数组,报错
防止常量为引用类型的时候能被修改:
const obj = { a: 1, b: 2 }; console.log(obj);// {a:1,b:2} Object.freeze(obj); obj.a = 3; console.log(obj);// {a:1,b:2}
Object.freeze()方法接收一个参数,如果此参数是一个对象,则此方法把这个对象冻结,被冻结的对象不能修改、添加、删除其属性或者属性值
-
ES6以前防止常量为引用类型的时候能被修改的做法
var person = { name: 'Dell' }; Object.defineProperty(person,'name',{ // value: 'Dell', writable: false }); Object.seal(person);
Object.defineProperty() 方法会在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。它接收三个参数:要在其上定义属性的对象,要定义或修改的属性的名称,将被定义或修改的属性描述符。
使用Object.seal()方法可以封闭一个对象,阻止添加新属性,并将所有现有属性标记为不可配置,已有属性的值如果原来是可写的,就可以改变。
封装方法:(具体也不是很理解)Object.defineProperty(Object,'freezePolyfill',{ value: function(obj){ for(var i in obj){//遍历属性和方法 if(obj.hasOwnProperty(i)){ // 修改遍历到的属性描述 Object.defineProperty(obj,i,{ writable: false }); } // 使用Object.seal()方法封闭对象 Object.seal(obj); } } }); const person = { name: 'Dell' }; Object.freezePolyfill(person);
使用hasOwnProperty()方法,在给定属性存在于对象实例中时返回true,保证属性是来自实例而不是原型。