53-附录-ECMAScript Harmony

一、ECMAScript Harmony

Harmony(ECMAScript 6)与 ECMAScript 5 的指导思想一致:只进行增量调整,不彻底改造 JavaScript 语言。

1. 一般性变化

(1)常量
const 关键字声明常量:const MAX_SIZE = 25;
使用方式与 var 类似,但 const 声明的变量在初始赋值后,就不能再重新赋值。
可以像声明变量一样在任何地方声明常量。
同一作用域中,常量名不能与其他变量或函数名重名。
支持常量的浏览器:Firefox、Safari 3+、Opera 9+ 和 Chrome。

(2)块级作用域及其他作用域
JavaScript 没有块级作用域,在语句块中定义的变量与在包含函数中定义的变量共享相同的作用域。
定义块级作用域的语法:使用 let 关键字。

与 const 和 var 类似,可以使用 let 在任何地方定义变量并为变量赋值。
区别:使用 let 定义的变量在定义它的代码块之外没有定义。

for (var i=0; i < 10; i++) { 
    //执行某些操作
}
alert(i); //10

for (let i=0; i < 10; i++) {
    //执行某些操作
}
alert(i); //错误!变量 i 没有定义

另外的使用方式:
1-创建 let 语句,在其中定义只能在后续代码块中使用的变量

var num = 5;
let (num=10, multiplier=2){
    alert(num * multiplier); //20
}
alert(num); //5
// let 语句创建了自己的作用域,这个作用域里的变量与外面的变量无关

2-创建 let 表达式,其中的变量只在表达式中有定义

var result = let(num=10, multiplier=2) num * multiplier;
alert(result); //20
// 执行表达式之后,num 和 multiplier 变量就不存在了
2. 函数

(1)剩余参数与分布参数
剩余参数(rest arguments):可以定义可能会传进来的更多参数,然后把它们收集到一个数组中。
语法形式:三个点后跟一个标识符。

分布参数(spread arguments):通过分布参数,可以向函数中传入一个数组,然后数组中的元素会映射到函数的每个参数上。
语法形式:与剩余参数的语法相同,在值的前面加三个点。

唯一区别:分布参数在调用函数的时候使用,剩余参数在定义函数的时候使用。

// 使用剩余函数
function sum(num1, num2, ...nums){
    var result = num1 + num2;
    for (let i=0, len=nums.length; i < len; i++){
        result += nums[i];
    }
    return result;
}
var result = sum(1, 2, 3, 4, 5, 6);
// 使用分布函数
var result = sum(...[1, 2, 3, 4, 5, 6]);
var result = sum.apply(this, [1, 2, 3, 4, 5, 6]); // 等价代码

(2)默认参数值
为参数指定默认值,可以在参数名后面直接加上等于号和默认值。

function sum(num1, num2=0){
    return num1 + num2;
}
var result1 = sum(5);
var result2 = sum(5, 5);

(3)生成器
生成器,就是一个对象,每次能生成一系列值中的一个。
创建生成器,可以让函数通过 yield 操作符返回某个特殊的值。

对于使用 yield 操作符返回值的函数,调用它时就会创建并返回一个新的 Generator 实例。然后,在这个实例上调用 next() 方法就能取得生成器的第一个值。此时,执行的是原来的函数,但执行流到 yield 语句就会停止,只返回特定的值。(这点 yield 与 return 很相似)
如果再次调用 next() 方法,原来函数中位于 yield 语句后的代码会继续执行,直到再次遇见 yield 语句时停止执行,此时再返回一个新值。

function myNumbers(){
    for (var i=0; i < 10; i++){
        yield i * 2;
    }
}
var generator = myNumbers();
console.log(generator.next()); // {value: 0, done: false}
console.log(generator.next()); // {value: 2, done: false}
console.log(generator.next()); // {value: 4, done: false}
generator.close(); // 书中为关闭生成器,验证报错
3. 数组及其他结构

(1)迭代器
为对象创建迭代器,可以调用 Iterator 构造函数,传入想要迭代其值的对象。
要取得对象中的下一个值,可以调用迭代器的 next()方法。默认情况下,这个方法会返回一个数组。

该方法现在测试报错。
ES6 实现了几种迭代器:
forEach、every、some、reduce、reduceRight (不生成新数组)
map、filter (生成新数组)

(2)数组领悟
数组领悟(array comprehensions):指的是用一组符合某个条件的值来初始化数组。
基本形式:array = [ value for each (variable in values) condition ];

该方式现在测试报错。
ES6 实现方式:filter()。

(3)解构赋值
需求:从一组值中挑出一或多个值,然后把它们分别赋给独立的变量。
–> 解构赋值(destructuring assignments)

// 数组
var [name, value] = ["color", "red"];
var [, value] = ["color", "red"]; // 取部分值
// 交换变量,不需要临时变量temp了
var value1 = 5;
var value2 = 10;
[value2, value1] = [value1, value2];
// 对象
var person = {
    name: "Nicholas",
    age: 29
};
var { name: personName, age: personAge } = person;
// 定义了两个变量,personName 和 personAge,分别得到了 person 对象中对应的值
var { age: personAge } = person; // 选择要取得的值
4. 新对象类型

(1)代理对象
代理(proxy):一个表示接口的对象,对它的操作不一定作用在代理对象本身。
代理是一种非常有用的抽象机制,能够通过 API 只公开部分信息,同时还能对数据源进行全面控制。

创建代理对象:Proxy.create(),传入一个 handler (处理程序)对象和一个可选的 prototype (原型)对象。
var proxy = Proxy.create(handler, myObject);

ES6 实现方式:var p = new Proxy(target, handler);
target 是要拦截的对象,handler 是拦截函数对象。
handler 提供了13种拦截行为:
getPrototypeOf / setPrototypeOf
isExtensible / preventExtensions
ownKeys / getOwnPropertyDescriptor
defineProperty / deleteProperty
get / set / has
apply / construct

(2)代理函数
代理函数(proxy function):代理函数与代理对象的区别是它可以执行
var proxy = Proxy.createFunction(handler, function(){}, function(){});

(3)映射与集合
Map 类型,也称为简单映射,只有一个目的:保存一组键值对儿。
简单映射能做到键和值与对象属性分离,从而保证对象属性的安全存储。
简单映射的基本 API:get()、set() 和 delete()。

var map = new Map();
map.set("name", "Nicholas");
map.set("book", "Professional JavaScript");
console.log(map.has("name")); //true
console.log(map.get("name")); //"Nicholas"
map.delete("name");

Set 类型,也称集合,就是一组不重复的元素。
与简单映射不同的是,集合中只有键,没有与键关联的值。(??)
在集合中,添加元素使用 add() 方法,检查元素是否存在使用 has() 方法,删除元素使用 delete() 方法。

var set = new Set();
set.add("name");
console.log(set.has("name")); //true
set.delete("name");
console.log(set.has("name")); //false

ES6 实现方式:
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
语法:new Map([iterable])
所有 Map 对象实例继承 Map.prototype。

属性:
  • Set.prototype.constructor:返回一个函数,它创建了实例的原型。默认是Map函数。
  • Set.prototype.size:返回Map对象的键/值对的数量。
方法:
  • Set.prototype.clear():移除Map对象的所有键/值对。
  • Set.prototype.delete(key):如果Map对象中存在该元素,移除并返回true;如果该元素不存在则返回false。
  • Set.prototype.entries():返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组。
  • Set.prototype.forEach(callbackFn[, thisArg]):按插入顺序,为Map对象里的每一键值对调用一次callbackFn函数。如果为forEach提供了thisArg,它将在每次回调中作为this值。
  • Set.prototype.get(key):返回键对应的值,如果不存在,则返回undefined。
  • Set.prototype.has(key):返回一个布尔值,表示Map实例是否包含键对应的值。
  • Set.prototype.keys():返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的键 。
  • Set.prototype.set(key, value):设置Map对象中键的值。返回该Map对象。
  • Set.prototype.values():返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的值 。
  • Set.prototype[@@iterator]():返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组。

Set 对象存储任何类型的唯一值,无论是原始值或者是对象引用。
语法:new Set([iterable]);
所有 Set 实例继承自 Set.prototype。

属性:
  • Set.prototype.constructor:返回实例的构造函数。默认情况下是Set。
  • Set.prototype.size:返回Set对象的值的个数。
方法:
  • Set.prototype.add(value):在Set对象尾部添加一个元素。返回该Set对象。
  • Set.prototype.clear():移除Set对象内的所有元素。
  • Set.prototype.delete(value):移除Set的中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。Set.prototype.has(value)在此后会返回false。
  • Set.prototype.entries():返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法和Map对象保持相似, 每个值的键和值相等。
  • Set.prototype.forEach(callbackFn[, thisArg]):按照插入顺序,为Set对象中的每一个值调用一次callBackFn。如果提供了thisArg参数,回调中的this会是这个参数。
  • Set.prototype.has(value):返回一个布尔值,表示该值在Set中存在与否。
  • Set.prototype.keys():与values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
  • Set.prototype.values():返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
  • Set.prototype[@@iterator]():返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

(4)WeakMap 和 WeakSet
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
语法:new WeakMap([iterable])
所有 WeakMap 实例继承自 WeakMap.prototype。
属性:constructor
方法:delete(key)、get(key)、has(key)、set(key, value)

WeakSet 对象允许你将弱保持对象存储在一个集合中。
语法:new WeakSet([iterable]);
所有 WeakSet 实例都继承自 WeakSet.prototype。
属性:constructor
方法:add(value)、clear()、delete(value)、has(value)

注意浏览器兼容。

(5)StructType
JavaScript 一个最大的不足是使用一种数据类型表示所有数值。
ECMAScript 6 引入了类型化结构,带来了更多的数值数据类型。
var Size = new StructType({ width: uint32, height: uint32 });

(6)ArrayType
通过数组类型(ArrayType)可以创建一个数组,并限制数组的值必须是某种特定的类型(与 WebGL 中的类型化数组很相似)。
var SizeArray = new ArrayType(Size, 2);

5. 类

JavaScript 中的类只是一种语法糖,覆盖在目前基于构造函数和基于原型的方式和类型之上。
class name [extends] { // class body }

// 普通方法定义
function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function(){
    alert(this.name);
};
Person.prototype.getOlder = function(years){
    this.age += years;
};
// 新语法定义的类
class Person {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    sayName(){
        alert(this.name);
    }
    getOlder(years){
        this.age += years;
    }
}

新语法以关键字 class 开头,然后就是类型名,花括号中定义的是属性和方法。
定义方法不必再使用 function 关键字,有方法名和圆括号就可以。
如果把方法命名为 constructor,那它就是这个类的构造函数(与前一个例子中的 Person 函数一样)。
在这个类中定义的方法和属性都会添加到原型上,具体来说,sayName() 和 getOlder() 都是在 Person.prototype 上定义的。

(1)私有成员
类语法建议默认支持私有成员,包括实例中的私有成员和原型中的私有成员。

(2)getter和setter
新的类语法支持直接为属性定义 getter 和 setter,方法名前加 get 和 set 关键字。

class Person {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    get age(){
        console.log('获取age');
        return this._age;
    }
 
    set age(age){
        console.log('设置age');
        this._age = age;
    }
}

(3)继承
使用类语法而不是过去那种 JavaScript 语法,最大的好处是容易实现继承。
只要使用与其他语言相同的 extends 关键字就能实现继承,而不必去考虑借用构造函数或者原型连缀。

class Employee extends Person {
    constructor(name, age){
        super(name,age);
    }
}
// 等价于
function Employee(name, age){
    Person.call(this, name, age);
}
Employee.prototype = new Person();
6. 模块

模块(或者“命名空间”、“包”)是组织 JavaScript 应用代码的重要方法。
每个模块都包含着独立于其他模式的特定、独一无二的功能。

模块在其自己的顶级执行环境中运行,因而不会污染导入它的全局执行环境。
默认情况下,模块中声明的所有变量、函数、类等都是该模块私有的。

对于应该向外部公开的成员,可以在前面加上 export 关键字。

module MyModule { 
    //公开这些成员
    export let myobject = {};
    export function hello(){ alert("hello"); };
    //隐藏这些成员 
    function goodbye(){
        //... 
    }
}

导入模块使用 import 命令。

//只导入 myobject
import myobject from MyModule;
console.log(myobject);
//导入所有公开的成员
import * from MyModule;
console.log(myobject);
console.log(hello);
//列出要导入的成员名
import {myobject, hello} from MyModule;
console.log(myobject);
console.log(hello);
//不导入,直接使用模块
console.log(MyModule.myobject);
console.log(MyModule.hello);

外部模块
通过提供模块所在外部文件的 URL,也可以动态加载和导入模块。
首先要在模块声明后面加上外部文件的 URL,然后再导入模块成员。

module MyModule from "mymodule.js";
import myobject from MyModule;

import myobject from "mymodule.js";

上一篇:52-JavaScript高级程序设计-新兴的API
下一篇:54-附录-严格模式

全书整理版:《Javascript高级程序设计》第3版(总结版)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值