变量的解构赋值
数组的解构赋值:
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,变量的值就等于undefined。事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
结构赋值可以指定默认值,但如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。例如:
function f(){
console.log('aaa');
}
let [x=f()] = [1]; //x=1 且 不会输出aaa
对象的解构赋值:
对象的解构赋值与数组有一个重要的不同:数组的是按照次序(我理解为index)对应才能赋上值。对象的解构赋值没有次序但需要变量名与对象的属性名相同。
如果变量名与属性名不一致,必须写成下面这样。下面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。(相当于给对象属性名起个别名,作为变量名用,赋值也是赋给变量名)
var{foo模式(对象属性名): baz变量名} = {foo:'aaa',bar:'bbb'}
baz // "aaa"
这样写时,baz的声明和赋值是一起的,对于let和const来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。
let foo;
let {foo} = {foo: 1}; // SyntaxError: Duplicate declaration "foo"
函数参数的解构赋值
下面代码看似传入一个数组,但是传入参数的那一刻,数组就被结构成变量x,y了
function add([x,y]){
return x+y;
}
add([1,2]);
字符串
JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。
字符串的遍历器接口
ES6为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历。
for (let x of "text"){console.log(x)} // "f" "o" "o"
for(let x of text){console.log(x)} //Uncaught TypeError: text is not iterable
字符串API
indexOf(): 可以用来确定一个字符串是否包含在另一个字符串中。
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
这三个方法都支持第二个参数,表示开始搜索的位置。使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
repeat(次数):返回一个新字符串,表示将原字符串重复n次。参数如果是小数,会被取整。如果repeat的参数是负数或者Infinity,会报错。但是,如果参数是0到-1之间的小数,则等同于0,参数是字符串,则会先转换成数字。
padStart(),padEnd():如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。
Iterator和for…of循环
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
为对象添加Iterator接口:
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
//调用obj[Symbol.iterator]后返回对象,此对象有next()方法
return {
next() {
if (index < self.data.length) {
//next()方法的返回值对象,对象.value得到值,value.done得到是否是最后一个属性,当true时是最后一个,for...of 看到true就不执行了,这行也不打印。
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
Promise
1.API:
Promise.resolve():将promise对象的状态由peding转换为fulfilled成功,返回值为一个promise实例对象,是属于Promise函数对象的方法,不是实例对象的方法;
Promise.reject():返回一个失败的promise实例对象
Promise.all():参数为数组,数组中存promise实例对象,当数组中实例对象状态全是成功时,返回一个成功的promise实例对象,结果值为数组中promise实例对象的结果值组成的数组;当数组中有失败状态的promise实例对象时,返回那个失败的promise实例对象。
Promise.race():参数为数组,数组中存promise实例对象,返回值为最先发生状态改变的promise实例对象。
改变promise实例对象的状态方式:
1.resolve() 2.reject() 3.抛出错误:throw “出错了”
问题:
1.改变promise对象的状态和执行回调谁先谁后?(resolve先执行还是then被指定)
都有可能,当promise对象中封装的是同步的,如resolve(“ok”),按顺序执行,当是异步的,会先按顺序执行完初始化代码,在执行异步队列中的任务
,then 的回调执行总是在改变状态之后才能拿到数据,注意是回调执行,不是then方法指定
2.异常穿透:
在promise调用链中,只需在最后指定一个catch,或then,写出错误的回调,这个错误的回调会穿透调用链,找到第一个出现状态为错误的地方并将此promise实例对象返回。
3.如何中断promise链:返回一个pading状态的promise对象,因为所有的then回调都是在promise对象状态改变后才执行的,中间返回pading状态的promise对象,后面的状态也都是peding,then的回调不会执行
Class
let methodName = "getArea";
class Point {
//定义构造器
constructor(x,y){
this.x = x;
this.y = y
}
//定义方法,在Point.prototype上
toString(){
return "(" + this.x + "+" this.y + ")";
}
[methodName](){}
}
- 类的内部所有方法都是不可枚举的,es5的写法可以
- class不存在变量提升,要先声明类在new,先有父类再有子类,否则会报错
- 由于本质上,ES6的类只是ES5的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。
类的继承
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)且super必须写第一行
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
-
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
-
在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。
-
Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。B extend A 时,①B的隐式原型属性,表示构造函数的继承,总是指向父类(函数对象,因为es6是先构造父类在对父类进行拓展),②B的prototype属性的–proto–属性总是指向父类的prototype,表示方法的继承(因为父类的方法在父类的原型上(实例对象))。
-
这两条继承链,可以这样理解:作为一个对象,子类(B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型(prototype属性)是父类的实例。
super
- 做函数时,用于构造方法第一行super(参数.)
- 做对象时,在普通方法中,指向父类的原型对象(注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。) ;在静态方法中,指向父类
Class的取值函数(getter)和存值函数(setter)
Class的静态属性和实例属性
静态属性指的是Class本身的属性,不是其实例对象上的属性,
class Foo {
}
Foo.prop = 1;
Foo.prop // 1
目前,只有这种写法可行,因为ES6明确规定,Class内部只有静态方法,没有静态属性。
1).类的实例属性
class MyClass {
myProp = 42;
constructor() {
console.log(this.myProp); // 42
}
}
2)类的静态属性
类的静态属性只要在上面的实例属性写法前面,加上static关键字就可以了。
// 老写法
class Foo {
}
Foo.prop = 1;
// 新写法
class Foo {
static prop = 1;
}
new.target属性:返回new命令作用于的那个构造函数。否则返回undefined,可以确保构造函数只能用于new
function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error('必须使用new生成实例');
}
}
Mixin模式指的是,将多个类的接口“混入”(mix in)另一个类。
function mix(...mixins) {
class Mix {}
for (let mixin of mixins) {
copyProperties(Mix, mixin);
copyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if ( key !== "constructor"
&& key !== "prototype"
&& key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
使用:上面代码的mix函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。
class DistributedEdit extends mix(Loggable, Serializable) {
// ...
}
ES6模块化
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict"。
ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。