《ES标准入门》&《UNDERSTANDING ECMACHRIPT 6》 读书摘录笔记(上)

写在最前面,我回归是因为CSDN终于可以写私有博客啦,赞!!!

前言

这两本书应该是目前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

关于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§;
}

    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 入门

UNDERSTANDING ES6 英文原版

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洲上牧童

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值