ECMAScriptS 6 | 新特性
Posted Hugh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ECMAScriptS 6 | 新特性相关的知识,希望对你有一定的参考价值。
新特性概览
参考文章: http://www.cnblogs.com/Wayou/p/es6_new_features.html
—————————————————————————————————————————————————————————
ES6测试引入文件
<<bootstrap.js>>
<<traceur.js>>
—————————————————————————————————————————————————————————
let命令
- let命令用来声明变量,用法类似于var,但仅仅可以使用在定义的代码块中
- 不存在变量提升
- 暂时性死区:只要块级作用域内存在let命令,它所声明的变量就"绑定"binding这个区域,不再受外部影响
- 不允许重复声明:let不允许在相同作用域内重复声明同一个变量
样例
<<test.js>>
// 在该代码块中声明let,外部不能调用,花括号之内 { let a = 100; var b = 200; } // console.log(a); // 在这里var进行了变量提升,可以在全局中使用,let不可以变量提升 console.log(b); // ******************************************************************** // 不存在变量提升 // ES5 var arr = [], arrTest = []; // 在循环中c并没有赋给数组,而是被i循环覆盖为9,所以输出的都是9 for (var i = 0; i < 10; i++) { var c = i; arr[i] = function() { return c; } } // 遍历执行 for (var i = 0; i < 10; i++) { arrTest[i] = arr[i](); } console.log(arrTest); // ES6 var arr2 = []; // var c → let d for (var i = 0; i < 10; i++) { let d = i; arr2[i] = function() { return d; } } for (var i = 0; i < 10; i++) { arrTest[i] = arr2[i](); } console.log(arrTest); // 教学视频中没有说清楚,那么是否是因为let d 只对对应的代码块起作用,出现了10个不同的d呢? // ******************************************************************** // 暂时性死区 { console.log(e); let e = 100; console.log(e); } // 在视频中得到的结果是undefined和100,而在我的测试中第一个console.log得到的是报错信息,提示未定义 // 在该代码块中let管辖着e,当未声明变量e时,打印e则会提示未定义的,暂时不可以使用的 // ******************************************************************** // 不允许重复声明 { var f = 1; let f = 100; } { let g = 100; var g = 1; } // 重复声明会产生报错信息Identifier \'f\' has already been declared
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <script type="text/javascript" src="test.js"></script> </head> <body> </body> </html>
—————————————————————————————————————————————————————————
块级作用域
let实际上为JavaScript新增了块级作用域
-
在ES5中只有全局作用域和函数作用域,没有块级作用域,容易出现以下问题:
- 内层变量可能会覆盖外层变量
- 用来计数的循环变量泄露为全局变量
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> var time = new Date(); function f1() { console.log(time); if (false) { var time = \'hello\'; // 变量重复声明了之后,内部time会覆盖已有变量的内存地址,在运行时console.log会寻找地址,此时地址还没有存入值\'hello\',所以输出为undefined // time =\'hello\'; // 如果是使用赋值语句,则正常输出time为Date(); } } f1(); // **************************************** // 循环变量泄露为全局变量的问题 for (var i = 0; i < 12; i++) {} // i跳出了循环体,循环结束后i没有被销毁 console.log(i); // 在其他函数中也会被使用 function f2() { console.log(i); } f2(); // **************************************** // Demo1: // 第二次声明nTest1会覆盖第一次声明,得到200的值 function f3() { var nTest1 = 100; if (true) { var nTest1 = 200; } console.log(nTest1); } f3(); // **************************************** // Demo2:自调用函数 // 视频演示中ES5得到的是inside,ES6得到的是outside // 测试时ES5报错,ES6正常inside function fun(){ console.log(\'i am outside\'); }; (function (){ if (false) { function fun(){ console.log("i am inside"); }; } fun(); })(); </script> <!-- ES6 --> <script type="text/traceur"> // **************************************** // Demo1: function f4() { let nTest2 = 100; if (true) { let nTest2 = 200; console.log(nTest2); } console.log(nTest2); } f4(); // **************************************** // Demo2:自调用函数 function fun(){ console.log(\'i am outside\'); } (function (){ if (false) { function fun(){ console.log("i am inside"); } } fun(); }()) </script> </head> <body> </body> </html>
—————————————————————————————————————————————————————————
const命令
const关键字声明的是常量,不可改变
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> // const命令 const Pi = 3.1415; console.log(Pi); // 赋值的话返回错误信息Assignment to constant variable. // Pi = 1; // *********************************************************** // const的块级作用域问题 if (true) { var a = 1; } // Error: a is not defined // 可见const关键字定义的也有块级作用域的问题 // console.log(a); </script> <!-- ES6 --> <script type="text/traceur"> // 暂时性死区 if (true) { console.log(b); const b = 2; } // *********************************************************** { //Error:["file:///F:/6.Code/Front-End/JavaScript/Test/index_inline_script_1.js:6:16: Duplicate declaration, c"] //不可重复声明 //var c=200; const c = 300; console.log(c); } // *********************************************************** // 通过const声明对象时,显示这个对象是只读的,该对象是冻结状态的,但仍然可以写入值 const person = {}; person.name = "hello"; console.log(person); person.name = "hugh"; console.log(person); console.log(Object.isSealed()); console.log(Object.isFrozen()); // 如果以冻结方法定义的对象,则是无法写入的 const person2 = Object.freeze({}); person2.name = "hugh"; person2.age = 21; console.log(person2); console.log(person2.name); console.log(person2.age); console.log(Object.isFrozen()); // 正确的const冻结对象的方法 const person3 = Object.freeze({ name: \'hhh\', age: 1 }); console.log(person3); console.log(Object.isFrozen()); // *********************************************************** // const声明的数组可以数组操作,但不可以整体赋值,视为重新定义 const arr1 = []; arr1.push(1); arr1.push(2); console.log(arr1); arr1.pop(); console.log(arr1); console.log(arr1.length); //Error:["file:///F:/6.Code/Front-End/JavaScript/Test/index_inline_script_1.js:30:5: arr1 is read-only"] //arr1 = [\'a\',\'b\',\'c\']; //console.log(arr1); // *********************************************************** // 彻底冻结对象,请对比参考JavaScript中的递归冻结函数方法 var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach((key,value)=>{ if(typeof obj[key]===\'object\'{ constantize(obj[key]); }); }); }; </script> </head> <body> </body> </html>
—————————————————————————————————————————————————————————
跨模块常量
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> </script> <!-- ES6 --> <script type="text/traceur"> // module.js, 在这里将变量输出 export const cVariantName = "111"; export const iVariantName = 3; export const fVariantName = 4.111; // use.js, 在这里将变量全部引入 import * as variant from \'./module.js\'; console.log(variant.cVariantName); // 输出"111"; console.log(variant.iVariantName); // 输出3; console.log(variant.fVariantName); // 输出4.111; // otherUse.js, 引入部分变量 import {iVariantName,fVariantName} as variant from \'./module.js\'; console.log(variant.iVariantName); // 输出3; console.log(variant.fVariantName); // 输出4.111; // onlyUse.js, 只引入一个变量 import iVariantName as variant from \'./module.js\'; console.log(variant.iVariantName); // 输出3; </script> </head> <body> </body> </html>
—————————————————————————————————————————————————————————
全局对象属性
- 全局变量是最顶层的对象
- 浏览器环境指的是window对象
- Node.js指的是global对象
-
在JavaScript中所有全局变量都是全局对象的属性
p.s.在Node中这一条只对REPL环境使用,模块环境必须显式声明成global属性
-
ES6规定:
- var、function命令声明的全局变量,属于全局对象的属性
- let、const、class命令声明的全局变量,不属于全局对象的属性
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> </script> <!-- ES6 --> <script type="text/traceur"> var varName = "varValue"; // 浏览器环境 console.log(window.varName); // 输出varValue // Node环境 // console.log(global.varName); // 通用环境 console.log(this.varName); // 输出varValue let letName = "letValue"; console.log(window.letName); // 兼容模式:letValue, 严格模式:undefined console.log(this.letName); // 兼容模式:letValue, 严格模式:undefined </script> </head> <body> </body> </html>
—————————————————————————————————————————————————————————
解构赋值 Dustructuring
- 解构:ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值
- 不完全解构:等号左边的模式只匹配一部分等号右边的数组
- 指定默认值:ES6内部使用严格相等运算符===来判断一个位置是否有值,如果数组成员不严格等于undefined默认值不会生效
- let和const命令:只要某种数据结构具有iterator(迭代器)接口,都可以采用数组形式解构赋值
数组解构赋值
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> // Set对象 var s = new Set(); s.add("Thomas Jefferson"); s.add(1776); s.add("founding father"); s.forEach(function (item) { console.log(item.toString() + ", "); }); </script> <!-- ES6 --> <script type="text/traceur"> //数组解构 // 通过这种形式批量结构赋值 var [a,b,c] = [1,2,3]; console.log(a); console.log(b); console.log(c); // 对应位置数组解构 let [foo,[[bar],,base]] = [1,[[2],3,4]]; console.log(foo); console.log(bar); console.log(base); // ...tail 作为数组 let [head, ...tail] = [0,1,2,3,4,5,6,7,8,9]; console.log(head); console.log(tail); // 不完全解构 let [x,y] = [1,2,3]; console.log(x); console.log(y); let [x1,y1] = [100]; console.log(x1); // 100 console.log(y1); // undefined // 指定默认值 var [temp = \'string\'] = []; // 在这里temp的默认值为string,如果不给值的话为默认string console.log(temp); var [temp1 = \'string1\'] = [\'string2\']; console.log(temp1); var [temp2 = \'aaa\',temp3] = [\'bbb\']; console.log(temp2); // \'bbb\' console.log(temp3); // undefined, 解构时完全对应给定值,bbb赋值给temp2,没有值给tmep3,参见不完全解构 var [temp4 = \'aaa\'] = [undefined]; console.log(temp4); // \'aaa\' 因为给定的值是未定义的 // 非遍历结构 - 报错 // var [temp5] = 1; // traceur.js:31513 TypeError: 1[Symbol.iterator] is not a function // 迭代器 let [a1,b1,c1] = new Set([\'aaa\',\'bbb\',\'ccc\']); console.log(a1); console.log(b1); console.log(c1); function* fibs(){ let a2 = 0; let b2 = 1; while(true){ yield a2; [a2,b2] = [b2,a2+b2]; } } var [first,second,third,fourth,fifth,sixth] = fibs(); console.log(first); console.log(second); console.log(third); console.log(fourth); console.log(fifth); console.log(sixth); </script> </head> <body> </body> </html>
对象解构赋值
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> </script> <!-- ES6 --> <script type="text/traceur"> // 对象解构赋值 var {name,age,id} = {id:\'0001\',name:\'hugh\',age:20}; console.log(name); console.log(age); console.log(id); // 可以不按顺序 // 变量名和属性名不一致 var {change_name,age,id} = {id:\'0001\',name:\'hugh\',age:20}; console.log(change_name); // undefined // 将变量名和属性名对应起来 var {name:person_name,age:person_age,id:person_id} = {id:\'0001\',name:\'hugh\',age:20}; console.log(person_id); console.log(person_name); console.log(person_age); // 重构对象 let object = {first:1,second:2}; let {first:first_num , second:second_num} = object; // 结构对象 console.log(first_num); console.log(second_num); // 指定默认值 // 结构默认值的条件为 === undefined var {x = 3,y = 5} = {}; console.log(x); console.log(y); var {massage:msg = "yes!"} = {}; console.log(msg); // 已声明变量的解构赋值 var test; // {test} = {test:1}; ({test} = {test:1}); console.log(test); // 现有对象的方法 let {sin,cos,tan} = Math; console.log(sin(Math.PI/6)); </script> </head> <body> </body> </html>
字符串解构赋值
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> </script> <!-- ES6 --> <script type="text/traceur"> // 字符串的解构赋值 const [a,b,c,d,e,f] = "hello!"; // 逐位对应解构赋值 console.log(a); console.log(b); console.log(c); console.log(d); console.log(e); console.log(f); // 字符串的length属性解构 const {length : len} = "hhh"; // 通过对象取属性 console.log(len); const {length} = "1111"; console.log(length); </script> </head> <body> </body> </html>
函数参数解构赋值
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> </script> <!-- ES6 --> <script type="text/traceur"> // 函数参数的解构赋值 function sum ([x, y]){ return x + y; } console.log(sum([1, 2])); // 解构赋值传入一个数组,数组变量解构赋值 // 函数参数解构默认值 // 在这里0和1并不是赋值操作,而是声明变量,所以不能使用以下这种方法:function fun({x, y} = {x : 0, y : 1}),将{x, y} = {x : 0, y : 1}作为函数内部的语句来操作,如果传入的是fun(),return 0 0,传入fun({x:100}),return 100 undefined function fun({x = 0, y = 1} = {}){ return [x, y]; } console.log(fun({x : 100})); // y返回的是默认值 console.log(fun()); // x,y均返回默认值 </script> </head> <body> </body> </html>
解构赋值用途
- 交换变量的值
- 从函数返回多个值
- 函数参数的定义
- 提取Json数据
- 函数参数的默认值
- 遍历Map结构
- 输入模块的指定方法
<<index.html>>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="traceur.js"></script> <script src="bootstrap.js"></script> <!-- ES5 --> <script type="text/javascript"> </script> <!-- ES6 --> <script type="text/traceur"> // 交换变量的值 // ES5中通过temp变量, temp = a; a = b; b = temp; var x = 100, y = 200; [x, y] = [y, x]; console.log([x, y]); // ************************************************************************************ // 从函数返回多个值,优势:不需要使用fun.id之类的操作,直接取值到变量 // 返回一个数组 function fun(){ return [1, 2, 3]; } var [x, y, z] = fun(); console.log(x); console.log(y); console.log(z); // 返回一个对象 function fun1(){ return { id : "0001", name : "hugh", age : 20 }; } var {id : person_id, name : person_name, age : person_age} = fun1(); console.log(person_id); console.log(person_name); console.log(person_age); // ************************************************************************************ // 函数参数的定义 // 参数有次序 function fun2([x, y, z]){ console.log(x); console.log(y); console.log(z); } fun2([1, 2, 3]); // 参数无次序 // 使用到的是对象的解构赋值,用于接口的交互,如果是使用AJAX交互时,没必要在传值前打包,直接封装成Json对象,打包好传过来,可以使整个模块更清晰 function fun3({id, name, age}){ console.log(id); console.log(name); console.log(age); } fun3({name : "dong", id : \'0002\', age : 30}); // ************************************************************************************ // 提取Json数据 var jsonData = { id : "0003", name : "dong", age : 21, score : { Chinese : 99, Math : 98, English : 97 } }; // 通常情况下提取Json数据 console.log(jsonData); console.log("id:"+jsonData.id); console.log("name:"+jsonData.name); console.log("chinese score:"+jsonData.score.Chinese); console.log("english score:"+jsonData.score.English); // ES6下解构后 let {id:number, xingming, age, score:chengji} = jsonData; console.log("ES6:"+number); console.log("ES6:"+xingming); console.log("ES6:"+age); console.log("ES6:"+chengji.Chinese); // ************************************************************************************ // 函数参数的默认值 // 内建的ajax // jQuery.ajax({ // url :\'/path/to/file\', // type :\'POST\', // dataType : \'xml/html/script/json/jsonp\', // data : {param1 : \'value1\'}, // complate : function(xhr, textStatus){ // // called when complete // }, // success : function(data, textStatus, xhr){ // // called when success // }, // error : function(xhr, textStatus, errorThrown){ // // called when there is an error // } // }); // 解构赋值方式,通过参数的默认值来进行ajax运算,突出函数的传值 // 避免了在函数体内部再写 var foo = config.foo || \'default foo\'; 这样的语句 // jQuery.ajax = function (url, { // async = true, // beforeSend = function (){}, // cache = true, // complete = function (){}, // crossDomain = false, // global = true, // // ...more config // }){ // // ...do stuff // }; // ************************************************************************************ // 遍历Map解构 var map = new Map(); map.set("id","0007"); // 创建键值对 map.set("name","hehe"); console.log(map); // map结构是object for(let [key, value] of map){ // 遍历获取键值对 console.log(key + " is " + value); } for (let[key] of map){ // 遍历获取键名 console.log(key); } for (let [, value] of map){ // 遍历获取键值, p.s. [ , value] !!! console.log(value); } // ************************************************************************************ // 输入模块的指定方法 const { SourceMapConsumer , SourceNode } = require("source-map"); </script> </head> <body> </body> </html>
以上是关于ECMAScriptS 6 | 新特性的主要内容,如果未能解决你的问题,请参考以下文章