ES6新特性

ES6 ECMAScript 6 是 ECMA 于 2015.06 发布的版本,以前总感觉 JS 是一个”临时”发明的语言,现在终于变得”正规”了许多,值得记录一下变更点。

let

  • 对应以前的 var 关键字,可以定义一个局部变量,该变量只在自己的作用域(大括号内)起作用。
  • 在 ES6 之前,我们都是用 var 来声明变量,而且 JS 只有函数作用域和全局作用域,没有块级作用域,所以{}限定不了 var 声明变量的访问范围
{
  var i = 9;
}
console.log(i); // 9
{
  let i = 9; // i变量只在 花括号内有效!!!
}
console.log(i); // Uncaught ReferenceError: i is not defined
  • 与 var 相比,let 不存在”声明提升”处理,let 之前的区域叫”暂时性死区 temporal dead zone,简称 TDZ”,表示该变量完全不可用
  • let 在一个块(大括号)内不允许重复声明
var a = 99; // 全局变量a
f(); // f是函数,虽然定义在调用的后面,但是函数声明会提升到作用域的顶部。
console.log(a); // a=>99,  此时是全局变量的a
function f() {
  console.log(a); // 当前的a变量是下面变量a声明提升后,默认值undefined
  var a = 10;
  console.log(a); // a => 10
}
/* 输出结果:
undefined
10
99
*/
  • 由于 let 只在块内起作用,很适合在 for 循环使用
for (var i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, 0);
} // 输出: 10 次 10

for (let i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, 0);
} // 输出: 0 1 2 ... 9

const

  • const 是新增的关键字,与 C++中的 const 一样,表示常量
  • 与 let 类似,在块内起作用
  • const 用来声明一个常量,声明时必须赋值,且一旦声明就不能改变
  • const 只要求被 const 修饰的变量本身是常量,如果这个变量本身是引用,则二级引用(成员变量)不受限制

symbol

在以前 Javascript 6 个基础类型object,string,boolean,number,null,undefined基础上增加了 symbol,表示独一无二的值,与 NaN 有点类似。

var sy = Symbol('test');
var sy1 = Symbol('test');
console.log(typeof sy);//'symbol'
sy == sy1;//false
var sy2 = new Symbol('test');//error : Symbol is not a constructor

Destructuring Assignment(解构赋值)

这是一个很有意思的语法糖,可以根据模式匹配,自动将=前的多个元素赋值

let [a,b,c] = [1,2,3]; // 1,2,3
**************************
let [a,b,c] = [1,,3]; // 1,undefined,3
**************************
let [a,,b] = [1,2,3]; // 1,3
**************************
//...是剩余运算符,表示赋值运算符右边除第一个值外剩余的都赋值给b
let [a,..b] = [1,2,3]; // 1,[2,3]
**************************
// 可以解构字符串为字符数组
let arr = 'hello';
let [a,b,c,d,e] = arr; // 'h','e','l','l','o'
**************************
// 可以解构对象,将属性映射赋值到变量
let obj = {name:'ren',age:12,sex:'male'};
let {name,age,sex} = obj;
console.log(name,age,sex); // 'ren' 12 'male'
let {name:myName,age:myAge,sex:mySex} = obj; // 自定义变量名
console.log(myName,myAge,mySex); // 'ren' 12 'male'

Set Map

ES6 增加了两个基于 hash 的常用容器

literal(字面量) 创建对象

可以直接用变量、函数拼接出一个对象

let name = 'ren';
let age = 12;
let myself = {
    name,
    age,
    say(){
        console.log(this.name);
    }
};
console.log(myself); // {name:'ren',age:12,say:fn}
myself.say(); // 'ren'

...拷贝属性运算符

  • 提供了一个简易的拷贝对象属性的方法...
  • 这个操作仍然只能算是”浅拷贝”,因为只有第一层属性拷贝了
  • 可以利用JSON.stringify() JSON.parse()进行深拷贝
let obj = {name:'ren',age:12};
let person = {...obj};
console.log(person); // {name:'ren',age:12}
obj == person; // false
let another = {sex:'male'};
let someone = {...person,...another}; // 合并对象
console.log(someone); // {name:'ren',age:12,sex:'male'}

Object.assign

浅拷贝属性到目标对象

let source = {a:{ b: 1},b: 2};
let target = {c: 3};
Object.assign(target, source);
console.log(target);//{c: 3, a: {b:1}, b: 2}
source.a.b = 2;
console.log(target.a.b);//2
// 由于数组对象的属性值是下标,所以相同下标内容会被覆盖
Object.assign([1,2,3],[11,22,33,44]);//[11,22,33,44]

Object.is

===符号类似,用于判断是否全等,但是结果有些不同

Object.is(1,1); // true
Object.is(1,true); // false
Object.is([],[]); // false
Object.is(+0,-0); // false
Object.is(NaN,NaN); // true

参数默认值

  • ES6 的参数默认值规则和 C++不同,不必要必须右边的参数先有默认值,因为参数允许传 undefined
function add(a=1,b=2){
    return a + b;
}
add(); // 3
add(2); // 4
add(undefined, 4); // 5

variadic parameter 可变参数

function add(...num){
    return num.reduce(function(result,value){
        return result + value;
    });
}
add(1,2,3,4); // 10

lambda expression 箭头函数

箭头函数可以替代匿名函数、闭包,以更优雅的语法实现相同的功能。

let add = (a,b) => {
    return a+b;
}
let print = () => {
    console.log('hi');
}
let fn = a => a * a;
  • 箭头函数可以直接捕获定义时环境中的变量
  • 如果只有一个参数或没有参数可以省略=>左边的括号
  • 如果直接返回结果,可以没有大括号,但是可读性很差,不建议采用
  • 箭头函数与闭包最大的区别是this指向的对象不同,闭包的this指向后期闭包的调用者,而箭头函数的this与声明时的 this 指向一致,这样就不需要重命名this为参数了
  • 箭头函数没有 arguments 属性,可以利用新增的可变参数语法实现相同功能

class constructor static extends

通过一些类相关关键字实现面向对象,但 class 的本质依然是一个函数,只是增加了一些辅助关键字方便使用。

class Person {//关键字声明方式
    constructor(name, age){
        this.name = name;
        this.age = age
        this.say = () => {  // 这里定义实例的私有成员
            console.log(this.name + ":" + this.age);
        }
    }
    methods(){  // 此方法会被添加到类的 prototype 属性上
        console.log('hello ' + this.name);
    }
    // 无法在类中直接定义类的属性需要在类外添加到 prototype
    // property = "test";
    static sp = 123;    // 定义类的静态属性
    static sm = () => { // 定义类的静态函数
        console.log(this.sp);
    };
}
//let ex = class{}  字面量方式
var person = new Person('ren', 10);
person.say(); // 'ren:10'
person.methods(); // 'hello ren'
Person.sm(); // 123

class Student extends Person{
    constructor (name,age,sex){
        super(name,age);
        this.sex = sex;
    }
}
var student = new Student('ren',12,'male');
student.name; // 'ren'
student.sex; // 'male'
student.say(); // 'ren:12'
  • 类中各成员间无须,
  • super可以引用父类的this,且必须放在子类constructorthis调用之前

export import

通过导入导出关键字,可以指定文件向外导出的对象

let a = 'a';
let b = 1;
let c = {name:'c'};
let d = 'd';
export {a, b};
export defalut c;
export {d}
************************************
import 'url'
************************************
import x from 'url'
************************************
import c,{a,b} from 'url'
************************************
import {a as a1,b as b1} from 'url'
  • 只有 export 的对象才可以用 import 接收
  • export default 在一个文件中只能出现一次,表示默认导出的一个对象,可以用import x from 'xxx'导入
  • export 可以出现多次,分别导出不同的对象,导出的对象要用大括号包住
  • import 时 url 可以是绝对路径,也可以是相对路径
  • import 时可以不指定变量名,表示只是导入一次文件并不关心导出变量,通常是用于预处理或引入样式
  • import 时的对象名要与导出的一致,也可以在导入同时重命名,可以只导入所需的对象
  • import 默认导出对象时,可以用任意名字接收,因为这个名字不会造成歧义,无须区分名字

Generator yield

类似 Python 的 yield 语法,可以实现迭代功能的对象

function* idMaker(){
    let index = 0;
    while(true)
        yield index++;
}
let gen = idMaker(); // "Generator { }"
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

Promise

Promise 对象类似 setTimeout(xxx, 0),可以实现异步调用。如果浏览器支持,可以实现多线程加速。

let myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code.
  // In reality, you will probably be using something like XHR or an HTML5 API.
  setTimeout( function() {
    resolve("Success!")  // Yay! Everything went well!
  }, 250)
})

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log("Yay! " + successMessage)
});