JS語言的傳統方法是通過構造函數,定義並生成新對象,是一種基於原型的面向對象系統。在ES6中新增加了類的概念,可以使用 class 關鍵字聲明一個類,之后以這個類來實例化對象。
構造函數示例
const Demo = function (a, b) { this.a = a; this.b = b; return this; }; Demo.prototype = { constructor: Demo, print: function () { console.log(this.a + ' ' + this.b); } }; const demo = new Demo('hello', 'world').print();
class示例
class Demo { constructor(a, b) { this.a = a; this.b = b; return this; } print() { console.log(this.a + ' ' + this.b); } }; const demo = new Demo('hello', 'world').print(); console.log(typeof demo);
1 Demo中的constructor方法是構造方法,this關鍵字則代表實例對象。也就是說,ES5的構造函數Demo,對應ES6的Demo這個類的構造方法。
2 Demo這個類除了構造方法,還定義了一個print方法。注意,定義"類"的方法的時候,前面不需要加上function這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。
3 構造函數的prototype屬性,在ES6的“類”上面繼續存在。而且類的所有方法都定義在類的prototype屬性上面。
class Point { string(){ return 'aaa' } } class ColorPoint extends Point { constructor(color) { super(); // 調用父類的constructor() this.color = color; } toString() { return (this.string() ) } } console.log(new ColorPoint('#000')); //console.log(new ColorPoint(1,2,'#000')) console.log(new ColorPoint('#000').toString()); //aaa console.log(new ColorPoint('#000').__proto__); //Point {constructor: function, toString: function} console.log(ColorPoint.__proto__); // class Point{...} console.log(ColorPoint.prototype); // Point {constructor: function, toString: function} console.log(ColorPoint.__proto__.prototype); // Object {constructor: function, string: function}
作為一個對象,子類(B)的原型(__proto__屬性)是父類(A);作為一個構造函數,子類(B)的原型(prototype屬性)是父類的實例。
Object.setPrototypeOf(B.prototype, A.prototype);
等同於
B.prototype.__proto__ = A.prototype;
Object.setPrototypeOf(B, A);
等同於
B.__proto__ = A;
super
1、super作為函數調用時,代表父類的構造函數。ES6 要求,子類的構造函數必須執行一次super函數。
2、super作為對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類
super代表了父類A的構造函數,但是返回的是子類B的實例,即super內部的this指的是B,因此super()在這里相當於A.prototype.constructor.call(this)。
class A { constructor() { this.x = 1; } print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } m() { super.print(); } } let b = new B(); b.m() // 2
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();
類相當於實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱為“靜態方法”。
new.target
new是從構造函數生成實例的命令。ES6為new命令引入了一個new.target屬性,(在構造函數中)返回new命令作用於的那個構造函數。如果構造函數不是通過new命令調用的,new.target會返回undefined,因此這個屬性可以用來確定構造函數是怎么調用的。
Class內部調用new.target,返回當前Class。
class Rectangle { constructor(length, width) { console.log(new.target === Rectangle); this.length = length; this.width = width; } } var obj = new Rectangle(3, 4); // 輸出 true
ps:子類繼承父類時,new.target會返回子類。
class Shape { constructor() { if (new.target === Shape) { throw new Error('本類不能實例化'); } } } class Rectangle extends Shape { constructor(length, width) { super(); console.log(length+' '+width) // ... } } var y = new Rectangle(3, 4); // 正確 var x = new Shape(); // 報錯
靜態方法
函數命前聲明static,就為靜態方法。該方法不會被實例繼承,而是直接通過類來調用。
class Person { constructor(name) { this.name = name; } static say () { console.log("say hi"); } }; Person.say();
靜態屬性
靜態屬性指的是Class本身的屬性,即Class.propname
,而不是定義在實例對象(this
)上的屬性。
class Foo { } Foo.prop = 1; Foo.prop // 1
新寫法
class MyClass { static myStaticProp = 42; constructor() { console.log(MyClass.myStaticProp); // 42 } }
實例屬性
class ReactCounter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } }
新寫法
class ReactCounter extends React.Component { state = { count: 0 }; }