ES6常用API

1、let

let关键字,用来声明变量,它的用法类似于var。
1、let不允许重复声明变量;

var a = 1;
var a = 2;
console.log(a);//2

var b = 3;
let b = 4;
console.log(b);//Identifier 'b' has already been declared

2、 不会添加到window对象下

let b = 4;
console.log(window.b);//undefined

3、let声明变量仅在块级作用域内有效;

if(12){
    // var a = 12;
    let a = 34;
}
console.log(a);//a is not defined

for(var i=0;i<5;i++){
    console.log(i);//0 1 2 3 4
}
console.log(i);// 5

for(let i=0;i<5;i++){
    console.log(i);//0 1 2 3 4
}
console.log(i);// i is not defined

4、不能通过let声明和形参相同的变量;

function fn(a,b){
// var a=1;
let a=1;
}
fn(2,3);//Identifier 'a' has already been declared

5、let声明变量不会提升;

console.log(a);//undefined
var a = 11;

console.log(b);//报错,Cannot access 'b' before initialization
let b = 22;
注意:let声明的变量一定要在声明之后使用,否则报错。

6、暂时性死区

var a = 2;
function test(){
    console.log(a);//报错,Cannot access 'b' before initialization
    let a = 4;
}
test();
应用
var lis = document.querySelectorAll('.list li');
for(var i=0;i<lis.length;i++){
    lis[i].index = i;
    lis[i].onclick = function(){
        lis[this.index].style.color = 'red';
    }
}
for(let i=0;i<lis.length;i++){
    lis[i].onclick = function(){
        lis[i].style.color = 'red';
    }
}

2、const

const和let以上的特性一样
const声明常量

let b=2;
b=4;
console.log(b);//4
const a = 123;
a = 456;// 报错,Assignment to constant variable.
const与let类似,但是,const常量一旦声明,常量将不能重新赋值!

let x;
const y;// 报错,Missing initializer in const declaration
意味着,const一旦声明,就必须立即初始化,不能留到以后赋值!

在工作中,通常为了和变量区分, 声明常量使用大写
const USER_NAME = '张三丰';

const声明的常量不能改变嘛?
不能,常量的内存地址不能改变
本质:const实际上保证的,并不是值不能改变,而是指向的那个内存地址不能改变。

const USER = {
    name: 'xm',
    age: 24
}
USER.age = 25;
USER = {// 报错,Assignment to constant variable.
    name: 'xm',
    age: 25
}
console.log(USER.age);

3、箭头函数

语法:(形参)=>{函数体}

function fn(a,b){}
setTimeout(()=>{});
fun()=>{};// 报错, Malformed arrow function parameter list
let fun = ()=>{};
fun();

什么时候可以简写:
1.只有一个形参时,参数的小括号可以省略

[1,2].forEach(item=>{
    console.log(item);
});

2.当函数体只有一句代码,或只有返回值时,函数体的{}也可以省略

let fn = _ => _ ;  //等同于fn = (_)=>{return _}
console.log(fn(111));

箭头函数需要注意的地方:
1.关于 this 指向问题,箭头函数中没有自己的 this
箭头函数中的this指向外层函数的this

setTimeout(function(){
    console.log(this);//window
},1000);

box.onclick = function(){
    console.log(this);//box
    // setTimeout(function(){
    //     console.log(this);//window
    // },1000);
    setTimeout(()=>{
        console.log(this);//box
    },1000);
}
let test = (a,b)=>a+b;

2.箭头函数内部的 this 不能改变指向

let obj = {a:123};
setTimeout(function(){
    console.log(this);
}.bind(obj),1000);//{a:123}

setTimeout(()=>{
    console.log(this);
}.bind(obj),1000);//报错,missing ) after argument list

3.箭头函数内部没有arguments对象。更不能通过arguments对象访问传入参数。

let fn1 =(a,b)=>{
    console.log(arguments[0]);//报错,arguments is not defined
};
fn1(2,3);

4.不能作为构造函数使用,不能使用new调用

let fn1 = function(){};
function fn2(){};
new fn1();
new fn2();
let fn3 = ()=>{};
new fn3();//fn3 is not a constructor

注意:

1、如果没有参数要有小括号
2、如果有一个参数可以不用小括号
3、如果有多个参数必须加小括号
4、如果仅有一条语句,并且返回该内容,(ES5中的return),可以不写return
5、如果有多条语句,必须加{}将内容括起来,否则将会认为是函数外语句,并且如果return就必须加return语句。
6、箭头函数的this指向是原代码块的this指向,不受其他执行影响。

3、template string((字符串模板)

1、ES6中字符串模板使用反引号 ` 表示,字符串模板中可以解析变量和函数,使用 ${ } 解析

// let showHtml = '<div class="header"><span>'+tit1+'</span><span>'+tit2+'</span></div><div class="content"><span>'+con1+'</span><span>'+con2+'</span></div>';

let showHtml = '<div class="header">'+
                    '<span>'+tit1+'</span>'+
                    '<span>'+tit2+'</span>'+
                '</div>'+
                '<div class="content">'+
                    '<span>内容1</span>'+
                    '<span>内容2</span>'+
                '</div>';

let test = (a,b)=>a+b;

let str = `
    <div class="header">
        <span>${tit1}</span>
        <span>${tit2}</span>
    </div>
    <div class="content">
        <span>${con1}</span>
        <span>${con2}</span>
        <span>${test(3,5)}</span>
    </div>`;

// show.innerHTML = str;
// let name = '小明';
// let age = 24;
// function fn(a,b,c){
//     console.log(a);
//     console.log(b);
//     console.log(c);
// }
// fn `大家好,我叫${name},今年${age}岁`;
// fn `大家好,我叫,今年岁`;
// 函数后面跟字符串模板,函数内部第一个参数一定是数组
</script>
</body>
</html>

4、Destructuring(解构赋值)

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
解 构 赋值:解析数据结构,进行赋值

// let a = 1, b =2 ,c = 3;

// 数组解构赋值
let arr = [24,77,86,66];
// let a = arr[0];
// let b = arr[1];
// let c = arr[2];
// let d = arr[3];
//  let [a,b,c,d] = arr;
// console.log( a,d );

// 对象解构赋值
// let obj = {
//     name: 'xiaohong',
//     friends: ['xw','xf','xd'],
//     sex: '女'
// }
// let {name,friends,sex} = obj;
// console.log(name);
// console.log(sex);

// 函数参数解构赋值
// function fn(n,a,s,f){

// }
// fn('xm',23,'男',['xh','xw']);

解构赋值可以作用在函数的参数上,让函数参数的值传递顺序改变
// function fn({n,a,s,f}){
    
// }
// fn({f:['xw','xh'],s:'男',n:'xm',a:25});

5、函数的默认参数

// 基本用法
function first(x = 1, y = 2) {
    console.log("x:"+x ,"y:"+ y);
}
first(); // 'x:1 y:2'
first(100); // 'x:100 y:2'

// 与解构赋值结合
function second({x, y = 2}) {
    console.log("x:"+x ,"y:"+ y);
}
second({}); // 'x:undefined y:2'
second({x:100}); // 'x:100 y:2'
second({x:100,y:200}); // 'x:100 y:200'

// 这种写法在传入多个形参时可以不按顺序写入,会方便很多,但有个问题,
// 没有默认值时,每次都要传“{}”就会显得很麻烦,于是我们可以再设置一次默认值。

// 双重默认值
function third({x = 1 ,y = 2} = {}) {
    console.log("x:"+x ,"y:"+ y);
}
third(); // 'x:00 y:2'
third({x:100,y:200}); // 'x:100 y:200'
third({x:100}); // 'x:100 y:2'

6、includes()方法

1、数组的 includes() 方法用来判断一个数组是否包含一个指定的值,返回布尔值

arr.includes(value,index);
value 要查找的元素值
index 从 index 索引处开始查找 value,默认为 0,可选

2、字符串的 includes() 方法用于判断一个字符串是否包含一个指定的值,返回布尔值

str.includes(searchString,index);
searchString 要搜索的字符串
index 从 index 索引处开始搜索searchString ,默认为 0,可选

// includes()判断数组是否有某个元素
let arr = [1,2,3,4];
console.log( arr.includes(3) );//true
console.log( arr.includes(2,2) );//false

let str = 'hello world';
console.log( str.includes('a') );//false
console.log( str.includes('e',4) );//false

7、Array.from( )

Array.from:将含有length属性,以数字为key的对象、类数组转成真正的数组。
Array.from(obj, map函数);
第一个参数为要转换的对象,第二个参数为一个函数,可选,类似map函数。
map函数 : 遍历数组--操作数组--返回数组
var arr = [1,2,3,4,5];
var newArr = arr.map( (item) => { return item*2 } );
console.log( newArr );

类数组元素集合:
var lis = document.getElementsByTagName("li");
console.log(lis);
lis.push('abc');
console.log(lis);

将lis集合(类数组)转成 数组:
lis = Array.from(lis);
console.log( lis )
lis.push('abc');
console.log(lis);

将对象转成 数组:
var obj = {
    "0" : 10 ,
    "1" : 20 ,
    "2" : 30 ,
    "length" : 3
};
var arr = Array.from( obj );
console.log( arr );

第二个参数是一个匿名函数 实现的是map功能:
var newArr = Array.from( obj , (item) => { return item*2; } )
console.log( newArr );

8、三个点(…)

扩展运算符用三个点号表示,其功能是把数组或类数组对象(部署了iterator接口)展开成一系列用逗号隔开的参数序列。
console.log(...[1, 2, 3]);
console.log(1, ...[2, 3, 4], 5);

var lis = document.getElementsByTagName("li");
console.log([...lis]);

其他用法:
var arr1 = [1,2];
var arr2 = [3,4,5];
function addItems(arr, ...items) {
    arr.push(...items);
};
addItems(arr1,...arr2);  =>  addItems(arr1,3,4,5);
console.log(arr1);

arr.push(...items) 和 addItems(arr1,...arr2) 函数调用都使用了扩展运算符将数组变为参数序列
注意这行:function addItems(arr, ...items) 这里的三个点并不是扩展运算符,而是 rest运算符

rest运算符也是三个点,其功能与扩展运算符恰好相反,把逗号隔开的参数序列组合成一个数组

var arr = [1,2,3];
function fn(...args) {// rest运算符 组合数组
console.log(args);
};
fn(...arr);// 扩展运算符 展开数组
console.log(...arr);

9、Set 和 Map

ES6 提供了两种新的数据结构 Set 和 Map。

Set 是一个构造函数,用来生成 Set 数据结构,它类似于数组,但是成员的值都是唯一的、没有重复的, 初始化 Set 可以接受一个数组或类数组对象作为参数,也可以创建一个空的 Set:

var s1 = new Set();
var s2 = new Set([1, 2, 3]);
console.log(s1);
console.log(s2);

在 Set 中成员的值是唯一的,重复的值自动被过滤掉

var s1 = new Set([1, 2, 2, 3, 1, 4]);
console.log(s1);

Set 的一些属性方法:
size:返回成员总数
add(value):添加某个值,返回Set结构本身
delete(value):删除某个值,返回一个布尔值,表示删除是否成功
has(value):返回一个布尔值,表示该值是否为Set的成员
clear():清除所有成员,没有返回值

var set = new Set([1,2]);
set.add(3);// 添加成员
console.log(set.size);// 3 成员总数
console.log(set);// Set(3) {1, 2, 3}
set.add([4,5]);// 添加成员
console.log(set.size);// 4 成员总数
console.log(set.has(2));// true 有该成员
console.log(set);// Set(4) {1, 2, 3, [4, 5]}
set.delete(2);// 删除成员
console.log(set);// Set(3) {1, 3, [4, 5]}
console.log(set.has(2));// false 没有该成员
set.clear();// 清除所有成员
console.log(set);// Set(0) {}

得益于数据结构 Set 查找更快速高效,但也因为数据结构的内部数据是无序的,无法实现按下标改查,排序等操作

var arr = [1,2,3,'a',4,'b'];
var set = new Set(arr);
console.log(set[0]);// undefined
console.log(set['a']);// undefined

Set 没有类似 getter 的方法,怎么取值呢?
可以遍历取值,或者可以把 Set 转成真正的数组!

var s5 = new Set([4,5,6,7,8]);
var [a,b,c] = [...s5];
console.log(a);
console.log(b);
console.log(c);

var arr = [...s5];
console.log(arr[0]);

遍历 Set 对象

var set = new Set(['a','b','c','d']);
set.forEach((val,key,set)=>{
    console.log('val: '+val);
    console.log('key: '+key);
    console.log(set);
});

使用ES6的 for of 遍历(数组,类数组)

var set = new Set(['a','b','c','d']);
for (const val of set) {
    console.log(val);
}

for/of 与 for/in

for/of:遍历值

var arr = [4,5,6,7];
for (var val of arr) {
    console.log(val);//4 5 6 7
}

for/in:遍历键

var arr = [4,5,6,7];
for (var key in arr) {
    console.log(key);//0 1 2 3
}

var str = 'javascript';
for (var val of str) {
    console.log(val);
}

Map
是一个构造函数,用来生成 Map 数据结构,它类似于对象,也是键值对的集合,但是“键”可以是非字符串, 初始化 Map 需要一个二维数组,或者直接初始化一个空的 Map:

var m1 = new Map();
var m2 = new Map([['a', 123], ['b', 456], [3, 'abc']]);
console.log(m1);
console.log(m2);

Map 的一些操作方法:

set(key, value):设置键值对
get(key):获取键对应的值
has(key):是否存在某个键
delete(key):删除某个键值对,返回一个布尔值,表示删除是否成功

var map = new Map([['a', 123], ['b', 456], [3, 'abc']]);
map.set('c',789);
console.log(map.get('c')); // 789
console.log(map.has('b')); // true 此key存在
map.delete(3); // true 成功删除key
console.log(map); // Map(3) {"a" => 123, "b" => 456, "c" => 789}

遍历 Map 对象

var map = new Map([['a', 123], ['b', 456], [3, 'abc'],['c', 789]]);
map.forEach((val,key,obj)=>{
    console.log('val: '+val);
    console.log('key: '+key);
    console.log(obj);
});

当然也可以使用ES6的 for of 遍历

var map = new Map([['a', 123], ['b', 456], [3, 'abc'],['c', 789]]);
for (const item of map) {
    console.log( item );//数组
}
for (const [key,val] of map) {
    console.log(key+' : '+val);
}

传统 Object 用字符串作键,Object 结构提供了“字符串 — 值”的对应
Map 结构提供了“值 — 值”的对应,是一种更完善的 Hash 结构实现
如果你需要“键值对”的数据结构,Map 比 Object 更快速 更高效 更合适。

10、Symbol类型

// Symbol 表示独一无二的值,第七种数据类型
// console.log( Date.now() === Date.now() );//true
// console.log( Math.random() === Math.random() );//false

// 创建Symbol值
let s1 = Symbol('age');
let s2 = Symbol('phone');//参数phone,描述信息
// console.log(s1);
// console.log(s2);
// console.log( s1 === s2 );//false
console.log( typeof s1 );// 'symbol'

let obj = {
    name: '张三',
    // age: 24,
    sex: '男',
    // s1: 24
    [s1]: 24,
    [s2]: 13500009999,
    [Symbol('address')]: '深圳市宝安区后瑞地铁站'
}
// console.log(obj);
// console.log( obj.s1 );//undefined  对象.属性
// console.log( obj[s1] );//24  对象['属性']
// console.log( obj[Symbol('address')] );//undefined


// 避免以下操作访问到Symbol属性
// for (let key in obj){
//     console.log(key);
// }

// console.log( Object.keys(obj) );

// console.log( Object.getOwnPropertyNames(obj) );

// let obj2 = JSON.stringify(obj);
// console.log(obj2);


// 获取对象的所有Symbol属性
let attrs = Object.getOwnPropertySymbols(obj);
// console.log(attrs);// [Symbol(age), Symbol(phone), Symbol(address)]
// console.log( obj[attrs[2]] );


// 如何每次都能得到相同的Symbol值
let s3 = Symbol.for('age');//生成一个独一无二的值
let s4 = Symbol.for('age');// 系统会自动检测,如果这个值已经存在,就不再重新生成,返回已生成的值
// console.log( s3 === s4 );//true
// console.log( Symbol.for('age') === Symbol.for('age') );//true

let obj3 = {
    name:'xh',
    [Symbol.for('age')]: 24,
    [Symbol.for('sex')]: '男'
}
console.log( obj3[Symbol.for('age')] );

console.log( Symbol.keyFor(s3) );// 'age'


console.log( [1,2] );

11、Object扩展

Object.getOwnPropertySymbols(obj) 
方法会返回当前对象的所有 Symbol 属性,返回数组


Object.setPrototypeOf(obj1,obj2)
方法用来设置一个对象的 prototype 对象,与Object.getPrototypeOf 方法配套

es5 实现原型链接
var obj1 = Object.create(obj2); // 创建一个空对象,并把该对象的原型链接到obj2对象

es6 实现原型链接
Object.setPrototypeOf(obj1,obj2); // 把对象obj1的原型链接到obj2


Object. getOwnPropertyDescriptors(obj)
获取指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
var obj2 = {
    name: 'xm',
    age: 23
}
var attrObj1 = Object.getOwnPropertyDescriptor(obj2,'name'); // ES5获取一个属性的描述符
var attrObj2 = Object. getOwnPropertyDescriptors(obj2); // 获取所有自身属性的描述符
console.log(attrObj1);
console.log(attrObj2);
{
    configable: true,
    enumerable: true,
    writeable: true,
    value: 'xm'
}


Object.values(obj)
方法返回一个数组,成员是参数对象自身的所有可枚举属性的值(与Object.keys配套)
var obj = { foo: 'abc', baz: 123 };
console.log(Object.values(obj));


Object.entries(obj)
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ]

Object.entries方法可以将对象转为真正的Map结构
const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }


Object.assign(target,source)
方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
var obj1 = {a: 1, b: 2, c: {d: 4, e: 5}};
var obj2 = Object.assign({}, obj1);
console.log(obj1.c === obj2.c); // ture
Object.assign 方法是一种对象浅拷贝,如果对象属性是引用类型,只能是拷贝引用地址

对象深拷贝
var obj1 = {a: 1, b: 2, c: {d: 4, e: 5}};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1.c === obj2.c); // false

jQuery.extend(boolean,target,source);


Object.is(val1,val2)
方法用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
不同之处只有两个:一是: +0不等于-0,二是: NaN等于自身。

+0 === -0 //true
NaN === NaN // false

{} === {} // false
Object.is({}, {}); // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

12、ES6 类与继承

1、ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。
新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
无需考虑ES5中令人头疼的几个部分:构造函数、原型、继承…
2、用class定义一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。

3、class之间可以通过extends关键字实现继承,这比ES5通过修改原型链实现继承,要清晰和方便很多。
4、super关键字,它指向父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

5、ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

// Class 类 (语法糖)
class Cat {// 父类
    constructor(n,c){//构造函数
        this.name = n;//对象自身属性
        this.color = c;//对象自身属性
        this.traint = function (){
            console.log('喵喵~');
        }
    }
    type = 'animal';//对象自身属性
    skill(){//原型对象上的方法
        console.log('抓老鼠');
    }
    static sayHi(){//静态方法,Cat.sayHi()
        console.log('hi~~');
    }
}
let cat1 = new Cat('小黑','baise');
// console.log( cat1 );
// console.log(cat1.name);
// cat1.skill();
// console.log( cat1.__proto__ === Cat.prototype );//true
// Cat.sayHi();

// 继承
class Dog extends Cat {//子类
    constructor(f,n,c){
        super(n,c);//先调用super方法  Cat.call(this,n,c)
        this.food = f;
        // console.log( super.type );//undefined
        // console.log( this.type );//animal
        // this.skill();//'抓老鼠'
        // super.skill();//'抓老鼠'
        // super.traint();//报错
        // console.log( super );//报错
        // console.log( super === Cat.prototype );
    }
}
let dog1 = new Dog('骨头','大黄','yellow');
console.log( dog1.food );
dog1.skill();
console.log( dog1.name );
console.log( dog1.type );