Es6
let、const
let
- 不可重复声明变量
- 暂时临时死域
- 作用域块
const
- 必须先赋值
- 不可重复声明变量
- 对于纯数字、字符、等基本结构的话,不可更改,但可以更改数组里面的元素、对象里面的 key
- 只能去改变引用类型 (object array),无法取改变基本类型 (
boolean
、number
、string
、null
、undefined
、symbol
、bigint
(Chrome 67+开始))
var
- var 声明,存在变量提升问题
- var 是全局变量声明的方式
for (var i = 0; i < 5; i++) {
}
console.info(i); // 5
for (let i = 0; i < 5; i++) {
}
console.info(i); // 抛出未定义 且 for 括号和 大括号是不同的作用域
数组
以下 为 es6 引入的新特性
特性 | 静态方法 | 实例方法 | 描述 |
---|---|---|---|
Array.from() | |||
Array.of() | 替代 Array() 方法,强化一致性 | ||
copyWithin() | 会修改原始数组 - 性能优化 - 音频处理 | ||
find() | 返回查找的第一项,存在 undefined | ||
findIndex() | 找到返回索引值,否则 -1 | ||
findLast() | |||
findLastIndex() | |||
fill() | |||
entries() | |||
keys() | |||
values() | |||
includes() | |||
flat() | |||
flatMap() | |||
at() | |||
toReversed() | |||
toSorted() | |||
toSpliced() | |||
width() | |||
group() | |||
groupToMap() | |||
数组空位 | |||
Array.from()
Array.of()
find()
和下面方法是一组类似的函数:
findIndex()
findLast()
findLastIndex()
同样的,filter()
及其相近,只是 filter()
返回数组,存在多个,find()
返回第一个匹配的元素。
和 filter()
区别:
filter() | find() | |
---|---|---|
时间复杂度 | O(n) | 最佳 O(1) 、最差 O(n) |
链式调用 | 更具有多段处理能力 | 单个 |
返回值 | 始终数组 | undefined 的额外判断 |
性能考虑 | 性能差 | 性能优先 |
findIndex()
返回匹配得第一项的索引值,否则 -1
。
indexOf() | find() | |
---|---|---|
查找简单 | 最佳✅ | ⚠️一般 |
查找属性 | ❎ | ✅ |
复杂度条件 | ❎ | ✅ |
返回值 | 一样 | 一样 |
findLast()
从后往前,其他跟 find()
一样
findLastIndex()
从后往前,其他跟 findIndex()
一样
fill()
用于填充数组。填充的元素可以是任意类型,
参数:
value
start
end
[1, , 2, 3, 4, 5, 6, 7, 8, 9, 10].fill(7)
// [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
new Array(3).fill(6)
// [6, 6, 6]
Array.of(1, 2, 3).fill(8)
// [8, 8, 8]
keys()
- 返回迭代器,
for ... of
语句可以遍历
const a = ['a', 'b'].keys()
console.log(a)
// Array Iterator {}...
Array.from(a)
// [0,1]
values()
- 返回迭代器,
for ... of
语句可以遍历
const b = ['a', 'b'].values()
console.log(b)
// Array Iterator {}...
console.log(b.next())
// {value:"b",done:false}
Array.from(b)
// ["a","b"]
console.log(b.next())
// {value:undefined,done:true}
entries()
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
// 也可以适用 next() 方法获取
console.log(entries.next().value); // [0, 'a']
includes
与字符串引入的 includes
一样,返回布尔值,表示是否包含给定的值。
- 更加严格,比如
NaN
误判
参数:
value
start
es5 之前达到判断效果的方法:
const arr = [1, 2, 3];
const el = 2;
if (arr.indexOf(el) !== -1) {
// ...
}
// or
if (~arr.indexOf(el)) {
// ...
}
兼容性方案:
const constains = (() => {
return Array.prototype.includes
? (arr, value) => err.includes(value)
: (arr, value) => arr.some(el => el === value)
})
constains(['a', 'b'], ' a')
flat()
拍平数组,默认拍平一层,无限层,则入参是 Infinity
。
- 默认自动跳过空位
- 不改变原数组
flatMap()
执行一次 map()
函数,然后对返回值再 flat()
,返回新数组。
处理嵌套数据结构:
const books = [
{title: "Book 1", tags: ["fantasy", "adventure"]},
{title: "Book 2", tags: ["sci-fi"]}
];
// 获取所有标签的扁平数组
const allTags = books.flatMap(book => book.tags);
// ["fantasy", "adventure", "sci-fi"]
字符串处理:
const sentences = ["Hello world", "Goodbye universe"];
const words = sentences.flatMap(sentence => sentence.split(' '));
// ["Hello", "world", "Goodbye", "universe"]
at()
允许使用负数索引,超出返回,返回 undefined
。
const arr = [1, 2, 3]
arr.at(2) // 3
arr.at(-1) // 3
toReversed()
不改变原始数组,返回一个新数组。
对比 | es6 | es5 |
---|---|---|
toReversed() | reverse() |
toSorted()
不改变原始数组,返回一个新数组。
对比 | es6 | es5 |
---|---|---|
toSorted() | sort() |
toSpliced()
执行位置,删除指定数量成员,并插入心智,不改变原始数组,返回一个新数组。
对比 | es6 | es5 |
---|---|---|
toSpliced() | splice() |
with()
用来将指定位置成员替换为新值,不改变原始数组,返回一个新数组。
对比 | es6 | es5 |
---|---|---|
with() | splice(index,1,value) |
group() 暂不兼容
数组成员分组,返回一个新对象,字符串分组,使用 group()
。
const array = [1, 2, 3, 4, 5];
array.group((num, index, array) => {
return num % 2 === 0 ? 'even' : 'odd';
});
// { odd: [1, 3, 5], even: [2, 4] }
groupToMap() 暂不兼容
数组成员分组,返回一个新 Map 对象,key 可以是各种值,对象分组使用 groupToMap()
。
const array = [1, 2, 3, 4, 5];
const odd = {odd: true};
const even = {even: true};
array.groupToMap((num, index, array) => {
return num % 2 === 0 ? even : odd;
});
空位处理
es5 中:
方法 | 跳过空位 |
---|---|
forEach() | ✅ |
filter() | ✅ |
reduce() | ✅ |
every() | ✅ |
some() | ✅ |
map() | ✅ 跳过保留 -✅保留这个值 |
join() | 视为 undefined , 和 null 处理为空字符串 |
toString() | 视为 undefined , 和 null 处理为空字符串 |
es6 中,空位视为 undefined
。
处理空位的方法或语句:
Array.from()
...拓展符
copyWithin()
fill()
for ... of
将空位视为 undefined
:
entries()
keys()
values()
find()
findIndex()
sort 稳定性
const arr = [
{id: 1, val: 2},
{id: 2, val: 2},
{id: 3, val: 1}
];
- 稳定排序:关键字相同项目后,排序前后的顺序不变,默认
// 结果可能为:
// [
// {id: 3, val: 1},
// {id: 2, val: 2}, // id: 2 跑到 id: 1 前面
// {id: 1, val: 2}
// ]
- 不稳定排序:关键字排序后,再排序
- 多重排序有问题
// 新版引擎输出(稳定):
arr.sort((a, b) => a.val - b.val);
// 结果保证:
// [
// {id: 3, val: 1},
// {id: 1, val: 2}, // id: 1 和 id: 2 顺序不变
// {id: 2, val: 2}
// ]
箭头函数
箭头函数 this 是父作用域的 this,不是调用时的 this
- 如何知道当前的父作用域
箭头函数不能作为构造函数,不能使用 new
箭头函数没有 arguments 和 caller、callee
箭头函数通过 call、apply 调用不会改变 this 指向,只会入参
箭头函数没有原型属性
const fn = () => {
};
console.log(fn.prototype);
箭头函数不能作为 Generator 函数,不能使用 yield 关键字
箭头函数返回对象需要加括号
箭头函数不能在 ES6 class 中声明的方法为实例方法,不是原型方法
//demo1
class Super {
sayName() {
//do some thing here
}
}
//通过Super.prototype可以访问到sayName方法,这种形式定义的方法,都是定义在prototype上
var a = new Super();
var b = new Super();
a.sayName === b.sayName; //true
//所有实例化之后的对象共享 prototype 上的sayName方法
//demo2
class Super {
sayName = () => {
//do some thing here
};
}
//通过Super.prototype访问不到sayName方法,该方法没有定义在prototype上
var a = new Super();
var b = new Super();
a.sayName === b.sayName; //false
//实例化之后的对象各自拥有自己的sayName方法,比demo1需要更多的内存空间
- 多重箭头函数是高阶函数,相当于内嵌函数,就是闭包函数
函数
- 剩余参数 (rest 参数) 的表示法,同样,items 是最后一个参数
// 此时的items 是一个数组
function push(array, ...items) {
items.forEach(function (item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
console.log(a); //[1,2,3]
class 类
特殊的函数
=>类表达式
、类声明
- 只能有且只有一个
constructor
方法 - 一个构造函数可以使用
super
关键字来调用一个父类的构造函数 static
关键字定义一个类的静态方法。可以不需要实例化该类,但不能通过一个类实例调用静态方法
//demo1
class Home {
//构造函数
constructor(height, width) {
this.height = height;
this.width = width;
}
// getter,隐藏的get属性
get area() {
return this.all();
}
//methods
all() {
return this.height * this.width;
}
}
const h = new Home(360, 480);
console.log(h.height); //360
console.log(h.area); //就可以调用
console.log(h.area()); //报错。get 的属性值不是一个function
// demo2 `demos` 关键字定义一个类的静态方法。可以不需要实例化该类,但不能通过一个`类实例调用静态方法`
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
//demos 关键字,顶一个类的静态方法
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy); //参数平方和的平方根
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));
// 相当于Point.distance({x:5,y:10},{x:10,y:5})
- 用原型和静态方法包装
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined
Animal.eat(); // class Animal
let eat = Animal.eat;
eat(); // undefined
//当然了。如果小改动了一下
speak.bind(obj)(); //这样就可以了~~
// demo2 ,知道结果可能如下,但不太理解这样的方式
function Animal() {}
Animal.prototype.speak = function () {
return this;
};
Animal.eat = function () {
return this;
};
let obj = new Animal();
let speak = obj.speak;
speak(); // global object
let eat = Animal.eat;
eat(); // global object
- Object.setPrototypeOf() 继承常规对象
- extends 创建子类
// 父类
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + 'Noise');
}
}
// 子类
class Dog extends Animal {
speak() {
//此处基础父类的this 属性name 值
console.log(this.name + 'by dog');
}
}
//实例化
const d = new Dog('LiLei');
d.speak();
- species
派生数组类。返回 Array 对象,允许覆盖默认的构造函数。类似
map()
返回默认构造函数的方法时,希望返回一个父 Array 对象,而不是 Arr,可以Symbol.species
class Arr extends Array {
static get [Symbol.species]() {
return Array;
}
}
const a = new Arr(1, 2, 3);
const mapped = a.map((x) => x * x);
console.log(mapped instanceof Arr);
console.log(mapped instanceof Array);
- supper
supper
关键字用于调用对象的父对象上的函数
// demo1 这个demo 看不出来什么
class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise');
return 2;
}
}
class Lio extends Cat {
speak() {
super.speak();
console.log(this.name + ' for Lio');
return 111;
}
}
const animal = new Lio('litter red');
/** demo2 super 简单应用 */
// 声明一个对象
const Family = {
name: 'Jo Home',
};
// 再生一个对象,内含一个函数`getName`
const main = {
getName() {
return super.name;
},
};
let home = main.getName();
console.log(home);
//以上这样做并没有什么卵用,但是如果使用了Object.setPrototypeOf(要设置在原型上的对象,prototype)
// 在home前面增加
Object.setPrototypeOf(main, Family);
/** demo3 关于class*/
supper.name;
// 等同于 属性
Object.getPrototypeOf(this).name;
// 等同于 方法
Object.getPrototypeOf(this).name.call(this);
Promise 对象
状态的变更
TIP
缺点:无法向外抛出错误移除,并主动中断这样的流程结果
const promise = new Promise((resolve, reject) => {
const a = 1;
if (a === 1) {
resolve('ddd');
} else {
reject('sss'); //最好是返回一个变量,不然某些环境下,会导致警告或者报错,可以是字符串、数组、对象,但只能是一个参数
}
});
promise()
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log();
});
resolve()
- 只能入参一个,但可以是
数组
、对象
- 只能入参一个,但可以是
reject()
- 只能入参一个,但可以是
数组
、对象
- 只能入参一个,但可以是
then()
catch()
finally()
all()
race()
Generator
async await
- generator 的语法糖
- Generator 的改进
- 内置执行器
- 更好的语义
- 更广的适用性
- 返回值是 promise
- async 函数的的返回值是 Promise 对象,
async
表示该函数内部有异步操作 - await 命令后可以是 Promise 对象和原始类型的值 (数值,字符串,布尔值,此时等同于同步操作)
- 如果包装成为一个函数,then 里面表示当遇到 await 是执行 then 然后才执行后面
- 如何使用
async/await
- 函数声明
- 函数表达式
- 对象的方法
- class 的方法
- 箭头函数方法
// demo1
async function all() {
return new Promise((resolve, reject) => {
let time1 = Math.random();
let time2 = Math.random();
// 第一个异步
setTimeout(() => {
console.log(1, time1 * 10 * 5000);
resolve(time1 * 10 * 5000);
}, time1 * 10 * 5000);
// 第二个异步
setTimeout(() => {
console.log(2, time2 * 10 * 5000);
resolve(time2 * 10 * 5000);
}, time2 * 10 * 5000);
});
}
all()
.then((res) => {
console.log(3, res);
})
.catch((err) => {
console.log(4, err);
});
/*************************************************/
// 第一个异步
async function all1() {
return new Promise((resolve) => {
let time1 = Math.random();
setTimeout(() => {
console.log(1, time1 * 10 * 1000);
resolve(time1);
}, time1 * 10 * 1000);
});
}
// 第二个异步
async function all2() {
return new Promise(() => {
let time2 = Math.random();
setTimeout(() => {
console.log(2, time2 * 10 * 1000);
}, time2 * 10 * 1000);
});
}
// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
console.log('a');
await all1();
console.log('b');
await all2();
console.log('c'); //这个不会执行,以为还在等待promise 的回来
}
all();
而以下代码呢?
// 第一个异步
async function all1() {
return new Promise((resolve) => {
let time1 = Math.random();
setTimeout(() => {
console.log(1, time1 * 10 * 1000);
resolve(time1 * 10 * 1000);
}, time1 * 10 * 1000);
});
}
// 第二个异步
async function all2() {
return new Promise((resolve) => {
let time2 = Math.random();
setTimeout(() => {
console.log(2, time2 * 10 * 1000);
resolve(time2 * 10 * 1000);
}, time2 * 10 * 1000);
});
}
// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
console.log('a');
await all1().then((res1) => {
console.log(res1);
});
console.log('b');
await all2().then((res2) => {
console.log(res2);
});
console.log('c');
}
all();
// 结论是
/*a
Promise {<pending>}
1 2824.509694408435
27 2824.509694408435
29 b
16 2 6266.805712440053
32 6266.805712440053
34 c*/
再看一下这个 结论: a 第 0s——10s 计时后,打印 1 10 10 异步一函数的 then 打印 b 第 10s——17s 计时后,打印 2 8 8 异步二函数的 then c 第 18s……
// 第一个异步
async function all1() {
return new Promise((resolve) => {
let time1 = 10;
setTimeout(() => {
console.log(1, time1);
resolve(time1);
}, time1 * 1000);
});
}
// 第二个异步
async function all2() {
return new Promise((resolve) => {
let time2 = 8;
setTimeout(() => {
console.log(2, time2);
resolve(time2);
}, time2 * 1000);
});
}
// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
let i = 0;
setInterval(() => {
console.log(i++);
}, 1000);
console.log('a');
await all1().then((res1) => {
console.log(res1);
});
console.log('b');
await all2().then((res2) => {
console.log(res2);
});
console.log('c');
}
all();
再看,把 async/await 里面有两个普通的定时任务会怎么样?
结论,此时 all1 与 all2 是异步任务了, a b c 0s-8s 计时 2 8 9s 10 s 1 10
// 第一个异步
async function all1() {
let time1 = 10;
setTimeout(() => {
console.log(1, time1);
}, time1 * 1000);
}
// 第二个异步
async function all2() {
let time2 = 8;
setTimeout(() => {
console.log(2, time2);
}, time2 * 1000);
}
// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
let i = 0;
setInterval(() => {
console.log(i++);
}, 1000);
console.log('a');
await all1();
console.log('b');
await all2();
console.log('c');
}
all();
// 以下声明都成立
function* a1() {}
function* a2() {}
function* a3() {}
function* a4() {}
a1();
a2();
a3();
a4();
function* hello() {
yield 'hello'; //yield 表达式
yield 'world'; //yield 表达式
return 'hello and world';
}
- 分段执行。
yield
表示暂停执行的标志,next
表示恢复执行 - es6 提供的异步编程解决方案。阮一峰 Generator 函数的语法
- 状态机,封装了多个内部状态
- 有
*
星号 function * a(){} - 函数体内部使用了 yield 表达式,定义不同的内部状态 (yield 产出的意思)
function a(){
yield 'hello'
}
var func = a()
Reflect
现阶段一些方法同时部署到 Object、Reflect 对象上,未来某些方法只能从 Reflect 上获取,比如 Object.defineProperty
- 合理化取值。比如 Object.defineProperty(obj,name,desc) 在无法定义属性时,报错,但 Reflect.defineProperty(obj,name,desc) 会返回 false
// 旧的方法
try {
Object.defineProperty(target, property, attributes);
// 成功的取值
} catch (e) {
// 失败
}
if (Reflect.defineProperty(target, property, attributes)) {
// 成功
} else {
// 失败
}
Object
操作都变成了函数行为,而不是一个操作符
// 旧的写法
'assign' in Object; //true
// 新写法
Reflect.has(Object, 'assign'); //true
// 旧的delete 操作符
var obj = {
name: 'Old',
};
delete obj.name;
// 新的函数式操作方法
Reflect.deleteProperty(obj, 'name');
- Reflect 对象的方法和 Proxy 对象的方法一一对象,只要在 Proxy 有,Reflect 就会有。
- 不管 Proxy 怎么修改默认行为,总可以在 Reflect 上获取默认行为
var obj = 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);
},
});
- proxy 的拦截操作,内部的偶调用对应的 Reflect 方法,保证原生行为能够正常执行
- 易阅读
// old
Function.prototype.apply.call(Math.floor, undefined, [1.6]); //1
// new
Reflect.apply(Math.floor, undefined, [1.6]); //1
Reflect 静态方法:一共 13 个
- Reflect.apply(target,thisArg,args)
- Reflect.construct(target,args)
- Reflect.get(target,name,receiver)
- Reflect.set(target,name,value,receiver)
- Reflect.defineProperty(target,name,dec)
- 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)
使用 Proxy 实现观察者模式
观察者模式 (Observer mode)
const queueObservers = new Set();
const observer = (fn) => queueObservers.add(fn);
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queueObservers.forEach((ob) => ob());
return result;
}
先定义一个 Set 集合,所有观察者函数都放进这个集合,observable 函数返回对象的代理,拦截赋值操作,拦截函数 set 中,启动执行所有观察者
字符串
模板字符串无法的打印 symbol
const sy1 = Symbol(11);
console.log(sy1);
console.log(`${sy1}`); // `Cannot convert a Symbol value to a string`