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 );