《ECMAScript 6 入门》阅读笔记(未完结)

http://caibaojian.com/es6/object.html

《ECMAScript 6 入门》阅读笔记(未完结)_第1张图片

1.ECMAScript 和 JavaScript 的关系

前者是后者的标准,后者是前者的一种实现。2015 年 6 月,ECMAScript 6 正式通过,成为国际标准。

2.let 和 const 命令

let:代码块内有效、无变量提升、不能重复声明;
const:

  • 有块作用域;
  • 声明只读变量,一旦声明必须初始化,声明之后不允许改变;
  • 声明一对象时,const变量中存储的是对象的指针,指针不可变,但对象本身可变;

3.解构赋值

《ECMAScript 6 入门》阅读笔记(未完结)_第2张图片
解构:从数组和对象中提取值,对变量进行赋值;
数组模型的解构(Array)

  • 不完全解构:let [x, y] = [1, 2, 3]; x=1,y=2;
  • 剩余运算符: let [a, ...b] = [1, 2, 3];,b = [2, 3];
  • 解构默认值:当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果;let [a = 2] = [undefined]; // a = 2

对象模型的解构(Object)

  • 不完全解构:let {p: [{ y }, x ] } = {p: [{y: 'world'}] };, x=undefined,y=‘world’;
  • 剩余运算符: let {a, b, …rest} = {a: 10, b: 20, c: 30, d: 40};`,rest = {c: 30, d: 40};
  • 解构默认值:let {a: aa = 10, b: bb = 5} = {a: 3};, aa = 3; bb = 5l;

解构赋值常用于:

  • 变量交换 [a,b] = [b,a]
  • 获取函数返回值 , [a,b] = f();
  • 选择性接收函数返回值, [a,b] = f();
  • 不确定函数共返回多少值,[a,…b] = f();

3.ES6 正则的扩展

《ECMAScript 6 入门》阅读笔记(未完结)_第3张图片

1.在ES5中RegExp构造函数的参数可为字符串/正则表达式,但参数为正则表达式时,ES5不允许使用第二个参数来添加修饰符;而在ES6中可以,并且返回的正则表达式会忽略原有的修饰符,使用新指定的修饰符。

var regex = new RegExp('xyz', 'i');   // 等价于 var regex = /xyz/i;
var regex = new RegExp(/xyz/i);      //  等价于 var regex = /xyz/i;
ES6
new RegExp(/abc/ig, 'i').flags

2.字符串的正则方法:match()、replace()、search()和split();
3.u修饰符,含义为“Unicode模式”,若字符串中有的字符是大于两个字节的,一定要加u修饰符才能正确识别,否则会把这个字符识别成两个字符,导致无法正确匹配;

  • .字符并不是可以匹配任何单个字符,只是说可以匹配任何单个不超过两个字节的字符;对于超过两字节的字符,必须加上u.字符才能正确识别; 另外,.字符也不能匹配换行符、回车符、行分隔符、段分隔符
    因此引入了s修饰符,从而使得.字符真正实现匹配任意单个字符。

  • ES6中,用\u{}表示Unicode字符,必须加u修饰符,否则{}中的数字会被解读为量词,/^\u{3}$/.test('uuu') // true

4.y修饰符,yg都是全局匹配,g是从上一次匹配的位置继续向后搜索,y要求必须从下一个位置开始匹配成功,匹配不成功就不再向后搜索返回null;
5.sticky属性判断是否设置了y修饰符;var r = /hello\d/y; r.sticky // true


4.字符串的扩展

《ECMAScript 6 入门》阅读笔记(未完结)_第4张图片
Unicode表示法

  • 超过两个字节的字符必须写成\u{}表示形式,如 \u{20BB7}

codePointAt()

  • ES5中charAt()charCodeAt()对Unicode 的处理是不到位的;ES6中,使用codePointAt()返回一个字符的码点,包括大于两个字节的字符;
  • codePointAt()返回的是码点的十进制值,如果想要十六进制的值,可以使用toString方法转换一下,s.codePointAt(0).toString(16) // "20bb7"
  • codePointAt()是测试一个字符由两个字节还是由四个字节组成的最简单方法,c.codePointAt(0) > 0xFFFF
  • 结合for…of循环,正确识别32位的UTF-16字符;
var s = '?a';
for (let ch of s) {
  console.log(ch.codePointAt(0).toString(16));
}

String.fromCodePoint()

  • ES5中使用fromCharCode()从码点返回对应字符,但无法识别Unicode编号大于0xFFFF的字符
  • ES6中引入String.fromCodePoint()解决上述问题

字符串的遍历器接口

  • 添加遍历器接口,?字符串可以被for...of循环遍历,其最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点;

子串的识别:

  • (原)indexOf,返回参数字符串在字符串首次出现的位置
  • (原)lastIndexOf,返回参数字符串在字符串最后一次出现的位置
  • (新)includes(),是否找到参数字符串,返回true/false
  • (新)startsWith(),参数字符串是否在原字符串的头部,返回true/false
  • (新)endsWith(),参数字符串是否在原字符串的尾部,返回true/false

字符串重复

  • string.repeat(n),指定字符串重复次数,返回新字符串;

字符串补全

  • padStart:返回新的字符串,表示用参数字符串从头部补全原字符串。
  • padEnd:返回新的字符串,表示用参数字符串从头部补全原字符串。
  • 以上两个方法接受两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。如果没有指定第二个参数,默认用空格填充。

模板字符串

  • 把数据和模板最后拼成一个字符串
  • 用反引号 `,可定义多行字符串,可在字符串中加入变量和表达式。
  • 多行字符串中的空格和换行被保留
  • 变量和表达式写在${}

标签模板

  • 是一个函数的调用,其中调用的参数是模板字符串,如alertHello world!;等价于alert('Hello world!');
  • 当模板字符串中带有变量,会将模板字符串参数处理成多个参数。fMy Name is ${name},I am ${age+1} years old next year.;等价于f(['My Name is',',I am ',' years old next year.'],'Mike',28);

String.raw()

  • String.raw()用于处理模板字符串,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。String.rawHi\n${2+3}!;// "Hi\\n5!"

5.数值的扩展 [新增方法 方法调整]

二进制和八进制表示

  • 二进制新写法 前缀 0b / 0B + 二进制 , 如0b11
  • 八进制新写法 前缀 0o / 0O + 八进制 , 如0o11

常量

  • Number.EPSILON,表示 1 与大于 1 的最小浮点数之间的差,约为 252 , 2.2204460492503130808472633361816E-16;用于测试数值是否在误差范围内,在误差范围内即视为相等equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true
  • Number.MAX_SAFE_INTEGER, 最大安全整数,253-1
  • Number.MIN_SAFE_INTEGER, 最小安全整数,-253+1

Number对象新方法

  • (新)Number.isFinite(),检查一个数值是否为有限的;所有非数值/NaN/Infinity/-Infinity都返回 false;
  • (新)Number.isNaN(),非 NaN 全部返回 false;

从全局移植到 Number 对象的方法

  • Number.parseInt(),将给定字符串转化为指定进制的整数,默认为 10 进制,小数点自动忽略;
  • Number.parseFloat(),将一个字符串解析成浮点数,若无法被解析成浮点数,则返回 NaN;
  • Number.isInteger(),判断给定的参数是否为整数,NaN 和正负 Infinity 不是整数;
  • Number.isSafeInteger(),判断数值是否在安全范围内;

Math 对象的扩展

  • Math.cbrt(),计算立方根,会对非数值进行转换,非数值且无法转换为数值时返回 NaN;
  • Math.imul(),两个数以 32 位带符号整数形式相乘的结果;
  • Math.hypot(),计算所有参数的平方和的平方根,非数值会先被转换为数值,空值会被转换为 0,参数为 Infinity 或 -Infinity 返回 Infinity,参数中存在无法转换为数值的参数时返回 NaN;
  • Math.clz32(),返回数字的32 位无符号整数形式的前导0的个数;当参数为小数时,只考虑整数部分;于空值或非数值,会转化为数值;
  • Math.trunc(),返回数字的整数部分;整数部分为 0 时也会判断符号;会将非数值转为数值;空值或无法转化为数值时时返回 NaN;
  • Math.fround(),获取数字的32位单精度浮点数形式;
  • Math.sign(),判断数字的符号(正、负、0),正数返1,负数返-1,0返0,-0返-0;会对非数值进行转换;参数为非数值(无法转换为数值)时返回 NaN;
  • Math.expm1(),计算 e 的 x 次方减 1 ,即 Math.exp(x) - 1
  • Math.log1p(x),即 Math.log(1 + x)
  • Math.log10(x),计算以 10 为底的 x 的对数
  • Math.log2(x),计算以 2 为底的 x 的对数
  • Math.sinh(x): 用于计算双曲正弦。
  • Math.cosh(x): 用于计算双曲余弦。
  • Math.tanh(x): 用于计算双曲正切。
  • Math.asinh(x): 用于计算反双曲正弦。
  • Math.acosh(x): 用于计算反双曲余弦。
  • Math.atanh(x): 用于计算反双曲正切。

6.数组的扩展

数组创建(of/from)、遍历(entries/keys/values)、索引(find/findIndex)、填充(fill/copyWithin)、包含(includes)、嵌套(flat/flatMap)、数组缓冲区(ArrayBuffer)、视图(DataView)、定型缓冲区(Int8Array…)、扩展运算符…


ES6空位 - ES6是明确将空位转为undefined,map()遍历会跳过空位 数组创建 - Array.of(),将一组值转换为数组,参数值可为不同类型; - Array.from(),将**类数组对象**或**可迭代对象**(map/set/字符串)转化为数组;

数组查找

  • arr.find(),查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素;
  • arr.findIndex(),查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引;

数组填充

  • arr.fill(arg1,arg2,arg3),将一定范围索引的数组元素内容填充为单个指定的值; arg1为填充值,arg2和arg3为填充的起始和结束索引,不包含结束索引;
  • arr.copyWithin() , 将一定范围索引的数组元素修改为此数组另一指定范围索引的元素;[1, 2, 3, 4].copyWithin(0,2,4) // [3, 4, 3, 4];第一个参数是被修改的起始索引,第二个参数是被用来覆盖的数据的起始索引,第三个参数可选;

数组遍历

  • entries() , 遍历键值对;
for(let [key,value] of ['a','b','c'].entries()){
    console.log(key,value);}
    
let ent = [1,2,3,4,5].entries();
console.log(ent.next().value);
console.log(ent.next().value);
  • keys(),遍历键名;
for(let key of ['a','b'].keys()){
    console.log(key); //[0,1]
}
console.log([...['a','b','c'].keys()]); //[0,1,2]
  • values(),遍历键值;
for(let value of ['a','b','c'].values()){
    console.log(value);
}
console.log([...['a','b','c'].values()]);

数组包含关系

  • includes(),数组是否包含指定值,true/false,参数1指包含的指定值,参数2指搜索的起始索引,默认为0;[1, 2, 3].includes(1); // true

嵌套数组转一维数组

  • flat(),带参数时可指定转换的嵌套层数,console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]],若参数为Infinity,表示不管嵌套多少层,全部转为一维数组,自动跳过空位;
  • flatMap(),先对数组中每个元素进行了的处理,再对数组执行 flat() 方法。[1, 2, 3].flatMap(n => [n * 2]);

数组缓冲区

  • 内存中的一段地址,实际字节数在创建时确定,之后只可修改其中的数据,不可修改大小;
//由构造函数创建缓冲区
let buffer = new ArrayBuffer(10);
let buffer1 = buffer.slice(1,3);
console.log(buffer.byteLength);
console.log(buffer1.byteLength);

视图 DataView类型

  • 视图是用来操作内存的接口,可操作数组缓冲区读取和写入数据,对8种数据类型通用。
// 默认 DataView 可操作数组缓冲区全部内容
let buffer = new ArrayBuffer(10);
    dataView = new DataView(buffer); 
dataView.setInt8(0,1);
console.log(dataView.getInt8(0)); // 1
 
// 通过设定偏移量(参数2)与长度(参数3)指定 DataView 可操作的字节范围
let buffer1 = new ArrayBuffer(10);
    dataView1 = new DataView(buffer1, 0, 3);
dataView1.setInt8(5,1); // RangeError

定型数组 (特定类型的视图)

  • 强制使用特定的数据类型,而不是使用通用的 DataView 对象来操作数组缓冲区;
  • 可接受参数包括定型数组、可迭代对象、数组、类数组对象;
  • let view = new Int32Array(10); 长度为40;
  • 定型数组可使用 entries()、keys()、values()进行迭代;
  • 定型数组不是普通数组,不继承自 Array ;Array.isArray(view) 为false;
  • 所有定型数组都含有静态 of() 方法和 from() 方法,运行效果分别与 Array.of() 方法和 Array.from() 方法相似,区别是定型数组的方法返回定型数组,而普通数组的方法返回普通数组;let view = Int16Array.of(1, 2); console.log(view instanceof Int16Array); // true
  • 定型数组中增加了 set() 与 subarray() 方法。 set() 方法用于将其他数组复制到已有定型数组, subarray() 用于提取已有定型数组的一部分形成新的定型数组。

类数组对象

  • 是一种类似数组的对象,比如对象中属性的读写、遍历操作obj[0] = 9;
  • 要求:使用从零开始,且自然递增的整数做键名,并且定义了length;没有 length 属性,则返回空数组;元素属性名不为数值,返回元素值为 undefined 的数组 ;
  • 但并不是类数组对象可以使用任何数组上的方法,如pop()、push()就不行;

扩展运算符

  • 复制数组
  • 合并数组[...[1, 2],...[3, 4]]

7.函数的扩展

《ECMAScript 6 入门》阅读笔记(未完结)_第5张图片
默认参数

  • 只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递。
  • 参数变量是默认声明的,所以不能用let或const再次声明!!!
  • 参数默认值可以与解构赋值的默认值,结合起来使用。
  • 通常情况下,定义了默认值的参数,应该是函数的尾参数,否则在调用函数时这个参数没法省略不写啊。

rest参数

  • …变量名,把几个参数转换成数组
  • rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
  • 扩展运算符可以理解为rest参数的逆运用,把数组展开成单个的元素

箭头函数

  • 参数 => 函数体
  • 如果函数体只有一条语句,可以将{}和return省略掉,如果语句为多条,则不可以省略{}和return
  • 当没有参数或有多个参数,要用 () 括起来
  • 当函数体省略{}和return时,若返回的是对象,需要将函数体{}用()括起来
  • 箭头函数不能用于构造函数
  • 箭头函数没有prototype属性
  • 箭头函数不绑定arguments
  • 箭头函数不绑定this,箭头函数内的this就是箭头函数外的那个this!因为箭头函数没有自己的this。
  • 箭头函数无法使用 call()或 apply()来改变其运行的作用域

尾调用

  • 函数体内调用某函数后不再做其他事情,只有一种可能,return tail(x);
  • 尾调用优化,即只保留内层函数的调用帧。只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧。尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

8.对象的扩展

《ECMAScript 6 入门》阅读笔记(未完结)_第6张图片
对象属性的简写

  • const person = {age: age, name: name} -> const person = {age, name};
  • sayHi:function(){...} -> sayHi(){...}
  • 如果是Generator 函数,则要在前面加一个*

属性表达式

  • ES6允许用表达式作为属性名,但是一定要将表达式放在方括号内,["he"+"llo"](){...}

对象的拓展运算符 …

  • 拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象。let person = {name: "Amy", age: 15}; let someone = { ...person };
  • 自定义的属性和拓展运算符对象里面属性的相同的时候:自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉。自定义的属性在拓展运算度前面,则变成设置新对象默认属性值。

对象的新方法

Object.assign(target, source_1, ···)

  • 将源对象的所有可枚举属性复制到目标对象中;若有同名属性,则后面的属性会覆盖前面的属性;若该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回;因为 null 和 undefined 不能转化为对象,所以会报错;
  • assign 的属性拷贝是浅拷贝,即如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用;
  • assign只拷贝自身对象的属性,若对象有继承,不会拷贝继承的属性,也不会拷贝不可枚举的属性
  • 数组处理:Object.assign([2,3], [5]);,将数组处理成对象,所以先将 [2,3] 转为 {0:2,1:3} ,然后再进行属性复制,最后得到[5,3]

Object.is(value1, value2)

  • 比较两个值是否严格相等,与(===)类似;
    Object.is(+0,-0); //false
    +0 === -0 //true
    Object.is(NaN,NaN); //true
    NaN === NaN //false
    Object.is([],[]); //false,数组为引用类型,虽然都为空,但是两者引用的是两个不同的地址

Object.entries()

  • 遍历对象的属性
    for(let {key,value} of Object.entries(test)){ console.log([key,value]);}

遍历对象属性常用的三种方法

  • for...in,不含Symbol属性
  • Object.keys(obj),不含Symbol属性,不含继承属性
  • Reflect.ownKeys(obj),含Symbol属性
let name = Symbol('name');
let product = {
    [name]:"洗衣机",
    "price":799
};

for(key in product){
    console.log(key);
}
for( key of Object.keys(product)){
    console.log(key);
}

for(key of Reflect.ownKeys(product)){
    console.log(key);
}

9.Symbol ES6中新增的数据类型 表示独一无二的值

Symbol

  • 是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object);
  • 凡是属性名属于Symbol类型,都是独一无二的,保证不会与其他属性名产生冲突;
  • 由于Symbol值不是对象,不能添加属性;

Symbol.for()

  • 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值;
  • Symbol.for()与Symbol()区别:Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值;

Symbol.keyFor()

  • 返回一个已登记的 Symbol 类型值的key,
    var s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo"
    var s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined

属性名的遍历《ECMAScript 6 入门》阅读笔记(未完结)_第7张图片

  • 对象中,通过for...inlet...of 都是拿不到对象的Symbol类型属性的;
  • 由Object.getOwnPropertySymbols()获取指定对象的所有 Symbol 属性名,返回一个数组;
  • Reflect.ownKeys()返回所有类型的键名,包括常规键名和 Symbol 键名,返回一个数组;

10.Map与Set

Set

  • 集合中没有重复的值;
  • add()
  • delete()
  • has()
  • clear()
  • size()
  • Set与Array互转
    new Set([4,5,6])
    [...set4]
  • keys(),values(),entries():由于Set结构没有键名,只有键值(键名和键值是同一个值),所以key方法和value方法的行为完全一致;
  • 数组去重方法
    let arr = [3, 5, 2, 2, 5, 5]; let unique = [...new Set(arr)];

《ECMAScript 6 入门》阅读笔记(未完结)_第8张图片

WeakSet

  • 不重复的值的集合不可遍历无size属性只能放置对象
  • WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用
  • add(value),delete(value) ,has(value),无clear()

Map

  • Map对象保存键值对
  • Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现
  • set()添加键,get()获取键,has()判断键,delete()删除键,clear()清除,size()
  • 遍历方法:keys(),values(),entries(),forEach()
  • Map与数组互转
    [...map.values()] [...map.entries()]
    new Map([[true, 7], [{foo: 3}, ['abc']]])
    Map与对象互转
    strMapToObj(myMap)
    objToStrMap({yes: true, no: false})
    Map与JSON互转
    strMapToJson(myMap)
    jsonToStrMap('{"yes":true,"no":false}')
  • 字符串true和布尔值true是两个不同的键
  • 对同一个键多次赋值,后面的值将覆盖前面的值
  • 读取一个未知的键,则返回undefined
  • Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键,解决了同名属性碰撞的问题

WeakMap

  • 只接受对象作为键名,键名所指向的对象,
  • WeakMap的设计目的:键名是对象的弱引用,不计入垃圾回收机制,对象可能会被自动回收,同时WeakMap自动移除对应的键值对,从而有助于防止内存泄漏
  • 无遍历操作(key()、values()、entries()、forEach()),无size属性,无法清空clear()
  • 四个方法:get()、set()、has()、delete()

Map 与Array的对比

//数据结构横向对比:增删改查

let map = new Map();
let array = [];

//add
map.set('t',1);
array.push({t:1});

console.log('add',map,array);

//find
//map返回的是true/false,array返回的是当前的查询对象
let map_exist = map.has('t');
let array_exist = array.find(item=>item.t);
console.log('find',map,array);

//change
map.set('t',2);
array.forEach(item => item.t ?item.t = 2 : '');
console.log('change',map,array);

//delete
map.delete('t');
let index = array.findIndex(item => item.t);
array.splice(index,1);
console.log('delete',map,array);

Set与Array的对比

//set && array

let set = new Set();
let array = [];

//add
set.add({t:1});
array.push({t:1});
console.log('add',set,array);

//find,
//如何判断Set集合中某对象是否存在? 要先保存对象,不能直接写`has({t:1})`
let item = {t:1};
set.add(item);
let set_exist = set.has(item);
let array_exist = array.find(item=>item.t);
console.log('find',set_exist,array_exist);

//change
set.forEach(item=>item.t?item.t=2:'');
array.forEach(item=>item.t?item.t=2:'');
console.log('change',set,array);

//delete
set.forEach(item=>item.t?set.delete(item):'');
let index = array.findIndex(item => item.t);
array.splice(index,1);
console.log('delete',set,array);

Map && Set && Object

//map和set在语义上优先,map使用成本最低

let item = {t:1};
let map = new Map();
let set = new Set();
let obj = {};
//add
map.set('t',1);
set.add(item);
obj['t'] = 1;
console.log('add',map,set,obj);

//find
let map_exist = map.has('t');
let set_exist = set.has(item);
let obj_exist = 't' in obj;
console.log('find',map_exist,set_exist,obj_exist);

//change
map.set('t',2);
item.t = 2;    //注意set的修改方式,对于Set结构,若存储了数据结构直接修改数据本身,否则只能通过forEach遍历依次做修改
obj['t'] = 2;
console.log('change',map,set,obj);


//delete
map.delete('t');
set.delete(item);
delete  obj['t'];
console.log('delete',map,set,obj);

1.优先使用Map
2.保证每个数据的唯一性,使用Set
3.尽量不用传统的数组和Object


11.Proxy和Reflect

Proxy

  • 在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,从而对外界的访问进行过滤和改写,理解为”代理器“
  • get() ,拦截某个属性的读取操作
  • set(),拦截某个属性的赋值操作
  • apply(),拦截函数的调用
  • has(),拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效
  • construct(),拦截new命令
  • deleteProperty(),拦截delete操作
  • defineProperty(),拦截添加新属性操作
  • getOwnPropertyDescriptor(),拦截Object.getOwnPropertyDescriptor
  • getPrototypeOf(),拦截Object.getPrototypeOf()运算符
  • isExtensible(),拦截Object.isExtensible操作
  • ownKeys(),拦截Object.keys()操作
  • preventExtensions(),拦截Object.preventExtensions()
  • setPrototypeOf(),拦截Object.setPrototypeOf方法
"use strict";

let obj = {
    time :"2019-05-10",
    name:'net',
    _r:123
};

let monitor = new Proxy(obj,{
    //拦截对象属性的读取
    get(target,key){
        return target[key].replace('2019','2018');
    },

    //拦截对象设置属性
    set(target,key,value){
        if(key === 'name')
        {
            return target[key] = value;
        }
        else{
            return target[key];
        }
    },
    //拦截key in object操作
    has(target,key){
        if(key === 'name'){
            return target[key];
        }
        else{
            return false;
        }
    },
    //拦截delete
    deleteProperty(target, key) {
        if(key.indexOf('_') === 0)
        {
            delete target[key];
            return true;
        }else{
            return target[key];
        }
    },
    //拦截Object.keys
    ownKeys(target) {
        return Object.keys(target).filter(item=>item!='time');
    }

});

//用户访问的是monitor,不是obj,由monitor对obj进行操作,Proxy相当于一个代理器
monitor.time = '2017';
console.log(monitor.time);
console.log('has','name' in monitor);
delete monitor.time;
console.log('delete',monitor.time);
delete monitor._r;
console.log(monitor);
console.log('ownKeys',Object.keys(monitor));

Proxy 使用场景

  • 数据校验(判断格式是否正确)

Reflect

{
    let obj = {
        time :"2019-05-10",
        name:'net',
        _r:123
    };
    console.log("Reflect",Reflect.get(obj,'time'));
    Reflect.set(obj,'name','nukewang');
    console.log(obj);
    console.log('has',Reflect.has(obj,'name'));
}

12.ES6 class

《ECMAScript 6 入门》阅读笔记(未完结)_第9张图片
JS传统方法是通过构造函数定义类

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

ES6中,通过关键字class定义类,注意class只是语法糖,本质还是函数

class Point {
  constructor(x, y) {
    //this代表实例对象
    this.x = x;
    this.y = y;
  }
 //定义类方法时,前面不需要加`function`关键字,方法之间无需逗号分隔
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

类继承

{
    class Parent{
        //构造方法,返回实例对象
        constructor(name='zqk'){
            this.name = name;
        }
    }
    //   继承
    class Child extends  Parent{
        constructor(name='child'){
            //继承传递参数,且super()要放在第一行,否则报错
            super(name);
            this.type = 'zqk-child';
        }
    }
    console.log('继承',new Child());
}

getter && setter

{
    //   类
    class Parent{
        //构造方法,返回实例对象
        constructor(name='zqk'){
            this.name = name;
        }
        //getter: 注意这里是属性,不是方法
        get longName(){
            return 'mk'+this.name;
        }
        set longName(val){
            this.name = val;
        }
    }
    let v = new Parent();
    console.log('getter',v.longName);
    v.longName = 'hello';
    console.log('setter',v.longName);
}

静态属性和静态方法

{
    class Parent{
        //构造方法,返回实例对象
        constructor(name='zqk'){
            this.name = name;
        }
        //    静态方法 , 通过类去调用而不是通过类的实例去调用
        static tell(){
            console.log('tell');
        }
    }
    //定义静态属性
    Parent.type = 'test';
    console.log('静态属性',Parent.type);
    Parent.tell();
}

13.ES6 Promise 实现异步编程,避免层层嵌套的回调函数

使用回调函数和使用Promise

{
    let ajax = function (callback) {
        console.log('执行');
        setTimeout(function () {
            callback && callback.call()
        },1000);
    }
    ajax(function () {
        console.log('timeout');
    })
}
{
    //返回Promise对象
    let ajax = function () {
      console.log('执行2');
      return new Promise(function (resolve,reject) {
          setTimeout(function () {
              resolve();
          },1000);
      })
    };
    ajax().then(function () {
        console.log('promise','timeout2');
    })
}

Promise嵌套使用

{
    let ajax = function () {
        console.log('执行3');
        return new Promise(function (resolve,reject) {
            setTimeout(function () {
                resolve();
            },1000);
        })
    };
    ajax()
        .then(function () {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve();

            },5000);
        })
    })
        .then(function () {
        console.log('timeout3');
    })
}

catch捕获错误

{
    let ajax = function (num) {
        console.log('执行4');
        return new Promise(function (resolve, reject) {
            if(num>5){
                resolve();
            }
            else{
                throw new Error('出错了');
            }
        })
    }
    ajax(4).then(function () {
        console.log('log',6);
    }).catch(function (err) {
        console.log('catch',err);
    })
}

Promise.all()

{
    //三张图加载完成后,再添加到页面
    function loadImg(src) {
        return new Promise((resolve, reject) => {
            let img = document.createElement('img');
            img.src = src;
            img.onload = function () {
                resolve(img);
            }
            img.onerror  = function (err) {
                reject(err);
            }
        })
    }
    function showImgs(imgs) {
        imgs.forEach(function (img) {
            document.body.appendChild(img);
        })
    }
    //把多个Promise实例当做一个Promise实例
    Promise.all([
        loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
        loadImg('http://i4.buimg.com/567571/2b07ee25b08930ba.png'),
        loadImg('http://i4.buimg.com/567571/5eb8190d6b2a1c9c.png')
    ]).then(showImgs);
}

Promise.race()

{

    function loadImg(src) {
        return new Promise((resolve, reject) => {
            let img = document.createElement('img');
            img.src = src;
            img.onload = function () {
                resolve(img);
            }
            img.onerror  = function (err) {
                reject(err);
            }
        })
    }
    function showImgs(img) {
        let p = document.createElement('p');
        p.appendChild(img);
        document.body.appendChild(p);
    }
    //race(),用法同all()一样,只要有一张图片加载成功就显示到页面上
    Promise.race([
        loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
        loadImg('http://i4.buimg.com/567571/2b07ee25b08930ba.png'),
        loadImg('http://i4.buimg.com/567571/5eb8190d6b2a1c9c.png')
    ]).then(showImgs);
}

14.Iterator(遍历器) 和 for…of循环

数据集合

  • Array、Object、Map、Set
  • 以上四种数据结构原生具备Iterator接口

Iterator接口

  • 为各种不同的数据结构提供统一的访问机制,即for…of循环
  • 任何数据结构只要部署Iterator接口,有Symbol.iterator属性,就可以完成遍历操作
    《ECMAScript 6 入门》阅读笔记(未完结)_第10张图片《ECMAScript 6 入门》阅读笔记(未完结)_第11张图片

自定义Iterator接口

{
    //自定义Symbol.iterator接口
    let obj = {
        start:[1,3,2],
        end:[7,8,9],
        [Symbol.iterator](){
            let self = this;
            let index = 0;
            let arr = self.start.concat(self.end);
            let len = arr.length;
            return {
                next(){
                    if(index

15.Generator 函数 异步编程

Generator

  • Generator函数是一个状态机,返回一个遍历器对象(Symbol.Iterator),代表Generator函数的内部指针,必须调用遍历器对象的next方法,使得指针移向下一个状态
  • function关键字与函数名之间有一个*号;二是,函数体内部使用yield语句,定义不同的内部状态

由Generator函数返回一个遍历器对象

{
    let obj = {};
    obj[Symbol.iterator] = function* () {
        yield  1;
        yield  2;
        yield  3;
    }
    
    for(let val of obj){
        console.log('val',val);
    }
}

Generator状态机

{
    let state = function* () {
        while(1){
            yield 'A';
            yield 'B';
            yield 'C';
        }
    }
    let status = state();
    console.log(status.next());
    console.log(status.next());
    console.log(status.next());
    console.log(status.next());
    console.log(status.next());
}

Generator的实例案例

{
    //实例案例  抽奖次数的限制,不再把次数限制保存在全局变量,避免别人修改了次数,从服务端传参进来,增加了安全性。
    let draw = function (count) {
        console.info(`剩余${count}次`);
    }

    let residue = function* (count) {
        //用if来判断的话只能输出一次,因为用if的话相当于只有一个状态, 第一次star.next();后就没有第二个状态可获取了,done = true;
        while(count>0){
            count--;
            yield draw(count);
        }
    }

    let star = residue(5);
    let btn = document.createElement('button');
    btn.id = 'start';
    btn.textContent = '抽奖';
    document.body.appendChild(btn);
    document.getElementById('start').addEventListener('click',function () {
        star.next();
    },false);
}
{
    //长轮询:用于数据定期更新,结合Promise 从服务端的获取状态。
    let ajax = function* () {
        yield  new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve({code:0});
            },200);
        })
    }
    let pull = function () {
        let generator = ajax();
        let step = generator.next(); //返回一个Promise的实例
        step.value.then(function (d) {
            if(d.code != 0){
                setTimeout(function () {
                    console.log('wait');
                    pull();
                },1000);
            }else{
                console.log(d);
            }
        })
    }

    //进行了一次轮询
    pull();
}

16.ES6 修饰器Decorator

  • 修饰器(Decorator)是一个函数,用来修改类的行为
  • 修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时
  • 修饰器函数的第一个参数target,就是所要修饰的目标类
  • 若同个方法有多个修饰器,先从外到内进入,然后由内向外执行
  • 修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升
  • 安装第三方修饰器的js库:npm install core-decorators

用法一,类的修饰,在类外面使用,添加类的静态属性和实例属性

{
    let typename = function (target) {
        target.myname = 'hello';
        target.prototype.isTestable=true;
    }
    @typename
    class Test{

    }
    let obj = new Test();
    console.log('添加类的实例属性',obj.isTestable);
    console.log('类修饰符',Test.myname);
}

用法二:类的方法的修饰,在类里面引入修饰器,修改time()行为

{
    //修饰器
    //target是所要修饰的目标类对象,name是所要修饰的属性名,descriptor是该属性的描述对象
    let readonly = function (target,name,descriptor) {
        descriptor.writable = false;
        return descriptor
    }
    class Test{
        //修饰器设置不允许修改
        @readonly
        time(){
            return '2019-05-11'
        }
    }
    let test = new Test();
    test.time = function(){   //报错
        console.log('reset time');
    }
    console.log(test.time());
}

用法三,埋点,做日志统计 ,埋点分析,是网站分析的一种常用的数据采集方法

{
    let log=(type)=>{
        return function(target,name,descriptor){
            let src_method=descriptor.value;
            descriptor.value=(...arg)=>{
                src_method.apply(target,arg);
                console.info(`log${type}`);//实现埋点,真实的业务中直接换成一个接口就可以了
            }
        }
    };

    class AD{
        @log('show')
        show(){
            console.log('adisshow');
        };
        @log('click')
            click(){
            console.log('adisclick');
        }
    }
    let ad=newAD();
    ad.show();
    ad.click();
}

17. ES6 模块化 module

  • ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
// lesson1.js
let A = 123;
function test() {
    console.log('test');
}
class Hello{
    test(){
        console.log('class');
    }
}

export default {
    A,
    test,
    Hello
}
import Lesson1 from './class/lesson1';
console.log(Lesson1.A);

ES6面试题归总

ES6面试题总结(2018-06-22)
关于 ES6 中 Promise 的面试题

  • ES6 Promise 用法讲解

说说ES6那些事儿–ES6十问


ES6 Promise中较难理解的题

《ECMAScript 6 入门》阅读笔记(未完结)_第12张图片

var urls = [
    'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg',
    'https://www.kkkk1000.com/images/getImgData/gray.gif',
    'https://www.kkkk1000.com/images/getImgData/Particle.gif',
    'https://www.kkkk1000.com/images/getImgData/arithmetic.png',
    'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif',
    'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg',
    'https://www.kkkk1000.com/images/getImgData/arithmetic.gif',
    'https://www.kkkk1000.com/images/wxQrCode2.png'];
function loadImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function () {
            console.log('一张图片加载完成');
            resolve();
        }
        img.onerror = reject;
        img.src = url;
    })
};

function  limitLoad(urls,handler,limit) {
    const  sequence = [].concat(urls);
    let promises = [];

    promises = sequence.splice(0,limit).map((url,index)=>{
        return handler(url).then(()=>{
            //promises数组中保存的就是返回的索引值
            return index;
        })
    });

    //reduce 为数组中的每一个元素依次执行回调函数
    //array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
    //total,currentValue参数必须存在,currentIndex, arr为可选参数
    //initialValue 作为第一次调用 callback 的第一个参数

    //Promise.all将多个Promise实例包装成一个新的Promise实例。
    // 成功时返回一个结果数组,而失败则返回最先被reject失败状态的值。
    return sequence.reduce(
        (last,url,currentIndex)=>{
        //    需要给reduce()定义一个初始化值initialValue,使reduce()从数组中currentIndex=0的元素开始处理,
            //    另外回调中未使用到last,可以随意定义,只是为了让回调从数组第一个元素开水处理
        return Promise.resolve()
            .then(()=>{
                return Promise.race(promises);
            })
            .catch(err=>{
                console.log(err);
            })
            .then(res=>{
                promises[res] = handler(sequence[currentIndex]).then(()=>{
                    return res;
                })
            })
        },0)
}
limitLoad(urls,loadImg,3);

你可能感兴趣的:(前端)