1. class简单使用
class是一个语法糖,其底层还是通过 构造函数 去创建的。所以它的绝大部分功能,ES5 都可以做到。新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function() {
return this.name;
}
const xiaoming = new Person('小明', 18);
console.log(xiaoming);
上面代码用ES6的class实现,就是下面这样
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
return this.name;
}
}
const xiaoming = new Person('小明', 18)
console.log(xiaoming);
// { name: '小明', age: 18 }
console.log((typeof Person));
// function
console.log(Person === Person.prototype.constructor);
// true
2. constructor
每个类都必须要有一个 constructor,如果没有显示声明,js 引擎会自动给它添加一个空的构造函数:
class Person {
}
// 等同于
class Person {
constructor () {
}
}
3. 属性和方法
定义于 constructor 内的属性和方法,即定义在 this 上,属于实例属性和方法,否则属于原型属性和方法。
class Person {
constructor (name) {
this.name = name
}
say () {
console.log('hello')
}
}
let jon = new Person()
jon.hasOwnProperty('name') // true
jon.hasOwnProperty('say') // false
4. 属性表达式
let methodName = 'say'
class Person {
constructor (name) {
this.name = name
}
[methodName] () {
console.log('hello')
}
}
5. 静态方法
不需要通过实例对象,可以直接通过类来调用的方法,其中的 this 指向类本身
class Person {
static doSay () {
this.say()
}
static say () {
console.log('hello')
}
}
Person.doSay() // hello
静态方法可以被子类继承
// ...
class Sub extends Person {
}
Sub.doSay() // hello
可以通过 super 对象访问
// ...
class Sub extends Person {
static nice () {
return super.doSay()
}
}
Sub.nice() // hello
6. 严格模式
不需要使用 use strict,因为只要代码写在类和模块内,就只能使用严格模式。
7. 提升
class 不存在变量提升。
new Person() // Uncaught ReferenceError: Person is not defined
class Person {
}
8. name 属性
name 属性返回了类的名字,即紧跟在 class 后面的名字。
class Person {
}
Person.name // Person
9. this
默认指向类的实例。
10. 取值函数(getter)和存值函数(setter)
class Person {
get name () {
return 'getter'
}
set name(val) {
console.log('setter' + val)
}
}
let jon = new Person()
jon.name = 'jon' // setter jon
jon.name // getter
11. class 表达式
如果需要,可为类定义一个类内部名字,如果不需要,可以省略:
// 需要在类内部使用类名
const Person = class Obj {
getClassName () {
return Obj.name //Obj
}
}
// 不需要
const Person = class {}
立即执行的 Class:
let jon = new class {
constructor(name) {
this.name = name
}
sayName() {
console.log(this.name)
}
}('jon')
jon.sayName() //jon
12. 继承
12.1 简介
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
继承的基本使用
class Point {
static hello() {
console.log('hello world');
}
constructor(x,y){
this.x = x;
this.y = y;
};
toString(){
return this.x +" "+this.y;
};
};
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
};
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
};
};
super关键字可以调用父类。constructor方法和toString方法之中,都出现了。
12.2 注意
(1) 一般情况写了constructor就要写super,否则new实例的时候会报错。
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。这与ES6 class的继承机制完全不同;
class的继承,实质是先将父类实例对象的属性和方法加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
class Point {
constructor(x,y){
this.x = x;
this.y = y;
};
};
class ColorPoint extends Point {
constructor(x, y, color) {
//未使用super
this.color = color;
};
};
new ColorPoint(10,10,"dog")//报错
(2) 另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。理由同上
class Point {
constructor(x,y){
this.x = x;
this.y = y;
};
};
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color;
//调用不对
super(x,y)
};
};
new ColorPoint(10,10,"dog")//报错
(3) 子类可以调用父类静态方法
class Point {
static hello() {
console.log('hello world');
}
constructor(x,y){
this.x = x;
this.y = y;
};
};
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
};
};
这是因为 extends 会导致子类ColorPoint构造函数与父类Point构造函数在一条原型链上。
12.3 与ES5继承的共同点
实例判断一致
dog instanceof ColorPoint // true
dog instanceof Point // true
12.4 super 关键字
super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.call(this)。
class A {}
class B extends A {
constructor() {
super();//A.call(this)。
}
}
(1)作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
class A {}
class B extends A {
m() {
super(); // 报错
}
}
(2)作为对象时,super 在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
上面有提到static会使方法存在于构造函数,也就是说可以把 super 看作 隐式原型。
但是,通过super来给属性赋值是不被允许的,效果等于super.x === this.x
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
(3)super作为对象,用在 static 静态方法之中,这时super将指向父类,而不是父类的原型对象。
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
(4)另外,在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。
class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m() // 3
(5)注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
class A {}
class B extends A {
constructor() {
super();
//console.log(super); // 报错
console.log(super.valueOf() instanceof B); // true
}
}
let b = new B();
上面代码中,super.valueOf()表明super是一个对象,因此就不会报错。同时,由于super使得this指向B的实例,所以super.valueOf()返回的是一个B的实例。
(6)最后,由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字。
var obj = {
toString() {
return "MyObject: " + super.toString();
}
};
obj.toString(); // MyObject: [object Object]