ECMAScript 6 | 新特性

时间:2023-03-09 04:37:54
ECMAScript 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); //
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>

ECMAScript 6 | 新特性