一、class 的定义与基本用法
1.class 的定义
ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为 JavaScript 引入新的面向对象的继承模型。
2. 定义类
用 class 定义类有两种方法:类表达式和类声明
1. 类声明
class Person{constructor(x,y){ this.x=x; this.y=y; } tostring(){return this.x+this.y} }
2. 类表达式
// 匿名类 var Person=class {constructor(x,y){ this.x=x; this.y=y; } tostring(){return this.x+this.y} } // 命名类 var Person = class Person {constructor(x, y) { this.x = x; this.y = y; } tostring() {return this.x + this.y} }
以上需要说明的点:
1. 类声明 / 类表达式不会向函数声明 / 函数表达式一样提升。你要访问一个类,就必须先声明。
2.ES6 类的 constructor 函数相当于 ES5 的构造函数。
3. 类中定义方法时,前面不用加 function,后面不得加 ,。
二、继承
extends 关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类。
// 父类 class Animal{constructor(name,age){ this.name=name; this.age=age; } tostring() {return (this.name + "的年龄是:" + this.age) } } // 子类 class Dogs extends Animal {constructor(name,age,code) {super(name, age); // 调用父类的 constructor(name,age) this.code = code; } toString() {return this.color + '的' + super.toString(); // 调用父类的 toString()} }
也可以扩展传统的基于函数的“类”(构造函数)
function Animal (name) {this.name = name;} Animal.prototype.speak = function () {console.log(this.name + 'makes a noise.'); } class Dog extends Animal {speak() {super.speak(); console.log(this.name + 'barks.'); } } var d = new Dog('Mitzie'); d.speak();//Mitzie makes a noise. Mitzie barks.
请注意,类不能继承常规(非可构造)对象。如果要继承常规对象,可以改用
var Animal = {speak() {console.log(this.name + 'makes a noise.'); } }; class Dog {constructor(name) {this.name = name;} } Object.setPrototypeOf(Dog.prototype, Animal);// If you do not do this you will get a TypeError when you invoke speak var d = new Dog('Mitzie'); d.speak(); // Mitzie makes a noise.
以上需要说明的点:
1. 类必须使用 new 调用,否则会报错。
2. 子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。
三、constructor 方法
constructor 是一种用于创建和初始化 class 创建的对象的特殊方法。如果不指定一个构造函数 (constructor) 方法, 则使用一个默认的构造函数(constructor)。
class Animal { } // 等同于 class Animal {constructor() {}}
以上需要说明的点:
1.constructor 方法默认返回实例对象(即 this)。
2. 在一个类中只能有一个名为“constructor”的特殊方法。一个类中出现多次构造函数 (constructor)方法将会抛出一个 SyntaxError 错误。
四、super 关键字
1.super 作为函数时,指向父类的构造函数。super()只能用在子类的构造函数之中,用在其他地方就会报错。
class Animal{constructor(){console.log(new.target.name); } } class Dogs extends Animal {constructor() {super(); } } new Animal() // Animal new Dogs() // Dogs
- super 作为对象时,指向父类的原型对象。
class A {p() {return 2;} } class B extends A {constructor() {super(); console.log(super.p()); // 2 } }
super 指向父类的原型对象,此时 super.p()就相当于 A.prototype.p()。
以上需要说明的点:
1. 由于 super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过 super 调用的。
五. 类的 prototype 属性和__proto__属性
Class 作为构造函数的语法糖,同时有 prototype 属性和__proto__属性,因此同时存在两条继承链。
1. 子类与父类
1. 子类的__proto__属性,表示构造函数的继承,总是指向父类。
2. 子类 prototype 属性的__proto__属性,表示方法的继承,总是指向父类的 prototype 属性。
class A {} class B extends A {} B.__proto__ === A // true B.prototype.__proto__ === A.prototype // true
2. 实例的 proto 属性
子类实例的 proto 属性的 proto 属性,指向父类实例的 proto 属性。也就是说,子类的原型的原型,是父类的原型。
class A{} class B extends A{} let a = new A(); let b = new B(); console.log(b.__proto__ === a.__proto__);//false console.log(b.__proto__.__proto__ === a.__proto__);//true