基础
class
是一个语法糖,只是在写法上更清晰简洁。可以看做是构造函数的另一种写法:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
alert(this.name);
}
}
var person1 = new Person("Angela", 18);
person1.sayName(); // Angela
以上构造了Person类,其中constructor函数作为构造函数,定义实例属性,sayName
是定义在原型的方法。创建新的实例需要使用new
命令。
类的内部定义的所有方法都是不可枚举的;类的属性名可以使用表达式;
类和模块的内部,都默认使用严格模式。类的声明不会提升;必须保证实例对象在声明类之后创建。
constructor方法
定义类的构造函数,使用new
创建实例时自动调用该方法,如果未定义则会自动创建一个空构造函数;
默认返回实例对象(this
),但如果在构造函数中指定返回另一个类的实例,则创建的实例对象属于指定的类;
class Fruit {
constructor() {
return new Person("Apple", 2);
}
}
var apple = new Fruit();
console.log(apple instanceof Fruit); // false
console.log(apple instanceof Person); // true
实例对象
类的实例对象共享同一个原型,除非显示定义在实例对象的属性,其他都是定义在原型上。
实例对象有一个__proto__
属性指向原型,但如果要获取原型建议使用Object.getPrototypeOf
;
Class表达式
用类似于函数表达式的另一种写法:
const Book = class {
constructor(name, price) {
this.name = name;
this.price = price;
}
sell() {
console.log(this.name+": $"+this.price);
}
}
var book1 = new Book("English", 10);
book1.sell(); // English: $10
类名为等号前的变量名,如果等号后的class
之后跟一个名称则只能在类内部使用,指代类本身。
私有方法和私有属性
把私有属性和方法名设置为Symbol值,可以达到私有化的效果;
目前有提案建议把属性和方法名前加“#”前缀使其私有化。
this的指向
在类中this
默认指向当前实例对象,但如果单独在类之外使用则会指向方法运行时的环境对象;
可以在构造函数中绑定this
,或使用箭头函数,或使用Proxy;
name属性
类的name
属性返回类名;
Class的静态方法
类相当于实例的原型,所有在类中定义的方法都会被实例继承,如果在一个方法前加上static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,称为“静态方法”。
静态方法中的this
指向类而不是实例对象。
Class的静态属性和实例属性
Class内部只有静态方法,没有静态属性,可以通过Class.propName
为Class指定静态属性。
新的提案中,类中实例属性可以直接在Class内部使用等号列出而不必一定要在构造函数中指定;类的静态属性可以在属性名前加static
。
new.target属性
new.target
一般用在构造函数中,如果使用new
命令调用构造函数,该属性为该构造函数的类名,否则为undefined
。
当子类继承父类时,这个属性返回子类;
类的继承
class Ebook extends Book {
}
var book2 = new Ebook("World", 5);
book2.sell(); // world, $5
console.log(book2 instanceof Book); // true
console.log(book2 instanceof Ebook); // true
console.log(book2); // Ebook {name:'world', price:5}
在子类的构造函数中需要使用super
获得父类,才能正确获得this
。
super
虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super
内部的this
指的是B,因此super()
在这里相当于A.prototype.constructor.call(this)
。
class Ebook1 extends Book {
constructor(name, price) {
super(name, price);
this.type = "e-book";
}
read() {
console.log("let\'s read this new "+this.type+"--"+this.name);
}
}
var book3 = new Ebook1("Campus Chinese", 15);
book3.read(); // let's read this new e-book--Campus Chinese
console.log(book3); // Ebook1 {name: "Campus Chinese", price: 15, type: "e-book"}
如果是以下写法会报错
class Ebook1 extends Book {
constructor() {
this.type = "e-book";
}
read() {
console.log("let\'s read this new "+this.type+"--"+this.name);
}
}
var book3 = new Ebook1("Campus Chinese", 15);
/*Uncaught ReferenceError: Must call super constructor in derived class
*before accessing 'this' or returning from derived constructor
*/
而如果没有指定constructor
方法则会默认添加:
class Ebook1 extends Book {
// 未显式指定则默认添加
constructor(...args) {
super(...args);
}
}
父类的静态方法也会被子类继承;
super
的应用除了作为函数,在子类构造函数中指代父类的构造方法并有指向子类的this
,还可以作为对象应用于普通方法中,指向该函数的父类的原型,而用在静态方法中,则指向方法的父类;
使用super
时必须显示指定是作为对象还是作为函数使用;
extends 也可以用来继承原生构造函数。
class myArray extends Array {
constructor() {
super();
}
}
var arr1 = new myArray();
arr1.push("4");
arr1.push("8");
var arr2 = arr1.map((x) => x / 2);
console.log(arr1, arr2); // myArray(2) ["4", "8"] myArray(2) [2, 4]