《ES标准入门》&《UNDERSTANDING ECMACHRIPT 6》 读书摘录笔记(上)
### 前言
*这两本书应该是目前ES6相关的比较好的了,网上有电子版本(文末有链接)。不过我买了书看,哈哈。这两篇摘录笔记分为上下两部分,本文是上半部分(1-6章),摘录了两本书里一些比较有用的知识点。*
### 目录
> #####1. 块级作用域绑定
> #####2. 字符串与正则表达式
> #####3. 函数的扩展
> #####4. 数组的扩展
> #####5. 对象的扩展
#####6. 集合(Set、Map)
> 7 . Symbol和Symbol属性
>
> 8 . Javascript中的类
>
> 9 . Promise、Generator函数、Async函数
>
> 10 . 代理(Proxy)和反射(Reflection)API
>
> 11 . 修饰器
>
> 12 . Module
### 一、块级作用域绑定
块级声明用于声明在指定块级作用域之外无法访问的变量。块级作用域存在于:1、函数内部;2、块中(字符'{'和'}'之间的区域)。Es6 中存在的两种变量声明就是Let和Const声明。
值得摘录的有以下六点:
##### 1. 不能重复声明
var count = 9;
//抛出语法错误
let count = 7;
##### 2. Const常量声明必须进行初始化
const age = 8;
//语法错误:常量未初始化
const name;
##### 3. 特殊的For循环
For循环设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for(let i = 0;i< 3;i++){
let i = 'can not change';
console.log(i);//输出'can not change'
}
##### 4. 暂时性死区
在区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭的作用域。只要在声明之前就使用这些变量,就会报错。
情况一:
var tmp = 123;
if(true){
tmp = 'abc';//ReferenceError
let tmp;
}
情况二:
function test(x = y,y = 2){
return [x,y];
}
test();//报错
情况三:
let x = x;//ReferenceError: x is not defined
##### 5. Const声明对象,对象属性可变
##### 6. 彻底冻结函数
constantize(obj) {//彻底冻结函数
Object.freeze(obj);
Object.keys(obj).forEach((key, i) => {
if (typeof obj[key] === 'object') {
constantize(obj[key]);
}
})
obj.name = 777;//TypeError: Cannot add property name, object is not extensible
return obj;
}
### 二、字符串与正则表达式
Es6加强了对Unicode的支持,并且扩展了字符串对象。正则表达式则增加了修饰符和属性。
值得摘录的有以下几点:
##### 1. codePointAt()与String.fromCodePoint()方法
完全支持UTF-16,接受编码单元的位置而非字符位置作为参数,返回与字符串中给定位置对应的码位,即一个整数值:
var text = "𠮷a";
console.log(text.charCodeAt(0)); //55362
console.log(text.codePointAt(0)); //134017
判断一个字符是2个字节还是4个字节组成:
function is32Bit(c){
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("𠮷"); // true
is32Bit("a"); // false
console.log(String.fromCodePoint(134071)); // "𠮷"
##### 2. normalize()方法
Es6为字符串添加了一个Normalize()方法,它可以提供Unicode的标准化形式,接受一个参数,指明应用哪种Unicode标准化形式(NFC,NFD,NFKC,NFKD)。只需要记住,在对比字符串之前,一定要先把它们标准化为同一种形式:
let normalized = values.map((text) => {
return text.normalize();
})
console.log(this[normalize]('\u01D1') === this[normalize]('\u004F\u030C')) // true
##### 3. includes()、startsWith()、endsWith()、repeat()方法
let tempArray = [1, "4", "uu", 5];
let tempStr = "test123uuunit582";
console.log(tempArray.includes(5)); //true
console.log(tempStr.includes("test123", 1)); //从第二个位置进行匹配
console.log(tempStr.startsWith("test")); //true
console.log(tempStr.startsWith("test123", 0)); //从第一个位置进行匹配
console.log(tempStr.endsWith("12",6)); //从第六个位置进行匹配
console.log(tempStr.repeat(3)); //test123uuunit582test123uuunit582test123uuunit582
##### 4. 模板字符串
领域专用语言(DSL),可以生成、查询并操作其他语言里的内容,且可以免受注入攻击(XSS、SQL等)。
> A、去掉模板字符串中的换行
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
> B、引用模板字符串本身
//写法一
let str = 'return ' + '`ni hao ${name}`';
let func = new Function('name',str);
let data = func('uct');
//写法二
let str2 = '(name) => `ni hao ${name}`';
let func2 = eval.call(null,str2);
let data2 = func2('uct');
console.log(data + ' ' + data2);
> C、标签模板
alert`123`;
//等同于
alert(123);
var a = 1,b = 2;
tag`Hello ${a + b} world ${a * b}`;
//等同于
tag(['Hello ',' world , ''],1,2);//第一个参数是数组,存放模板中没有变量替换的部分,后续参数是变量。
> D、String.raw()转义模板字符串
String.raw`Hi\n${3+4}`;//"Hi\\n5"
[关于new Function][newFunction]
[关于eval][eval]
[newFunction]: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function
[eval]:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval
##### 5.U修饰符与Y修饰符
U修饰符,即为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。Y修饰符,叫作“粘连”(Sticky)修饰符,它的后一次匹配都从上一次匹配成功的下一个位置开始。
> A、点(.)字符匹配(匹配除换行符外的所有字符)
var s = "𠮷";
/^.$/.test(s); // false
/^.$/u.test(s); // true
> B、返回字符串长度的函数
function codePointLength(){
var result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
> C、y修饰符隐含了头部匹配的标志(^)
const REGEX = /a/y;
REGEX.lastIndex = 2;
REGEX.exec("xaya"); // null
REGEX.lastIndex = 3;
const match = REGEX.exec("xaya");
match.index; // 3
### 三、函数的扩展
值得摘录的有以下几点:
##### 1.函数参数的默认值
> A、参数变量默认声明
function test(x = 1){
let x = 2;//error
}
> B、惰性求值
let x = 10;
function testLazy(p = x + 1){
console.log(p);
}
testLazy(); // 11
let x = 12;
testLazy(); // 13
> C、非尾部设置默认值,参数不可省略
function f(x = 1,y){
return [x,y];
}
f(,1); //报错
> D、默认参数的临时死区TDZ
function add(first = second,second){
return first + second;
}
console.log(add(1,1)); // 2
console.log(add(undefined,1)); // 抛出错误
//当初次调用add()时,绑定first和second被添加到一个专属于函数参数的临时死区
//表示调用add(1,1) 时的Javascript 代码
let first = 1;
let second = 2;
//表示调用add(undefined,1) 时的Javascript 代码
let first = second;
let second = 1; //所以抛出错误;
##### 2.默认参数表达式
初次解析函数声明时不会调用getValue()方法,只有当调用add()函数且不传入第二个参数时才会调用:
constructor() {
this.value = 5;
}
getValue(){
return this.value ++;
}
add(first,second = this.getValue()){
console.log('plus: ', first + second );
}
...
Es6Function.add(1,1); // plus: 2
Es6Function.add(1); // plus: 6
Es6Function.add(1); // plus: 7
##### 3. 只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能严格模式
const doSomething = (a,b = a,...c) => { //error
'use strict';
//code
}
##### 4. 不定参数限制使用
> 每个函数最多只能声明一个不定参数,而且一定要放在所有参数的末尾。
function any(a,...b,last){} //报错
> 不定参数不能用于对象字面量Setter中
let object = {
//语法错误,不可以在Setter中使用不定参数
set name(...value){}
}
> 取数组最大值
let values = [25,34,11,99];
console.log(Math.max(...values)); // 99
##### 5. 箭头函数
> A、箭头函数中的This值取决于该函数外部非箭头函数的this值,且不能通过call()、apply()或bind()方法来改变this值
thisScope(){
setTimeout(()=>{
console.log('id: ',this.id);
},100);
}
...
var id = 55;
Es6Function.thisScope.call({id:100}); // id : 100
> B、函数体内不可民使用Arguments对象,该对象在函数体内不存在,可以用Rest参数(...)代替
testArguments(){
setTimeout(() => {
console.log('args: ',arguments)
}, 100);
}
...
Es6Function.testArguments(1,2,5,6); // args: Arguments(4) [1, 2, 5, 6, callee: (...), Symbol(Symbol.iterator): ƒ]
##### 6. 尾调用优化
> 尾调用就是指某个函数的最后一步是调用另一个函数,只在严格模式下开启。
"use strict";
function doSomething(){
//优化后,立即返回结果
return doSomethingElse();
}
function doSomething2(){
//无法优化,无返回
doSomethingElse();
}
> 非严格模式下实现尾递归优化,用“循环” 替换 “递归”
function sum(x,y){
if(y > 0){
return sum( x + 1,y - 1);
} else {
return x;
}
}
sum(1,1000000);// Uncaught RangeError: Maximum call stack size exceeded(...)
//用蹦床函数可以将递归执行转为循环执行
function trampoline(f){
while(f && f instanceof Function){
f = f();
}
return f;
}
//返回一个函数,然后执行该函数,而不是在函数里调用函数,避免递归,消除调用栈过大问题
### 四、数组的扩展
值得摘录的有以下几点:
##### 1. 扩展运算符(...)
扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署该接口就无法转换
> 替代数组的 Apply方法
//Es5 的写法
function f(x,y,z){}
var args = [1,2,3];
f.apply(null,args);
//Es6 的写法
function f(x,y,z){}
var args = [1,2,3];
f(...args);
> 合并数组
Es5 合并数组
var arr1 = [1,2];
var arr2 = [3,4];
var arr3 = [5];
arr1.concat(arr2,arr3);
Es6 合并数组
[...arr1,...arr2,...arr3];
> 能够识别 32 位的 Unicode 字符的取字符串长度的方法
function length(str){
return [...str].length;
}
length('x\uD83D\uDE80y'); // 3
##### 3. Array.from()方法,用于将类似数组对象和可遍历对象(包括Set和Map结构)转为真正的数组
Array.from()方法除了支持遍历器接口转换外,还支持类数组对象(类数组对象的本质就是有Length属性)
Array.from({length:3});
// [undefined,undefined,undefined]
> 映射转换,Array.from()的第二个参数用来遍历类数组对象并返回
translate() {
return Array.from(arguments, (value) => value + 1);
}
console.log(NewArray.translate(1,2,3)); // [2,3,4]
##### 4. 为所有数组添加新的方法
> Array.of(),为了弥补数组构造函数Array()的不足
Array.of(3,11,8); // [3,11,8]
Array.of(undefined); // [undefined] , 通过Vue 显示到页面则为 [null]
Array(3); // [,,]
> copyWithin(),把数组内部指定位置成员复制到其他位置(会覆盖其他成员)
//将3号位复制到0号位
[1,2,3,4,5].copyWithin(0,3,4); //[ 4, 2, 3, 4, 5 ]
// -2相当于3号位,-1相当于4号位
[1,2,3,4,5].copyWithin(0,-2,-1); //[ 4, 2, 3, 4, 5 ]
> find(),返回符合条件的第一个(可能有多个)成员,如果没有就返回undefined
testFind(){ // 返回 3
return [1,2,3,4,5].find((value,index,arr)=>{
return value > 2;
})
}
> findIndex(),返回第一个符合条件的成员的位置,如果没有就返回 -1
testFindIndex(){ // 返回 -1
return [1,2,3,4,5].findIndex((value,index,arr)=>{
return value > 6;
})
}
> fill(),填充一个数组,接受第二第三个参数(起始位置和结束位置)
testFill(){
console.log(['a','b','c'].fill(8,1,2)); // ["a", 8, "c"]
return new Array(3).fill(8); //[ 8, 8, 8 ]
}
> includes(),返回一个布尔值,表示某个数组是否包含给定的值,它与indexOf 比起来更加直观,并且可以判断NaN的情况
testIncludes(){
return [1,2,NaN].includes(NaN); // true
}
> entries(),keys(),values()方法,它们都返回一个遍历器对象
testEntries(){
for(let [key,value] of ['a','b','c'].entries()){
console.log(key,value); // 0 "a" 1 "b" 2 "c"
}
let letter = ['a','b','c'];
//手动调用遍历器对象的Next方法进行遍历
let entries = letter.entries();
console.log(entries.next().value); //[0, "a"]
console.log(entries.next().value); //[1, "b"]
console.log(entries.next().value); //[2, "c"]
}
### 五、对象的扩展
ECMAScript6 中,为了使某些任务更易完成,在全局Object 对象上引入了一些新方法
值得摘录的有以下几点:
##### 1. 可计算属性名
let suffix = " name";
var person = {
["first" + suffix]: "Nicholas", // Es5 版本,这里的名称是不可以计算的
["last" + suffix]: "Zakas",
}
console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"
##### 2. Object.is(),弥补全等(===)运算符的特殊情况判断不一致
testObjectIs(){
console.log(+0 == -0); // true
console.log(+0 === -0); // true
console.log(Object.is(+0 , -0)); // false
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN , NaN)); // true
}
##### 3. Object.assign(),只会复制对象自身可枚举属性,忽略enumerable为false的属性
> 多个对象复制具有同名属性,排位靠后的源对象会覆盖排位靠前的
testObjectAssign(){
let receiver = {};
Object.assign(receiver,{
type:'girl',
name:'superman',
},{
type:'boy',
})
return receiver.type + " : " + receiver.name; // boy : superman
}
> 对象浅复制(源对象某个属性是对象,只复制这个对象的引用)
let target = { a: { b: 'c', d: 'e' } };
let source = { a: { b: 'hello' } }
console.log(Object.assign(target, source)); // {a: {b:'hello'}}
// a属性被整体覆盖掉,不会得到 {a : {b:'hello',d:'e'}} 的结果
##### 3. Object.values()
> 只返回对象自身可遍历属性
testObjectValues() { // p 的属性描述对象的 enumerable 默认是 false
var obj = Object.create({}, { p: { value: 42 } });
return Object.values(obj); // []
}
> Object.values 会过滤属性名为Symbol值的属性
console.log(Object.values({ [Symbol()]: 123, any: 'abc' })); // ['abc']
##### 3. Object.entries()
> 转换对象为真正的Map结构
testObjectEntries(){
let obj = { a: 'bar', b: 23 };
let map = new Map(Object.entries(obj));
console.log(map.get('a')); // bar
}
### 六、集合(Set、Map)
Es6 中的Set和Map核心思想跟Java中的Set和Map类似,Set集合成员是唯一的,没有重复,Set本身是构造函数用来生成Set数据结构;Map类型是一种存储着许多键值对的有序列表,它的键名的判断是通过Object.is()方法来实现的(Set也是通过这个方法来判断两个值是否一致),所以 5 和字符串"5"会被判定为两种类型。
值得摘录的有以下几点:
##### 1. Set
> 添加元素用add()方法,删除用delete()方法,判断存在用has(),清空用clear()
testSet() {
let set = new Set();
set.add(5);
set.add('5');
console.log(set.has(5)); // true
set.delete(5);
console.log(set.has(5)); // false
console.log(set.size); // 1
set.clear();
console.log(set.has('5')); // false
console.log(set.size); // 0
}
> 数组去重
testArrayUniq(){
let set = new Set([1,2,3,3,3,3,4,4,4,4,5]);
let arr = [...set];
console.log(arr); //[ 1, 2, 3, 4, 5 ]
return arr; //[ 1, 2, 3, 4, 5 ]
}
> 改变原来的Set结构
changeSet(){
let set = new Set([1, 2, 3]);
//方法二
set = new Set([...set].map((val) => val * 2));
//方法二
set = new Set(Array.from(set, val => val * 2));
return set; // [ 4, 8, 12 ]
}
##### 2. Map
Map结构提供了“值-值”对应,而Object结构提供了“字符串-值”对应,Map结构是一种更加完善的Hash结构实现
> 设置值set(),获取值get(),判断存在has(),删除delete(),清空用clear(),多次对同一个键赋值,后面覆盖前面
testMap(){
const m = new Map();
const o = {p:'hello uct'};
m.set(o,'content');
m.set(o,'new content')
console.log(m.get(o)); // new content
console.log(m.has(o)); // true
m.delete(o);
console.log(m.has(o)); // false
}
> Map 和 对象互相转换
//Map转为对象
strMapToObj(strMap){//Map to Object
let obj = Object.create(null);
for(let [k,v] of strMap){
obj[k] = v;
}
return obj;
}
//对象转为Map
objToStrMap(obj){// Object to Map
let strMap = new Map();
for(let k of Object.keys(obj)){
strMap.set(k,obj[k]);
}
return strMap;
}
##### 3. WeakMap
WeakMap只接受对象作为键名(Null除外)
> WeakMap 应用的典型场景就是以DOM节点作为键名的场景
const listener = new WeakMap();
listener.set(element1,handler1);
listener.set(element2,handler2);
element1.addEventListener('click',listener.get(element1),false);
element2.addEventListener('click',listener.get(element2),false);
//一旦DOM对象消失,与它绑定的监听函数也会自动消失
> 部署私有属性
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);//weakmap的私有属性
_action.set(this, action); //weakmap的私有属性
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
console.log(counter);
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
export default Countdown;
### 结语
*本来觉得没有必要写这个笔记,但是,看和写真的是两回事。书中的例子大都是抽象化的,也就是为了让大家看起来直观,去掉了上下文,有些还有错。自己动手写的话(运用实例),印象会非常深刻,在真实开发环境中会想到去用它。笔记的下篇已经在写了,相对上篇,我更期待下篇,NSL...*
### 参考链接
[ECMAScript 6 入门](http://es6.ruanyifeng.com/)
[UNDERSTANDING ES6 英文原版](https://leanpub.com/understandinges6/read)