文章目录
ES6入门教程–阮一峰
1. 变量声明-- let
let a = 1;
注意:
- 不能重复声明
- 存在块级作用域
- 作用域链仍然存在
- 存在暂时性死区,即不能在声明前就使用变量(没有变量提升)
- var 在全局作用域下声明变量会导致变量挂载在 window对象 上,let和const声明的变量不会
//例
var a = 1
let b = 1
const c = 1
console.log(window.a) // 1
console.log(window.b) // undefined
console.log(window.c) // undefined
2. 常量声明-- const
const PI = 3.14;
- 不能重复声明
- 存在块级作用域
- 作用域链仍然存在
- 存在暂时性死区,即不能在声明前就使用变量(没有变量提升)
- var 在全局作用域下声明变量会导致变量挂载在 window对象 上,let和const声明的变量不会
- 必须要赋初始值
- 一般常量名用大写
- 常量值不能修改
- 数组和对象的内容可以修改(其指向的地址不变,只是修改了内容),所以最好使用const声明数组和对象
例:const Arr = [“aaa”,“bbb”,“ccc”];
Arr.push(“ddd”);
3. 解构赋值
es6允许按照一定模式从可迭代对象(字符串、数组、对象)中提取值,对变量进行赋值,这被称为解构赋值。
-
字符串的解构赋值
let str= '1234'; let [a,b,c] = str; console.log(a);//1
-
数组的解构赋值
const arr = ["小迟","小娜","天仙"]; let [chi,na,xian] = arr; console.log(chi);//小迟
-
对象的解构赋值
const chi = { name:"迟娜", age:18, hobby:function(){ console.log("看书"); } } let {name,age,hobby} = chi; console.log(hobby);//function(){console.log("看书");} hobby();//看书
4. 展开运算符 […]
… 展开运算符能将数组、对象等转化为逗号分隔的参数序列
//数组
<script>
const numbers = ['1','2','3'];
const tfboys = ["易烊千玺",...numbers,"王源","王俊凯"];
console.log(tfboys);
//(6)["易烊千玺", "1", "2", "3", "王源", "王俊凯"]
</script>
<script>
//对象
const obj1 = {
a:1,
b:2
};
const obj2 = {
...obj1,
c:3,
d:4
};
console.log(obj2);
//{a: 1, b: 2, c: 3, d: 4}
</script>
5. 模板字符串
模版字符串是增强版字符串,用反引号标识,可以当做普通字符串使用,也可以用来定义多行字符串或者在字符串中嵌入变量
- 模板字符串中可以直接出现换行符,“”或‘’不能
let str = `<ul>
<li>小迟迟</li>
<li>小娜娜</li>
<li>迟小娜</li>
</ul>`
document.write(str);
- 可以使用${}进行变量拼接
let name = '小娜';
let des = `${name}是个小仙女`;
console.log(des);//小娜是个小仙女
6.set
ES6 提供了一种新的类似于数组的数据结构set(集合),但成员的值都是唯一的,
集合实现了iterator接口,所以可以使用「扩展运算符 …』和「for…of…』进行遍历,
ES6规定,所有部署了Iterator接口的对象(可遍历对象)都可以通过for…of去遍历,而for…in仅仅可以遍历对象。这也就意味着,数组也可以用for…of遍历,这极大地方便了数组的取值,且避免了很多程序用for…in去遍历数组的恶习。扩展运算符本质上也就是for…of循环的一种实现。
集合的属性和方法:
1)size返回集合的元素个数
2)add()增加一个新元素,返回添加新元素的set集合,可以用于链式调用
3)delete()删除元素,返回boolean值
4)has()检测集合中是否包含某个元素,返回boolean值
5)clear()清除数组
<script>
//声明一个空set
let s = new Set();
console.log(s);//Set(0) {}
//声明一个非空set
let ss = new Set(["aa","bb","cc"]);
console.log(ss);//Set(3) {"aa", "bb", "cc"}
//元素的个数
console.log(ss.size);//3
//添加一个元素
ss.add("dd");
//删除一个元素
ss.delete("bb");
console.log(ss);//Set(3) {"aa", "cc", "dd"}
//检测是否含有,有返回true,无返回false
console.log(ss.has("bb"));//false
//遍历结合
/*
* 结果:
* aa
* cc
* dd
*/
for(let v of ss){
console.log(v);
}
console.log(...ss);//aa cc dd
//清空集合
ss.clear();
console.log(ss);//Set(0) {}
</script>
- 集合的应用
<script>
const arr1 = [1,2,3,3,3,6,6,8,9,9];
const arr2 = [6,6,7,8,9,9];
//1.数组去重
const result = [...new Set(arr1)];
console.log(result);//(6) [1, 2, 3, 6, 8, 9]
//2.数组求交集
const result1 = [...new Set(arr1)].filter(item=>{
let s2 = new Set(arr2);
if(s2.has(item)){
return true;
}else{
return false;
}
});
console.log(result1);//(3) [6, 8, 9]
//3.数组求并集
const arr3 = [...arr1,...arr2];
const result2 = [...new Set(arr3)];
console.log(result2);(7) [1, 2, 3, 6, 8, 9, 7]
//4.数组求差集 arr1-arr2(在arr1不在arr2中的元素)
const result3 = [...new Set(arr1)].filter(item=>{
let s2 = new Set(arr2);
if(s2.has(item)){
return false;
}else{
return true;
}
});
console.log(result3);//(3) [1, 2, 3]
</script>
-
Set 结构的实例有四个遍历方法,可以用于遍历成员。
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员Set的遍历顺序就是插入顺序。
-
keys(),values(),entries()
keys(),values(),entries()方法返回的都是遍历器对象。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。let set = new Set(['red', 'green', 'blue']); for (let item of set.keys()) { console.log(item); } // red // green // blue for (let item of set.values()) { console.log(item); } // red // green // blue for (let item of set.entries()) { console.log(item); } // ["red", "red"] // ["green", "green"] // ["blue", "blue"] //entries方法返回的遍历器,同时包括键名和键值 //所以每次输出一个数组,它的两个成员完全相等。
-
forEach()
Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。let set = new Set([1, 4, 9]); set.forEach((value, key) => { console.log(key + ' : ' + value) }) // 1 : 1 // 4 : 4 // 9 : 9
7.map
ES6提供了一种类似于Object的新的数据结构–Map(字典)。可以理解为是Object的超集,用来存放键值对的集合,但是“键” 的范围不限于字符串,可以是各种类型的值(包括对象),可以用来存储不重复的值。Map实现 了iterator接口,所以可以使用『扩展运算符』和「 for…of…」进行遍历。Map 的属性和方法:
1)size返回Map 的元素个数
2)set(key,value) 增加一个新元素,返回增加新元素的Map集合,可用于链式调用
3)get(key) 返回键名对象的键值
4)has(key) 检测Map中是否包含某个元素,返回boolean值
5)delete(key)删除某个键
6)clear() 清空集合
<script>
//声明一个空map
let m = new Map();
console.log(m);//Map(0) {}
//键与值都是字符串
m.set("name","chi");
//键、值都为对象
let key = {
name:"aa"
}
m.set(key,function(){
console.log("aa");
})
console.log(m);//Map(2) {"name" => "chi", {…} => ƒ}
//map的大小
console.log(m.size);//2
//获取
console.log(m.get("name"));//chi
console.log(m.get(key));//ƒ (){console.log("aa");}
//检测是否包含
console.log(m.has(key));//true
//遍历
/*
* 遍历结果(数组)
* (2) ["name", "chi"]
* (2) [{…}, ƒ]
*/
for(let v of m){
console.log(v);
}
console.log(...m);//(2) ["name", "chi"] (2) [{…}, ƒ]
//删除键为xx的map
m.delete(key);
console.log(m);//Map(1) {"name" => "chi"}
//清空
m.clear();
console.log(m);//Map(0) {}
</script>
-
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map的所有成员。Map 的遍历顺序就是插入顺序。
const map = new Map([ ['F', 'no'], ['T', 'yes'], ]); for (let key of map.keys()) { console.log(key); } // "F" // "T" for (let value of map.values()) { console.log(value); } // "no" // "yes" for (let item of map.entries()) { console.log(item[0], item[1]); } // "F" "no" // "T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" // "T" "yes" // 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); } // "F" "no" // "T" "yes" //例2: var myMap = new Map(); myMap.set('0', 'foo'); myMap.set(1, 'bar'); myMap.set({}, 'baz'); var mapIter = myMap.keys(); console.log(mapIter.next().value); // "0" console.log(mapIter.next().value); // 1 console.log(mapIter.next().value); // Object
8. 函数参数
1). 参数设置默认值
es6允许给函数参数设置默认值
<script>
function add1(a,b,c){//c的值为undefined
return a+b+c;
}
let result1 = add1(1,2);
console.log(result1);//NaN
//1. 设置形参初始值,具有默认值的参数一般位置比较靠后
function add2(a,b,c=3){
return a+b+c;
}
let result2 = add2(1,2);
console.log(result2);//6
</script>
- 函数传参与解构赋值结合
/*
<script>
function connect({host,username,password,port}){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({
host:"127.0.0.1",
username:"root",
password:"root",
port:3306
})
</script>
*/
<script>
function connect({host='127.0.0.1',username,password,port=3306}){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({
username:"root",
password:"root"
})
</script>
2). rest参数
es6 引入rest参数,代替arguments,用于获取函数的参数
<script>
//es5
function test1(){
console.log(arguments);
}
//类数组对象Arguments(3) ["张颜齐", "姚琛", "任豪", callee: ƒ, Symbol(Symbol.iterator): ƒ]
test1("张颜齐","姚琛","任豪");
//es6的rest参数,rest参数必须要放在参数的最后
function test2(a,b,...args){
console.log(a);
console.log(b);
console.log(args);
}
//娜娜
//小迟
//数组(3) ["张颜齐", "姚琛", "任豪"]
test2("娜娜","小迟","张颜齐","姚琛","任豪");
</script>
9. 箭头函数
es6允许使用箭头函数(=>)定义函数。
//声明一个函数
注意:
- 允许给函数参数设置初始值
- 箭头函数本身不创建this,也可以说箭头函数本身没有this,但是在它声明时可以捕获其声明环境(全局和函数两种环境)的this供自己使用。this一旦被捕获,以后将不再变化。(箭头函数的this是静态的,this始终指向函数声明所在的作用域的this的值),可以用来解决闭包中使用普通函数导致的this指向不明确的问题
<script>
function person() {
console.log(this);
(() => {
console.log(this);
})();
}
person();
/*
* 执行结果:
* window
* window
*/
let P = {name:"迟"};
person.call(P);
/*
* 执行结果:
* {name: "迟"}
* {name: "迟"}
*/
const shape = {
radius: 10,
diameter() {
return this.radius * 2;
},
perimeter: () => 2 * Math.PI * this.radius // 该箭头函数的声明环境是全局环境,所以this指向window
};
shape.diameter(); // 20
shape.perimeter(); // NaN
</script>
- 箭头函数没有构造函数 constructor,所以也不能使⽤ new 来调
⽤,即箭头函数不能作为构造函数来实例化对象,会报错。- 箭头函数没有原型对象。
- 箭头函数不能使用arguments变量(es5中函数内部有一个arguments变量来保存实参),使用rest参数
- 箭头函数的简写:1)当形参有且只有一个的时候可以省略小括号。2)当代码体只有一条语句的时候可以省略大括号,return 关键字也必须省略,函数的执行结果就是函数返回值。
let pow = n =>n*n;
console.log(pow(9));//81
10.数组方法扩展
(1).Array.from()方法
把一个类数组转换成真正的数组,该方法的返回值是一个数组。
<script>
function test1(){
console.log(arguments);
}
//类数组:有下标有length
test1(1,2,3);//类数组Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
function test2(){
let arr = Array.from(arguments)
console.log(arr);
}
test2(1,2,3);//数组(3) [1, 2, 3]
</script>
(2).Array.of()方法
将所有参数组成一个数组并返回
<script>
let arr = Array.of(1,'2',[1,3]);
console.log(arr);//(3) [1, "2", Array(2)]
</script>
(3).find()方法
查找数组中满足要求的第一个元素,没有则返回undefind【参数是一个回调函数】
<script>
let arr = [1,2,5,6];
let val = arr.find((item)=>{
return item >= 5;
});
console.log(val);//5
</script>
(4).findIndex()方法
查找数组中满足要求的第一个元素的下标索引,没有则返回-1【参数是一个回调函数】
<script>
let arr = [1,2,5,6];
let index = arr.findIndex((item)=>{
return item >= 5;
});
console.log(index);//2
</script>
11. 对象的简单书写
es6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
let name = '迟娜';
const people = {
name,
hobby(){
console.log("看书");
}
}
console.log(people);
12.对象扩展
-
Object.is(value1,value2)判断两个值是否完全相等。
console.log(NaN === NaN);//false console.log(Object.is(NaN,NaN));//true
-
Object.assign(obj1,obj2) 对象合并,obj2中的属性值会将obj1中同名的属性值覆盖掉
const obj1 = { name:"小迟", age:18, sex:"女", like:"玩手机" } const obj2 = { name:"小迟", age:18, sex:"男", hobby:"看书" } console.log(Object.assign(obj1,obj2)); //{name: "小迟", age: 18, sex: "男", like: "玩手机", hobby: "看书"}
-
Object.setPrototypeOf(obj1,obj2)设置obj2是obj1的原型对象
Object.getPrototypeOf(obj)获取obj的原型对象const school = { name:"山东大学" } const cities = { xiaoqu : ["济南","威海","青岛"] } Object.setPrototypeOf(school,cities); console.log(Object.getPrototypeOf(school));//{xiaoqu: Array(3)} console.log(school);//{name: "山东大学"}
-
Object.freeze()方法
// *引用类型的内部属性值无法被常量化 const obj = { teacher: 'aa', age: 18 } obj.teacher = 'bb'; console.log(obj.teacher) //'bb' // * 引用类型如何冻结 // Object.freeze(); Object.freeze(obj); obj.teacher = 'cc'; // 不会改变值,并且不会报错 console.log(obj.teacher) //'bb' const obj2 = { teacher: '云隐', leader: '黄小杨', zhuawa: ['部部', '小可'] }; Object.freeze(obj2); // freeze无法冻结嵌套引用类型 // 思路: 嵌套遍历并且逐层freeze function deepFreeze(obj = {}) { Object.freeze(obj); // 兼容对象和数组 (Object.key(obj) || []).forEach(key => { if(type obj[key] === 'object') { deepFreeze(obj[key]); } }) }
13.class
14. Symbol
Symbol是es6引进的一种新的基本数据类型(六种基本数据类型:Number、String、Null、Boolean、Undefined、Symbol),表示独一无二的值,唯一性对用户来说是不可见的。注: 遇到唯一性的场景时要想到 Symbol
注意:
- Symbol的值是唯一的,可以用作属性名(作为对象属性名时,不能用点运算符,可以用 [ ] ),用来解决命名冲突的问题,并且可以有效的避免对象属性被修改。比如,在某个class类中定义一个内部方法,可以使用Symbol用作方法名,避免子类对该方法进行重写。
const myMethod = Symbol('myMethod') class myClass { constructor(context) { // 初始化 } // myMethod [myMethod](options) {} doSomeThing(event) { if (event) { this[myMethod]() } } } export default myClass
- Symbol值不能与去他数据进行运算和比较
- Symbol定义的对象属性不能使用for…in 循环遍历,但是可以使用Reflect:ownKeys来获取对象的所有键名。
<script>
//1创建Symbol
let s = Symbol();
//2创建Symbol,传入描述字符串,
//传入相同的值,创建的也不是同一个Symbol
let s2 = Symbol("这是Symbol");
let s3 = Symbol("这是Symbol");
console.log(s2 === s3);//false
console.log(Symbol.keyFor(s2))//undefined
//3创建Symbol,传入描述字符串,
//传入相同值,创建的是同一个Symbol
let s4 = Symbol.for("这是Symbol");
let s5 = Symbol.for("这是Symbol");
console.log(s4 === s5);//true
console.log(Symbol.keyFor(s4))//这是Symbol
</script>
-
重点:Symbol.iterator-- 迭代器
<script> /*let obj = { a:1, b:2, c:3 } //报错:obj对象没有Symbol.iterator属性,不能遍历 for(let val of obj){ console.log(val); } */ /* 迭代器基础语法 obj[Symbol.iterator]=function(){ return { next(){ return { //循环过程输出的值 value:'1', //false表示没有完成,true表示完成 done:false } } } } */ //*******添加迭代器*********** let obj = { a: 1, b: 2, c: 3 }; obj[Symbol.iterator] = function(){ // 迭代协议 let values = Object.values(obj);//(3) [1, 2, 3] let index = 0; return { next(){ if(index >= values.length){ return { done: true } } else { return { done: false, value:values[index++] } } } } }; for(let val of obj){ console.log(val); } //结果: //1 //2 //3 </script>
15.生成器(Generator)
生成器(Generator)是JavaScript ES6引入的特性,用来解决异步编程的一种方式。Generator 最大的特点就是可以控制函数的执行,它让我们可以分段执行一个函数。生成器(Generator)就是一个自带遍历器(Iterator)的函数。执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为
function* printOneToThree() {
//yield 放弃
//实质上将该函数以yield为界分解成若干小函数,每走一个next就执行一个小函数
yield 1;
yield 2;
yield 3;
}
let iterator= printOneToThree();
//我们每调用一次iterator.next()语句,就会运行一个yield表达式,直到所有的yield表达示全部运行完毕。做到分段执行一个函数
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
yield
yield既可以传参,也可以返回
<script>
function * gen(arg) {
console.log(arg);//AAA
let one = yield 1;
console.log(one);
let two = yield 2;
console.log(two);
let three = yield 3;
console.log(three);
}
let iterator= gen('AAA');
//我们每调用一次iterator.next()语句,就会运行一个yield表达式,
//直到所有的yield表达示全部运行完毕。做到分段执行一个函数
console.log(iterator.next()); // {value: 1, done: false}
//next方法可以传入实参,第二条next的实参作为第一条yield的返回值
console.log(iterator.next('BBB')); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next('CCC')); // {value: undefined, done: true}
</script>
生成器应用
1). 异步编程–定时器操作
<script>
//一秒钟输出111,
//之后两秒钟输出222,
//之后三秒钟三秒钟输出333
/*
setTimeout(function(){
console.log('111');
setTimeout(function(){
console.log('222');
setTimeout(function(){
console.log('333');
},3000);
},2000);
},1000);
*/
//改写上述回调地狱
function time1(){
setTimeout(function(){
console.log('111');
it.next();
},1000);
}
function time2(){
setTimeout(function(){
console.log('222');
it.next();
},2000);
}
function time3(){
setTimeout(function(){
console.log('333');
it.next();
},3000);
}
function * gen(){
yield time1();
yield time2();
yield time3();
}
let it = gen();
it.next();
</script>
16.promise
17. Proxy
在 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。
Proxy 是 ES6 中新增的功能。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问或者操作,都必须先通过这层拦截。这样可以针对Proxy实例进行操作,而不是直接针对目标对象target进行操作。
//target 代表需要添加代理的对象
//handler 用来自定义对象中的操作,比如可以用来自定义 set 或者 get 函数。
let p = new Proxy(target, handler)
-
注意,要使得Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象target进行操作。
如果handler没有设置任何拦截,那就等同于直接通向原对象。//例: var proxy = new Proxy({}, { get: function(target, propKey) { console.log(propKey); } }); proxy.time //time proxy.name //name
18. Reflect
Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。
-
将原生的一些零散分布在Object、Function或者全局函数里的方法(如apply、delete、get、set等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
-
修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
// 老写法 try { Object.defineProperty(target, property, attributes); // success } catch (e) { // failure } // 新写法 if (Reflect.defineProperty(target, property, attributes)) { // success } else { // failure }
-
让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
-
Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础,进行扩展。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
var loggedObj = new Proxy(obj, { get(target, name) { console.log('get', target, name); return Reflect.get(target, name); }, deleteProperty(target, name) { console.log('delete' + name); return Reflect.deleteProperty(target, name); }, has(target, name) { console.log('has' + name); return Reflect.has(target, name); } });
Reflect静态方法
Reflect对象一共有 13 个静态方法。这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value,receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target,prototype)
19. babel
babel是一个 ES6 转码器,可以将 ES6 代码转为 ES5 代码,以便兼容那些还没支持ES6的平台。