ES6 笔记
var、let与const
首先是 var 和 let 的区别。
let 声明的变量只在 let 命令所在的代码块内有效。
var 声明的变量式全局变量,在全局范围内有效。
const 声明一个只读的常量,一旦声明,常量的值就不能改变。
let 是在代码块内有效,var 是在全局范围内有效。
let 只能被声明一次,var 可以声明多次。
for 循环计数适合用 let。
let 不存在变量提升,var会变量提升。
let 和 const 会存在暂时性死区问题:也就是在声明之前赋值或者其他操作,会出现问题(TDZ)。
const 声明之后必须初始化,否则会报错。
字符串
字符的 Unicode 表示法:\u{20BB7}。
字符串的遍历接口:for...of (优点是可以识别大于0xFFFF的码点)。
模板字符串:使用 ` (反引号)包裹,可以在字符串中嵌入变量。
一些方法:
String.fromCharCode()方法,用于从 Unicode 码点返回对应字符String.raw()方法,ES6 还为原生的 String 对象,提供了一个raw()方法。该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。
...
模板字符串
解决了传统的获取dom进行添加标签定义过于冗余。
我们可以用 TAB 上面的反引号定义字符串,对于变量可以用 ${} 的形式添加。
函数
函数默认值、剩余参数
ES5中:
function add(a, b) {
a = a || 10;
b = b || 20;
return a + b;
}
console.log(add());
ES6中:
function add(a, b = 20) {
return a + b;
}
console.log(add(30));
默认的表达式也可以是一个函数
function add(a, b = getVal(5)) {
return a + b;
}
function getVal(val) {
return val + 5;
}
console.log(add(10));
剩余参数
ES5 利用 arguments 获取Obj内部元素:
function pick(obj) {
let res = Object.create(null);
for(let i = 1; i < arguments.length; i ++) {
res[arguments[i]] = obj[arguments[i]];
}
return res;
}
let book = {
title: 'ES6',
author: 'XiaoMo',
year: 2019
}
let bookData = pick(book, 'title', 'year');
console.log(bookData);
ES6 剩余参数:由三个点...和紧跟着的具名参数指定 ...keys
(剩余参数的数组是真数组,而arguments是伪数组)
function pick(obj, ...keys) {
let res = Object.create(null);
for (let i = 0; i < keys.length; i++) {
res[keys[i]] = obj[keys[i]];
}
return res;
}
let book = {
title: 'ES6',
author: 'XiaoMo',
year: 2019
}
let bookData = pick(book, 'title', 'year');
console.log(bookData);
函数之扩展运算符、箭头函数
剩余运算符:把多个独立的合并到一个数组中。
扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数。
const arr = [10, 20, 50, 30, 90, 100, 40];
console.log(Math.max(...arr));
箭头函数
使用 => 来定义
function(){} 等价于 ()=>{}
let add = function(a, b) {
return a + b;
}
let add = (a, b) => {
return a + b;
}
console.log(add(10, 20));
闭包+箭头函数+立即调用
let fn = (() => {
return () => {
console.log("Hello World!")
}
})();
fn();
箭头函数this指向和注意事项
ES6中使用箭头函数没有this绑定。
ES5中this指向,取决于该函数的上下文对象。
以下代码会出现问题:
let PageHandle = {
id: 123,
init: function() {
document.addEventListener('click', function(event) {
// this.doSomeThings is not a function
this.doSomeThings(event.type);
})
},
doSomeThings:function(type) {
console.log(`事件类型:${type}, 当前ID:${this.id}`)
}
}
console.log(PageHandle);
PageHandle.init();
ES5的解决办法:
let PageHandle = {
id: 123,
init: function() {
document.addEventListener('click', function(event) {
// this.doSomeThings is not a function
this.doSomeThings(event.type);
}.bind(this), false)
},
doSomeThings:function(type) {
console.log(`事件类型:${type}, 当前ID:${this.id}`)
}
}
PageHandle.init();
ES6的解决方法:
let PageHandle = {
id: 123,
init: function() {
// 箭头函数没有 this 指向,箭头函数内部 this 值只能通过查找作用域链来获取 this。
document.addEventListener('click', (event) => {
this.doSomeThings(event.type);
}, false)
},
doSomeThings:function(type) {
console.log(`事件类型:${type}, 当前ID:${this.id}`)
}
}
PageHandle.init();
一旦使用箭头函数,那么当前函数内部就没有作用域了。
- 箭头函数中不会存在 arguments。
- 箭头函数不能使用 new 来实例化对象(function函数是一个对象,但是箭头函数不是对象)。
ES6 解构赋值
解构赋值是对复制运算符的一种扩展。
它正对数组和对象来进行操作。
优点:代码书写上简洁易读。
let node = {
type: 'iden',
name: 'foo',
}
let {type, name} = node;
console.log(type, name);
let obj = {
a: {
name: 'zhangsan'
},
b:[],
c:'hello, world'
}
// 不完全解构
let {a} = obj;
// 可以使用剩余运算符
let {a, ...res} = obj;
console.log(res);
// 默认值
let {a, b = 30} = {a: 20};
对象的扩展功能
ES6直接写入变量和函数作为对象的属性和方法。
对于对象的名与值相同的,可以省略掉。
const person = {
name,
age,
sayName() {
console.log(this.name);
}
}
person.sayName();
对象的方法
1.is() 类似于 ==== 比较两个值是否严格相等。
console.log(NaN === NaN); 比较失效。
console.log(Object.is(NaN, NaN)); 比较成功。
2.assign()
对象的合并。
Object.assign(target, obj1, obj2);
将obj1, obj2 合并进 target。
ES6 Symbol
最大用处:用来定义对象的私有变量。
使用场景:
- 作为属性名
let sy = Symbol("key1");
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject); // {Symbol(key1): "kk"}
// 写法2
let syObject = {
[sy]: "kk"
};
console.log(syObject); // {Symbol(key1): "kk"}
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject); // {Symbol(key1): "kk"}
Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for...in 、 for...of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);
for (let i in syObject) {
console.log(i);
} // 无输出
Object.keys(syObject); // []
Object.getOwnPropertySymbols(syObject); // [Symbol(key1)]
Reflect.ownKeys(syObject); // [Symbol(key1)]
Map 与 Set
Map 与 Object 的区别
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
- Map 中的键值是有序的(FIFO原则),而添加到 Object 的值不是。
- Map 的键值对个数可以从 size 属性获取,但是 Object 只能手动计算。
- Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名的冲突。
Map 对象
Map类型是键值对的有序列表、键和值是任意类型。
let map = new Map();
map.set('name', '张三');
map.set('age', 20);
map.has('name');
map.clear();
Map的迭代
for...of
forEach()
Set 对象
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
创建一个空的集合:
let set = new Set();
添加元素:
set.add(1);// 添加值可以是任意类型
删除元素:
set.delete(1);
校验某个值是否在 set 集合中:
set.has(1);
set中的forEach没有意义,因为key和value相同。
将set转化成数组:
let set2 = new Set([1, 2, 3, 3, 3, 4]);
let arr = [...set2];
console.log(arr);
set 中对象的应用无法被释放。
let set3 = new Set(), obj = {};
set3.add(obj);
WeakSet 解决的这一问题,但是丢失了很多Set的属性。
- 不能传入非对象类型的参数。
- 不可迭代。
- 不能forEach。
- 没有size属性。
数组的扩展方法
from() 将为数组转化为真正的数组。
function add() {
let arr = Array.from(arguments);
console.log(arr);
}
add(1, 2, 3);
也可以使用[...arguments],更加方便。
from() 还可以接受第二个参数,用来对每个元素进行处理(回调函数)。
let lisarr = Array.from(lis, item => item.textContent);
console.log(lisarr);
of() 将一组值,转化为数组。
console.log(Array.of(3, 11, 20, [1, 2, 3], {id: 1}));
copyWithin() 数组内部将指定位置的元素复制到其他位置。
let arr = [1, 2, 3, 4, 8, 9, 10].copyWithin(0, 3);
console.log(arr);
find()、findIndex()查找到第一个符合条件的值。
接收一个回调函数。
let num = [1, 2, -10, -20, 9, 2].find(n => n < 0);
console.log(num);
entries() keys() values() 返回一个遍历器。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
entries() 包含了 keys() 和 values() 。
for (let [index, ele] of ['a', 'b'].entries()) {
console.log(inedx, ele);
}
includes() 返回一个布尔值,表示某个数组中是否包含给定的值。
console.log([1, 2, 3].includes(2));
之前使用的是 indexof() 。
迭代器
Iterator 是一种新的遍历机制。
有两个核心:
- 迭代器是一个接口,能快捷的访问数据。通过Symbol.iterator来创建迭代器,通过迭代器的next()方法,获取迭代后的结果。
- 迭代器适用于遍历数据结构的指针(数据库的游标)。
const items = ['one', 'two', 'three'];
const it = items[Symbol.iterator]();
console.log(it.next());//{value: "one", done: false} done 如果为 false则还有值,为true则表示遍历到最后了。
console.log(it.next());
console.log(it.next());
console.log(it.next());
生成器 Generator
与迭代器紧密相关。
generator 函数,可以通过yield关键字,将函数挂起,为了改变执行流程提供了可能性,同时为了做异步编程,也提供了方案。
他与普通函数的区别:
1.function后面,函数名之前有个 *
2.只能在函数内部使用 yield,让函数挂起。
function* func() {
console.log('start');
yield 2;
console.log('second');
yield 3;
console.log('end');
}
let fn = func();
// console.log(fn);
console.log(fn.next());
console.log(fn.next());
console.log(fn.next());
总结:generator 函数是分段执行的,yield 语句是暂停执行,而 next() 恢复执行。
function* add() {
console.log('start');
let x = yield '2';
console.log('one:' + x);
let y = yield '3';
console.log('two:' + y);
return x + y;
}
const fn = add();
console.log(fn.next()); // {value: '2', done: false}
console.log(fn.next(20));// {value: '3', done: false}
console.log(fn.next(30));// {value: 50, done: true}
使用场景:为不具备 Interator 接口的对象提供了遍历操作
function* objectEntries(obj) {
// 获取对象的所有 key 保存到数组 [name, age]
const propKeys = Object.keys(obj);
for (const propkey of propKeys) {
yield [propkey, obj[propkey]]
}
}
const obj = {
name: 'XiaoMo',
age: 18,
}
obj[Symbol.iterator] = objectEntries;
console.log(obj);
for(let [key, value] of objectEntries(obj)) {
console.log(`${key}:${value}`);
}
Generator 在异步编程中的应用
发送 ajax 请求,可能会陷入到 ”回调地狱“中。
使用 Generator 同步化。
function* main() {
let res = yield request('url')
console.log(res);
console.log('数据请求完成, 可以继续操作')
}
const it = main();
it.next();
function reauest(url) {
$.ajax({
url,
method:'get',
sucess(res) {
it.next(res);
}
})
}
加载Loading...页面、数据加载完成...(异步操作)、Loading关闭掉。
function* load() {
loadUI();
yield showData();
hideUI();
}
let itLoad = load();
itLoad.next();
function loadUI() {
console.log('加载loading...页面');
}
function showData() {
setTimeout(() => {
console.log('数据加载完成');
itLoad.next();
}, 1000);
}
function hideUI() {
console.log('隐藏loading...页面');
}
Proxy && Reflect
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。
(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
Promise
异步操作
相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果。
各种异步操作都可以用同样的方法进行处理 axios。
特点:
- 对象的状态不受外界影响、处理异步操作,三个状态:Pading(进行)、Resolved(成功)、Rejected(失败)。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
let pro = new Promise(function(resolved, rejected) {
// 执行异步操作
let res = {
code: 201,
data: {
name: 'XiaoMo'
},
error: 'GG'
}
setTimeout(() => {
if(res.code === 200) {
resolved(res.data);
} else {
rejected(res.error);
}
}, 1000);
})
console.log(pro);
pro.then((val) => {
console.log(val);
}, (err) => {
console.log(err);
})
.then => 捕获(padding => resolved)
.catch => 捕获(padding => rejected)
function timeOut(ms) {
return new Promise ((resolved, rejected) => {
setTimeout(() => {
resolved('Hello promise success!!')
}, ms);
})
}
timeOut(2000).then((val) => {
console.log(val);
})
Promise封装ajax
const getJOSN = function(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
console.log(this);
}
})
}
getJOSN('https://free-api.heweather.net/s6/weather/now').then((data) => {
console.log(data);
},(error) => {
console.log(error);
})
then() 方法:
then() 第一个参数是reslove回调函数,第二个是可选的为 reject 状态的回调函数。
可以使用:
getJSON().then((data) => {
console.log(data);
}).then((null, error) => {
console.log(error);
})
resolve()
let p = Promise.resolve('foo');
p.then((data) => {
console.log(data);
})
all()
let promise1 = new Promise((resolve, reject) => {});
let promise2 = new Promise((resolve, reject) => {});
let promise3 = new Promise((resolve, reject) => {});
let p4 = Promise.all([promise1, promise2, promise3]);
p4.then(() => {
// 三个都成功才会执行。
}).catch(err => {
// 如果有一个失败 则失败。
})
race() 某个异步请求设置超时事件,并且在超时后执行响应的操作。
function requestImg(imgSrc) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
resolve(img);
}
img.src = imgSrc;
});
}
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 3000);
})
}
Promise.race([requestImg('https://pic.616pic.com/ys_img/00/25/66/ydJbBZaUsR.jpg'), timeout()]).then(data => {
console.log(data);
document.body.appendChild(data);
}).catch(err => {
console.log(err);
})
done() finally() 不管是 resolve 还是 reject 都会执行。
可以用来关闭弹窗。
async 异步操作
作用:使得异步操作更加方便。
基本操作 async 它会返回一个 Promise 对象 then、catch。
async 是 Generator 的一个语法糖。
async function f() {
let s = await 'hello world';
let data = await s.split('');
return data;
}
f().then(v => {
console.log(v);
}).catch(e => {
console.log(e);
})
await 命令一定在 async 函数里面使用。
如果 async 函数中有多个 await 那么 then 函数会等待所有的 await 指令运行完的结果才去执行。
async function f2() {
try{
await Promise.reject('出错了');
} catch (error) {
}
return await Promise.resolve('hello');
}
f2().then(v => {
console.log(v);
}).catch(e => {
console.log(e);
})
Class 类的用法
class Person {
// 实例化的时候会立即被调用的方法
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
return this.name;
}
}
let p1 = new Person('XiaoMo', 18);
console.log(p1.sayName())
// 通过 Object.assign() 方法一次性向类中添加多种方法
Object.assign(Person.prototype, {
sayName() {
return this.name,
}
})
类的继承
使用关键字 extends
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
return this.name;
}
sayAge() {
return this.age;
}
}
class Dog extends Animal {
constructor(name, age, color) {
super(name, age);
this.color = color;
}
}
ES6 的模块化实现
ES6 模块功能主要有两个命令构成:export 和 import。
export 用于规定模块的对外接口 import 用于输入其他模块提供的接口。
index.js:
export const name = 'XiaoMo';
export const age = 18;
export function sayName() {
return 'my Name is XiaoMo';
}
对于一次性抛出:
cosnt name = 'XiaoMo';
...
export {
name, age, sayName
}
对于全部导入:
import * as f from './index.js'

浙公网安备 33010602011771号