http://caibaojian.com/es6/object.html
前者是后者的标准,后者是前者的一种实现。2015 年 6 月,ECMAScript 6 正式通过,成为国际标准。
let:代码块内有效、无变量提升、不能重复声明;
const:
解构:从数组和对象中提取值,对变量进行赋值;
数组模型的解构(Array)
let [x, y] = [1, 2, 3];
x=1,y=2;let [a, ...b] = [1, 2, 3];
,b = [2, 3];let [a = 2] = [undefined]; // a = 2
对象模型的解构(Object)
let {p: [{ y }, x ] } = {p: [{y: 'world'}] };
, x=undefined,y=‘world’;let {a: aa = 10, b: bb = 5} = {a: 3};
, aa = 3; bb = 5l;解构赋值常用于:
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
修饰符,y
与g
都是全局匹配,g
是从上一次匹配的位置继续向后搜索,y
要求必须从下一个位置开始匹配成功,匹配不成功就不再向后搜索返回null;
5.sticky属性判断是否设置了y修饰符;var r = /hello\d/y; r.sticky // true
\u{}
表示形式,如 \u{20BB7}
codePointAt()
charAt()
和charCodeAt()
对Unicode 的处理是不到位的;ES6中,使用codePointAt()
返回一个字符的码点,包括大于两个字节的字符;s.codePointAt(0).toString(16) // "20bb7"
;c.codePointAt(0) > 0xFFFF
;var s = '?a';
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
String.fromCodePoint()
fromCharCode()
从码点返回对应字符,但无法识别Unicode编号大于0xFFFF
的字符String.fromCodePoint()
解决上述问题字符串的遍历器接口
for...of
循环遍历,其最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点;子串的识别:
字符串重复
字符串补全
模板字符串
${}
中标签模板
alert
Hello world!;
等价于alert('Hello world!');
f
My 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
Hi\n${2+3}!;// "Hi\\n5!"
二进制和八进制表示
0b11
0o11
常量
equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true
Number对象新方法
从全局移植到 Number 对象的方法
Math 对象的扩展
数组创建(of/from)、遍历(entries/keys/values)、索引(find/findIndex)、填充(fill/copyWithin)、包含(includes)、嵌套(flat/flatMap)、数组缓冲区(ArrayBuffer)、视图(DataView)、定型缓冲区(Int8Array…)、扩展运算符…
数组查找
数组填充
[1, 2, 3, 4].copyWithin(0,2,4) // [3, 4, 3, 4]
;第一个参数是被修改的起始索引,第二个参数是被用来覆盖的数据的起始索引,第三个参数可选;数组遍历
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);
for(let key of ['a','b'].keys()){
console.log(key); //[0,1]
}
console.log([...['a','b','c'].keys()]); //[0,1,2]
for(let value of ['a','b','c'].values()){
console.log(value);
}
console.log([...['a','b','c'].values()]);
数组包含关系
[1, 2, 3].includes(1); // true
嵌套数组转一维数组
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]
,若参数为Infinity,表示不管嵌套多少层,全部转为一维数组,自动跳过空位;[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类型
// 默认 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
定型数组 (特定类型的视图)
let view = new Int32Array(10);
长度为40;Array.isArray(view)
为false;let view = Int16Array.of(1, 2); console.log(view instanceof Int16Array); // true
类数组对象
obj[0] = 9;
。扩展运算符
[...[1, 2],...[3, 4]]
rest参数
箭头函数
尾调用
return tail(x)
;const person = {age: age, name: name}
-> const person = {age, name};
sayHi:function(){...}
-> sayHi(){...}
*
属性表达式
["he"+"llo"](){...}
;对象的拓展运算符 …
let person = {name: "Amy", age: 15}; let someone = { ...person };
对象的新方法
Object.assign(target, source_1, ···)
Object.assign([2,3], [5]);
,将数组处理成对象,所以先将 [2,3] 转为 {0:2,1:3} ,然后再进行属性复制,最后得到[5,3]
Object.is(value1, value2)
Object.is(+0,-0);
//false+0 === -0
//trueObject.is(NaN,NaN);
//trueNaN === NaN
//falseObject.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);
}
Symbol
Symbol.for()
Symbol.keyFor()
var s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo"
,var s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined
for...in
和 let...of
都是拿不到对象的Symbol类型属性的;Set
add()
delete()
has()
clear()
size()
new Set([4,5,6])
[...set4]
let arr = [3, 5, 2, 2, 5, 5]; let unique = [...new Set(arr)];
WeakSet
Map
[...map.values()]
[...map.entries()]
new Map([[true, 7], [{foo: 3}, ['abc']]])
strMapToObj(myMap)
objToStrMap({yes: true, no: false})
strMapToJson(myMap)
jsonToStrMap('{"yes":true,"no":false}')
true
和布尔值true
是两个不同的键WeakMap
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
Proxy
get()
,拦截某个属性的读取操作set()
,拦截某个属性的赋值操作apply()
,拦截函数的调用has()
,拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效construct()
,拦截new命令deleteProperty()
,拦截delete操作defineProperty()
,拦截添加新属性操作getOwnPropertyDescriptor()
,拦截Object.getOwnPropertyDescriptorgetPrototypeOf()
,拦截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'));
}
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();
}
使用回调函数和使用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);
}
数据集合
Iterator接口
自定义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
Generator
*
号;二是,函数体内部使用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();
}
target
,就是所要修饰的目标类用法一,类的修饰,在类外面使用,添加类的静态属性和实例属性
{
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();
}
// 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那些事儿–ES6十问
ES6 Promise中较难理解的题
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);