ES6学习笔记

ES6学习笔记

在学习ES6的过程中做的一些记录,用于未来的快速回忆。

let&const

作用域的概念

  • ES6新增块级作用域的概念(一个大括号括起来的部分就是一个块作用域)
  • let与const用于在块级作用域中声明变量,该变量仅在当前块级作用域有效
  • ES6强制启用严格模式,变量未声明不允许使用

如何使用let与const

  • let关键词用于声明变量
  • const关键词用于声明常量,声明时必须赋值
  • let&const关键词不能在同一个块级作用域内重复声明一个变量
  • const定义的基本类型常量不能被修改,但是引用类型常量内部的值能被修改,只要不改变常量的引用即可

解构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值。

数组解构赋值

// 完全解构
//本质上这种写法属于‘模式匹配‘,只要等号两边的模式相同,左边的变量就会被赋予对应的值
let [a,b,c] = [1,2,3];        // a=1,b=2,c=3
let [a,[b]] = [1, [2]];        // a=1,b=[2]
let [,,a] = [1,2,3];        // a=3
let [d,...rest] = [1,2,3,4,5,6];    // d=1,rest=[2,3,4,5,6]
// 解构不成功
// 如果解构不成功,变量的值就等于undefined
let [x,y] = ['xy']    //x='xy',y=undefined
// 不完全解构
let [i,j] = [1,2,3];    // i=1,j=2
//如果等号的右边不是数组,或者说不是可遍历的结构,那么将会报错
let [a] = 1;    // 这里会报错:Uncaught TypeError: 1 is not iterable

默认值

解构赋值允许有默认值。

let [x,y='b'] = ['a'];
console.log(y); //blet [x,y = 'b'] = ['a',undefined];
console.log(y); //b 
// 数组成员为undefined时,默认值仍会生效
// 因为在ES6内部使用严格相等运算符‘===‘,判断一个位置是否有值,
// 所以当一个数组成员严格等于undefined,默认值才会生效。let [x,y = 'b'] = ['a',null];
console.log(y); //null
// 如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined

对象解构赋值

对象的解构与数组有一个重要的不同,数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

// 变量名与属性名一致的情况下
let {foo,bar} = {foo : "aaa",bar : "bbb"}
console.log(foo); //aaa
console.log(bar); //bbb
// 实际上 对象的解构赋值是以这样的形式简写的
let {foo : foo ,bar : bar} = {foo : "aaa",bar : "bbb"}
// 变量名与属性名不一致的情况下,必须这样写
let {a : name, b : age} = {a : 'zhangsan', b : 33};
console.log(name); //zhangsan
console.log(age);  //33

对象的解构赋值的内部机制,是先找到同名属性,然后再赋值给对应的变量,真正被赋值的是后者,而不是前者,第一个foo/bar 是匹配的模式,对应的foo/bar属性值才是变量,真正被赋值的是属性值(也就是第二个foo/bar)。

字符串解构赋值

const [a,b,c,d,e] = 'hello';
console.log(a); //h
console.log(b); //e
console.log(c); //l
console.log(d); //l
console.log(e); //olet { length : len} = 'yahooa';
console.log(len); 
//类似数组的对象都有一个length属性,还可以对这个属性解构赋值

布尔值/数值解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象,但是等号右边为undefined 和 null时无法转为对象,所以对他们进行解构赋值时,都会报错。

let {prop : x } = undefined;
console.log(x);
//报错:Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'

函数参数解构赋值

function move({x = 0,y = 0} = { }){return [x,y];}
console.log(move({x : 3,y : 4})); //[3,4]
console.log(move({x : 3})); //[3,0]
console.log(move({})); //[0,0]
console.log(move()); //[0,0]
// move()的参数是一个对象,通过对这个对象进行解构,得到变量x、y的值,如果解构失败,x和y 等于默认值
function move2({x,y} = {x : 1, y : 2 }){return [x,y];
}
console.log(move2({x : 6,y : 8})); //[6,8]
console.log(move2({})); //[undefined,undefined]
console.log(move2()); //[1,2]
// move2() 是为函数move的参数指定默认值,而不是为变量x和y指定默认值,
// 所以与前一种写法的结果不太一样,undefined 就会触发函数的默认值

解构作用

  • 改变变量的值

    let x = 1;
    let y = 2;
    [x,y] = [y,x];    // x=2, y=1
  • 从方法返回多个值

    function example(){return {foo : 'a',bar : 'b'}
    } 
    let {foo,bar} = example();    // foo='a',bar='b'
    function example222(){return [1,2,3,4,5];
    } 
    let [a,,...b] = example222();    // a=1, b=[3,4,5]
  • 函数参数的定义

    //参数是一组有次序的值
    function example([x,y,z]){return x + y + z;
    }
    example([1,2,3])
    console.log(example([1,2,3])); //6
    //参数是一组无次序的值
    function f({x,y,z}){return x + y + z;
    }
    f({x : 'a', z : 'b', y : 'c' });
    console.log(f({x : 'a', z : 'b', y : 'c' })); //acb
  • 提取JSON数据
  • 函数参数的默认值
  • 输入模块的指定用法

正则扩展

构造函数的变化

// ES5 的方式
let reg = new RegExp('xyz', 'i');
let reg2 = new RegExp(/xyz/i);
reg.test('xyz123');        // true
reg.test('xyz123');        // true
// ES6 方式的变化,可以有第二个参数用作修饰符
// 这种方式,第二个参数的修饰符会覆盖第一个正则表达式的修饰符
let reg3 = new RegExp(/xyz/ig, 'i')
reg3.flags        // i

u修饰符

// u -> unicode,用于处理unicode编码的正则匹配
// unicode中4个字节 = 一个字母
/^\uD83D/.test('\uD83D\uDC2A');         // true \uD83D\uDC2A 被当做两个字母来处理
/^\uD83D/u.test('\uD83D\uDC2A');    // false,\uD83D\uDC2A 被当作一个字母来处理
// 正则中,大括号里面若是unicode编码,需添加u修饰符才能被识别
/\u{61}/.test('a');        // false
/\u{61}/u.test('a');    // true
// 用'.'操作符识别大于两个字节的字符需要加上u修饰符
// '.'操作符不能处理换行符/回车符/行分隔符/段分隔符
/^.$/.test('吉');    // false
/^.$/u.test('吉');    // true
/吉{2}/.test('吉吉');    // false
/吉{2}/u.test('吉吉');    // true
// 凡是超过两个字节的匹配都需要添加 u 修饰符

y修饰符

let s = 'bbb_bb_b';
let a1 = /b+/g;
let a2 = /b+/y;
a1.exec(s);        // g修饰符全局匹配,不强调从下一个字符开始匹配,只要接下来的字符能匹配成功就ok
a2.exec(s);        // y修饰符全局匹配,必须紧跟着下一个字符开始匹配
// sticky属性用于判断是否开启了y修饰符
a1.sticky;        // false
a2.sticky;        // true

s修饰符

未实现,仅作为提案

// '.'操作符不能处理换行符/回车符/行分隔符/段分隔符
// 但添加 s 修饰符就可以

字符串扩展

Unicode表示法

console.log(`\u0061`);            // a
console.log(`\u20887`);            // []7,会将20087拆成2008 / 7
console.log(`\u{20887}`);        // ?, 大括号括起来后,将作为一个字符处理// 每两个字节为一个单位长度
// ES5 不能很好的处理长度超过两个字节的字符
let s = '?';
s.length;        // 2
'a'.length;        // 1
s.codePointAt(0);        // 可以很好的处理长度超过两个字节的字符的码值,将字符转为码值
String.fromCodePoint("0x20087");    // 将码值转为字符

遍历接口

let str "\u{20bb7}abc";
// ES5的遍历方式,不能很好的处理长度超过两个字节的字符
for(let i=0; i<str.length; i++) {console.log(str[i]);
}
// 新的遍历接口,可以自动处理任何字符
for (let code of str) {console.log(code);
}

模板字符串

let name = 'list';
let info = 'hello world';
let meg = `This is ${name}, ${info}`;
console.log(meg);

数值扩展

新增方法

Number.isFinite();        // 判断是否为有穷数
Number.isNaN();            // 判断是否不是数字
Number.isInteger();        // 判断是否为整数 , 25/25.0均为true,25.1为false,参数必须为数值
Number.MAX_SAFE_INTEGER        // 数的上限,ES5
Number.MAX_SAFE_INTEGER        // 数的下限,ES5
Number.isSafeInteger();    // 判断一个数是否在有效范围内
Math.trunc();            // 取整数部分
Math.sign();            // 正数返回1,负数返回-1,0返回0
Math.cbrt();            // 求立方根
// 还补充了三角函数方法、对数方法

方法调整

将ES5中某些全局方法移到对象下面,如parseInt()由全局移动到Number对象。

数组扩展(新增特性)

Array.from

// 从类数组中创建数组
// 如页面中有一堆 p 标签
let p = document.querySelectorAll('p');
let pArr = Array.from(p);
// 此时,将集合p转为了数组pArr// 将数组中的元素按照function的规则处理后并返回一个数组
Array.from([1,3,5], function(item){return item*2});        // [1,6,10]

Array.of

let arr = Array.of(1,2,3,4,5);        // arr=[1,2,3,4,5]
let emptyArr = Array.of();            // emptyArr=[]\

copyWithin

let arr = [1,2,3,4,5].copyWithin(0, 3, 5);
// arr = [4, 5, 3, 4, 5]
// 将起始位置3到终止位置5的元素覆盖掉从0开始的元素(不包括下标为5的元素)
let arr2 = [1,2,3,4,5].copyWithin(0, 1, 5);
// arr2 = [2, 3, 4, 5, 5];

find/findIndex

let val = [1,2,3,4,5,6].find(function(item){return item>3});
// val = 4
// find找到一个符合条件的元素就结束,并返回该元素的值
let index = [1,2,3,4,5,6].findIndex(function(item){return item>3});
// index = 3
// findIndex找到一个符合条件的元素就结束,并返回该元素的下标

fill

let arr = [1,'a',undefined];
arr.fill(6);                    // arr=[6,6,6]
// 将起始位置1到终止位置3的元素替换成5(不包括下标为3的位置)
arr.fill(5,1,3);                // arr=[6,5,5]

entries/keys/values

for (let index of [1,2,3].keys()){console.log(index);                    // 0,1,2
}
for (let value of [1,2,3].values()){console.log(value);                    // 1,2,3
}
for (let [index,value] of [1,2,3].entries()){console.log(index, value);            // 0,1;1,2; 2,3
}

includes

let a = [1,2,NaN].includes(1);        // a = true
let b = [1,2,NaN].includes(NaN);    // b = true

函数扩展

参数默认值

// 有默认值的参数右边的参数都需要有默认值
function test(x, y='hello world') {console.log("默认值:", x, y);
}
test('hhh');            // 触发默认值
test('hhh', 'aaa');        // 不触发默认值
// 默认参数的作用域
let x = "test";
function test(x, y=x) {console.log(x, y);
}
function test2(c, y=x) {console.log(x, y);
}
test("hhh");        // hhh hhh
test2("hhh");        // hhh test
// 采用这种方式传递默认值时,要注意等号右边变量的作用域
// 等号右边的变量的值与最近定义的同名变量的值相同

rest参数

// rest参数后不能有其他参数
function test3(...arg) {for(let v of arg) {console.log('rest:', v)}
}

扩展运算符

// 扩展运算符会将解构数据
console.log(...[1,2,3]);            // 1;2;3
console.log("a", ...[1,2,3]);        // a;1;2;3

箭头函数

// 单个参数时可以不使用括号,多个参数时需要使用括号将其括起来
// 函数体有多个语句时,使用大括号括起来
let arrow = v => v*2;
arrow(3);                    // 6let arrow2 = () => 5;
arrow2();                    // 5

尾调用

// 函数内部的最后一句是另一个函数
// 尾调用可以提升JS的性能
// 下面例子中,fx()就实现了尾调用
function tail(x) {console.log(x);
}
function fx(x) {return tail(x);
}
fx(123);        // 123

对象扩展(新增特性)

简洁表示法

// 当对象中的属性名与变量名相同时,可以只写一个
// 对象里面有方法,在ES6中可以省略function关键字
let o = 1;
let k = 2;
let obj = {o,k,hello () {console.log('hhh');}
}

属性表达式

// ES5中,对象中的key是固定的
// ES6中,key是可以用表达式或者变量的
let a = 'b';
let es5_obj = {        // {a:c}a: 'c'
}
let es6_obj = {        // {b:c}[a]: 'c'
}

扩展运算符(ES7提案)

// 不建议使用,支持不好
let {a,b,...c} = {a:'1', b:'2', c:'3', d:'5'};
// a = 1
// b = 2
// c = {c:'3', d: '5'}

Object新增方法

Object.is('abc', 'abc');            // 与===的功能相同
Object.assign({a:'1'}, {b:'2'});    // 将第二个对象的内容追加到第一个对象里,浅拷贝
// 只拷贝自身的数据,不拷贝继承的属性以及不可枚举的属性// 遍历对象
let obj = {a:'1', b:'2'};
for(let [key, value] of Object.entries(test)) {console.log([key, value]);
}

Symbol

Symbol概念

这种数据类型提供一个独一无二的值。

Symbol的作用

  • 声明

    // 方法1
    let a1 = Symbol();
    let a2 = Symbol();
    console.log(a1 === a2);        // false
    // 方法2
    // 在生成a3前,会检查‘a3’在全局是否存在
    // 若存在,则为取值
    // 若不存在,则为生成一个新的Symbol
    let a3 = Symbol.for('a3')
  • 作用

    // 可用于处理对象里面的重名,防止冲突
    let a1 = Symbol.for('abc');
  [a1]: '123','abc': '5','c': '6'

}
// 常规循环只能处理非Symbol值
for(let [key, value] of Object.entries(obj)) {

  console.log(key, value);            // {abc:'5', c:'6'}

}
// 这种方式只能处理Symbol值
Object.getOwnPropertySymbol(obj).foreach(function(item) {

  console.log(obj[item]);                // 123

})
// 这种方式能够处理以上两种情况
Reflect.ownKeys(obj).foreach(function(item) {

  console.log(obj[item]);                // 123,5,6

})`

Map & Set 数据结构

Set

  • Set中的元素是不能重复的
  • Set可用于数组去重
  • Set里面不会自动数据类型转换
let list = new Set();
list.add(5);    // 5
list.add(6);    // 5,6
list.size;        // 2let arr = [1,2,3,4,5];
let list2 = new Set(arr);
list2.size;        // 5let list3 = new Set([2, '2']);        // 2, '2'// 几个Set常用方法
let set = new Set([1,2,3]);
set.add(5);        // 添加元素
set.has(2);        // 判断是否有某个元素
set.delete(3);    // 删除元素
set.clear();    // 清空Set// 遍历Set
let set2 = new Set([2, '2']);
// 使用keys()与values()的效果一样,因为Set中键值一致
// 不使用keys()、values(),直接遍历Set也可以
// 也可以使用entries()
for(let key of set2.keys()) {console.log(key);            // 2, '2'
}
for(let value of set2.values()) {console.log(value);            // 2, '2'
}
for(let val of set2) {console.log(val);            // 2, '2'
}
for(let [key,value] of set2.values()) {console.log([key, value]);    // 2, '2'
}

WeakSet

  • WeakSet的元素只能是对象
  • WeakSet存储的是弱引用,且不会检测是否被垃圾回收机制回收
let weakList = new WeakSet();
let arg = {};
weakList.add(arg);        // arg
// WeakSet没有clear方法、没有size属性、不能遍历

Map

  • 任意数据类型都可以作为Map的数据类型
let map = new Map();
let arr = ['123'];
map.set(arr, 123);        // ['123']=>123
map.get(arr);            // 123// Map可以接收一个二维数组作为参数
let map2 = new Map([['a',123],['b',456]]);
console.log(map2);        // 'a'=>123, 'b'=>456
map2.size;                // 2
map2.delete('a');        // 'b'=>456
map2.clear();            // 清空Map

WeakMap

  • WeakMap的key只能是对象
  • WeakMap没有clear方法、没有size属性、不能遍历
  • WeakMap存储的是弱引用,且不会检测是否被垃圾回收机制回收
let weakMap = new WeakMap();
let arg = {};
weakMap.set(arg, 123);
weakMap.get(arg);            // 123

Map和Array的对比

let map = new Map();
let array = [];
// add
map.set('t', 1);        // 't'=>1
array.push({t:1});        // {t:1}
// query
let map_exist = map.has('t');                    // true
let array_exist = array.find(item=>item.t);        // {t:1}
// modify
map.set('t', 2);                                // 't' => 2
array.forEach(item => item.t?item.t=2:'');        // {t:2}
// delete
map.delete('t');
let index = array.findIndex(item => item.t);
array.splice(index, 1);

Set和Array的对比

let set = new Set();
let arr = [];
// add
set.add({t:1});
arr.push({t:1});
// query
let set_exist = set.has({t:1});                // false,因为是不同的引用
let arr_exist = array.find(item=>item.t);    // {t:1}
// modify
set.forEach(item => item.t?item.t=2:'');    // 若直接add,则不能修改成功,因为引用不同
arr.forEach(item => item.t?item.t=2:'');    // 这里遍历是确保{t:1}存在
// delete
set.forEach(item => item.t?delete(item):'');
let index = array.findIndex(item => item.t);
array.splice(index, 1);

Map、Set、Object的对比

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;
// query
let map_exist = map.has('t');
let set_exist = set.has(item);
let obj_exist = 't' in obj;
// modify
map.set('t', 2);
item.t = 2;                // 这里,set存储的是引用,因此对对象的修改也会修改set里面对应的值
obj['t'] = 2;
// delete
map.delete('t');
set.selete(item);
delete obj['t'];

Proxy与Reflect

Proxy的使用

// Proxy为代理,在用户和对象之间设置代理,防止用户直接操作对象
// 同时,可以在代理上做一些操作来修改查询到的内容
// 或者限制用户对某些属性的修改行为
let obj = {time: '2018-11-21',name: 'net',_r: 123
};
let monitor = new Proxy(obj, {// 拦截对象属性的读取// 这里,当查询的value中有2018字符串时将其替换成2019get (target, key) {return target[key].replace('2018', '2019');        // 将2018替换成2019},// 拦截对象属性的修改// 这里只允许对name的修改set (target, key, value) {if(key === 'name') {return target[key] = value;} else {return target[key];}},// 拦截key in obj操作,限制暴露哪些属性// 此处只允许判断是否存在name这个key,对于其它key一律返回falsehas (target, key) {if (key === 'name') {return target[key];} else {return false;}},// 拦截delete// 这里只允许删除以下划线开头的属性deleteProperty (target, key) {if (key.indexOf('_') > -1) {delete target[key];return true;} else {return target[key];}},// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames// 这里将time属性属性过滤掉ownKeys (target) {return Object.keys(target).filter(item => item!='time')}
});
monitor.time;            // 2019-11-21
monitor.time = '2015-11-21';        // 2018-11-21
monitor.name = 'Chung';                // Chung
'name' in monitor;            // true
'time' in monitor;            // false

Reflect的使用

// 使用Reflect有以下好处 
// 1.更加有用的返回值
// 2.函数操作,如判断一个属性是否在该obj里面, Reflect.has(obj, key)
// 3.更加可靠的函数式执行方式
// 4.可变参数形式的构造函数
// 5.控制访问器或者读取器的this
// Reflect用于代替直接操作对象
// 建议使用Reflect来操作对象
let obj = {time: '2018-11-21',name: 'net',_r: 123
};
Reflect.get(obj, 'time');     // 2018-11-21
Reflect.set(obj, 'name', 'Chung');    // name: 'Chung'
Reflect.has(obj, 'name');     // true

使用场景

// 使用Proxy和Reflect实现解耦的数据校验
function validator (target, validator) {return new Proxy(target, {_validator: validator,set (target, key, value, proxy) {if (target.hasOwnProperty(key)) {let val = this._validator[key];if(!!val(value)) {return Reflect.set(target, key, value, proxy);} else {throw Error(`Can not set ${key} to ${value}!`);}} else {throw Error(`${key} not exist!`)}}})
}const personValidator = {name (val) {return typeof val === 'string';},age (val) {return typeof val === 'number' && val > 18;}
}class Person {constructor (name, age) {this.name = name;this.age = age;return validator(this, personValidator)}
}const person = new Person('hh', 18);
console.info(person);       // {name: 'hh', age: '18'}
person.name = 20;           // 不能设置为20,因为 personValidator 限制name的值只能是string

类和对象

基本定义和生成实例

// 定义类
class Person {constructor (name='Jarry') {this.name = name;}
}
// 生成实例
let person = new Person('Chung');
console.log(person);      // Person {name: ‘Chung’}

继承

// 定义类
class Person {constructor (name='Jarry') {this.name = name;}
}
// 使用extends继承
class Student extends Person {constructor (name='Sunny') {super(name);    // 将子类的参数传递给父类// 若子类有自己的属性,则需要将自有属性放在super()下面}
}
// 生成实例
let stu = new Student();
console.log(stu);   // Student {name: 'Sunny'}

Getter 和 Setter

// 定义类
class Person {constructor (name='Jarry') {this.name = name;}// longName是属性而不是方法get longName () {return 'Good ' + this.name;}set longName (value) {this.name = value;}
}
// 生成实例
let person = new Person();
person.longName;          // 'Good Jarry'
person.longName = 'Chung';    // 'Good Chung'

静态属性和静态方法

// 定义类
class Person {constructor (name='Jarry') {this.name = name;}// 使用static关键字定义静态方法static tell () {console.log('hello');}
}
// 定义完类后再定义静态属性
Person.age = '18';// 调用静态方法,使用类名调用而非实例调用
Person.tell();      // 'hello'
Person.age;         // 18

Promise

  • Promise是异步编程的一种解决方案

Promise基本使用

// 以回调函数处理异步
let ajax = function(callback) {console.log('执行1');setTimeout(function() {callback && callback.call();}, 1000)
}
ajax(function() {console.log('Timeout One');
})// 以Promise处理异步
let ajax = function() {console.log("执行2");return new Promise(function(resolve, reject) {setTimeout(function() {resolve();}, 1000)})
}
ajax().then(function() {console.log('Timeout Two');  
})
// 上面的结果为:
// 执行1
// 执行2
// Timeout One
// Timeout Two

Promise使用场景

// 所有图片加载完再添加到页面
function loadImg(src) {return new Promise((resolve, reject) => {let img = document.createElement('img');img.src = src;img.onload = function () {resolve(img)}img.onerror = function () {reject(err)}})
}function showImgs(imgs) {imgs.forEach(function(img) {document.body.append(img)})
}
// all 将多个promise当作一个,全部加载完后才执行resolve
Promise.all([loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),loadImg('http://i4.buimg.com/567571/df1ef0720bea6833.png')
]).then(showImgs)
// race, 数组中某一个执行完就马上执行resolve
Promise.race([loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),loadImg('http://i4.buimg.com/567571/df1ef0720bea6833.png')
]).then(showImgs)

Iterator

  • for...of本质上是使用Iterator接口
let arr = [1,2];
let map = arr[Symbol.iterator]();
map.next();         // {value:1, done:false}
map.next();         // {value:2, done:false}
map.next();         // {value:undefined, done:true}// 为对象实现iterator接口并指定遍历方式
let obj = {start: [1,2,3],end: [7,8,9],[Symbol.iterator] () {let seft = this;let index = 0;let arr = seft.start.concat(self.end);let len = arr.length;return {next () {if (index < len) {return {value: arr[index++],done: false}} else {return {value: arr[index++],done: true}}}}}
}for(let key of obj) {console.log(key);         // 1,2,3,7,8,9
}

Generator

Generator基本定义与使用

let tell = function* () {yield 'a';yield 'b';return 'c';
}
let k = tell();
k.next();         // {value:'a', done:false}
k.next();         // {value:'b', done:false}
k.next();         // {value:'c', done:true}
k.next();         // {value:'undefined', done:true}let obj = {};
obj[Symbol.iterator] = function* () {yield 1;yield 2;yield 3;
}
for(let value of obj) {console.log(value);         // 1,2,3
}

实例

// 应用1:状态机
// 假设某一事物只有三种状态,该事物仅在这三种状态中转换
// 以下生成器使得state仅在A、B、C间转换
let state = function* () {while(1) {yield 'A';yield 'B';yield 'C';}
}// 应用2:限制抽奖次数
let draw = function(count) {// 此处为具体的抽奖逻辑console.log(`Remain ${count}`);
}let residue = function* (count) {while (count>0) {count --;yield draw(count);}
}// 应用3:长轮询
let ajax = function* () {yield new Promise(function(resolve, reject) {setTimeout(() => {resolve({code:0});}, 200);})
}let pull = function() {let generator = ajax();let step = generator.next();step.value.then(function(val) {if(val.code !== 0) {setTimeout(() => {console.log('wait');pull();}, 1000);} else {console.log(val);}})
}pull();

Decorator

  • 修饰器是一个函数,用来修改类的类型
  • 第三方库core-decorators提供了现成的修饰器
  • 解决了两个问题

    • 不同类间共享方法
    • 编译期对类和方法的行为进行改变
  • 参数

    • target - 目标类
    • name - 属性
    • descriptor - 属性描述符
// *修饰器特性在将来的ES版本可能会被移除
let readonly = function (target, name, descriptor) {descriptor.writable = false;return descriptor;
}class Test{@readonlytime () {return '2018-11-21';}
}
let t = new Test();
t.time = function(){};      // 会报错,因为修饰器不允许对该方法进行修改// 也可修饰整个类
let typename = function (target, name, descriptor) {target.myname = 'hello';
}
@typename
class Name{}
console.log(Name.myname);// 实例:日志系统的埋点
let log = (type) => {return function (target, name, descriptor) {let src_method = descriptor.value;descriptor.value = (...arg) => {src_method.apply(target, arg);console.log(`log ${type}`);}}
}class AD{@log('show')show () {console.info('ad is shown');}@@log('click')click () {console.log('ad is clicked')}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用jenkins创建节点

原料&#xff1a;(1)jre下载链接&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html (2)jdk:下载链接&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 一、创建SLAVE节点…

解决 Script Error 的另类思路

2019独角兽企业重金招聘Python工程师标准>>> 本文由小芭乐发表 前端的同学如果用 window.onerror 事件做过监控&#xff0c;应该知道&#xff0c;跨域的脚本会给出 "Script Error." 提示&#xff0c;拿不到具体的错误信息和堆栈信息。 这里读者可以跟我一…

迅雷影音怎样 1.5倍速度播放

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 看视频 觉得播放速度太慢&#xff0c;想让1.5速度播放可以这样设置&#xff1a; 点击快进按钮&#xff0c;点一次变为1.1倍&#xff0c…

git pull时冲突的几种解决方式

仅结合本人使用场景&#xff0c;方法可能不是最优的 1. 忽略本地修改&#xff0c;强制拉取远程到本地 主要是项目中的文档目录&#xff0c;看的时候可能多了些标注&#xff0c;现在远程文档更新&#xff0c;本地的版本已无用&#xff0c;可以强拉 git fetch --allgit reset --h…

Linux:echo命令详解

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 echo命令 用于字符串的输出 格式 echo string使用echo实现更复杂的输出格式控制 1.显示普通字符串: echo "It is a test"这里…

学习 shell脚本之前的基础知识

见 : http://www.92csz.com/study/linux/12.htm【什么是shell】 简单点理解&#xff0c;就是系统跟计算机硬件交互时使用的中间介质&#xff0c;它只是系统的一个工具。实际上&#xff0c;在shell和计算机硬件之间还有一层东西那就是系统内核了。打个比方&#xff0c;如果把计算…

Git cherry-pick后再merge出现一个“奇怪”的现象

背景描述&#xff1a;有的时候基于一个master branch拉出一个独立feature分支做开发时&#xff0c;两条分支都在并行开发&#xff0c;如果master分支增加了某些功能&#xff0c;解决了某些关键bug&#xff0c;而独立feature分支不需要所有的增加的commit&#xff0c;只需要某一…

Sublime Text3中文环境设置

Sublime Text3中文环境设置 1、首先打开安装好的的Sublime软件,选择Preferences下面的Package Contorol选项出现弹窗方框 2、在弹窗输入install package,选择对应&#xff08;默认第一个&#xff0c;如图这个&#xff09;命令点击进入;安装的时候&#xff0c;左下角会有进度条显…

C/C++图形化编程(2)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 站在巨人的肩上是为了超过巨人&#x…

Git clone之后你的硬盘上究竟发生了什么?

网上关于Git的使用有太多的博客&#xff0c;文章在讲解了&#xff0c;大部分是在讲解命令的用法&#xff0c;剩下一部分则在讲解git的内部原理&#xff0c;看过讲解基础命令使用的文章后&#xff0c;正常的开发使用是没有什么问题的了&#xff0c;而如果想更深入的了解git“高级…

感知机模型的对偶形式[转载]

转自:https://blog.csdn.net/jaster_wisdom/article/details/78240949#commentBox 1.区分一下易混淆的两个概念&#xff0c;梯度下降和随机梯度下降&#xff1a; 梯度下降&#xff1a;一次将误分类集合中所有误分类点的梯度下降&#xff1b; 随机梯度下降&#xff1a;随机选取一…

go语言渐入佳境[6]-operator运算符

运算符和其他语言一样&#xff0c;Go语言支持多种运算符&#xff0c;用于对变量进行运算。12345678910111213package mainimport "fmt"func main(){ //math() //relation() //logic() //wei() Assign()}算术运算符123456789101112func math(){ a : 4 b:2 fmt.Printf(…

记录腾讯云中矿机病毒处理过程(重装系统了fu*k)

2019-1-21日常上班的周一 刚想学学kafka&#xff0c;登录与服务器看看把&#xff0c;谁知ssh特别慢&#xff0c;很奇怪&#xff0c;我以为是我网速问题&#xff0c;断了wifi&#xff0c;换了网线&#xff0c;通过iterm想要ssh rootx.x.x.x&#xff0c;但是上不去&#xff1f; 就…

对象反序列化出现类型不匹配的情况(spring-boot-devtools)

目前在做springboot项目的shiro session redis共享功能。但是有一个对象我把它放到redis中之后再取出来就会出现类型不匹配的异常 AuthorizationUser user (AuthorizationUser) cache.getSuper(key); 异常信息&#xff1a; java.lang.ClassCastException: com.ch.evaluation.a…

音视频多媒体协议相关资料汇总

未知问题&#xff1a; 编码&#xff0c;封装&#xff0c;协议的区别&#xff1a; 如何将TS源流重新封装并通过P2P协议传输在安卓终端和苹果终端播放封装 介绍完了视频编码后&#xff0c;再来介绍一些封装。沿用前面的比喻&#xff0c;封装可以理解为采用哪种货车去运输&…

谷歌地图VS苹果地图:大数据领域竞争

摘要&#xff1a;iOS 6推出之后&#xff0c;争议最大的是什么&#xff1f;苹果地图。苹果地图成为人们抨击iOS 6的首选&#xff0c;而苹果放弃谷歌地图选择自力更生是迫不得已。苹果和谷歌之间的竞争领域可以用三个字来概括&#xff1a;大数据。谷歌拥有大数据&#xff0c;而苹…

微软正在考虑将Windows默认浏览器改为Chromium

据外媒报道&#xff0c;微软正在构建一个基于Chromium的浏览器&#xff0c;代号为Anaheim&#xff0c;目标是取代Windows中的Edge。 Microsoft Edge是微软于2015年推出的浏览器&#xff0c;该浏览器取代了IE成为Windows 10的默认浏览器。尽管如此&#xff0c;Microsoft Edge并没…

三次握手的第三个ACK包丢了,会发生什么?

转载自三次握手的第三个ACK包丢了&#xff0c;TCP的处理方式 三次握手的第三个ACK包丢了&#xff0c;客户端认为连接建立&#xff0c;写数据时&#xff0c;会触发RST。 当Client端收到Server的SYNACK应答后&#xff0c;其状态变为ESTABLISHED&#xff0c;并发送ACK包给Server&a…

一分钟了解四层/七层反向代理

转自公众号&#xff1a;架构师之路今天花几分钟简单和大家解释一下。场景&#xff1a;访问用户通过proxy请求被访问的真实服务器 路径&#xff1a;用户 -> proxy -> real-server什么是代理&#xff1f; 回答&#xff1a;[proxy]代表[访问用户]&#xff0c;此时proxy是代理…

tcp建立连接为什么需要三次握手

这是一个看似很“简单”的问题&#xff0c;但貌似并没有一个官方统一的答案。搜索了相关的资料&#xff0c;列举出一些答案。 以下部分转载自&#xff1a;tcp建立连接为什么需要三次握手 在《计算机网络》一书中其中有提到&#xff0c;三次握手的目的是“为了防止已经失效的连…