前一篇介紹了constructor,現在來介紹ES6開始有的class。
學過原型鏈之後再來看class,應該可以理解class做了什麼事情,現在來建一個class:
class Dog {
sayHello() {
console.log(`hello`);
}
}
const d = new Dog();
d.sayHello();
// hello
console.log(d);
// Dog {}
當d被變成這個class Dog的instance的時候,他就可以使用sayHello()
。
這個sayHello()
是不是就很像 constructor的prototype?
現在d物件雖然可以用function,但他還是個空物件,該怎麼放東西進去?
那就直接放constructor,然後放參數進去就會變成他的property了。
class Dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
setName(name) {
this.name = name;
}
getName() {
console.log(`${this.name} say hello to you`);
}
}
const d = new Dog("dd", 22);
console.log(d);
// Dog { name: 'dd', age: 22 }
d.setName("cc");
d.getName();
// cc say hello to you
function Dog(name, age) {
this.name = name;
this.age = age;
}
Dog.prototype.getName = function () {
console.log(`${this.name} say hello to you`);
};
Dog.prototype.setName = function (value) {
this.name = value;
};
const c = new Dog("cc", 23);
console.log(c);
// Dog { name: 'cc', age: 23 }
c.setName("ee");
c.getName();
// ee say hello to you
所以class跟constructor的差異看起來像是prototype、property都可以直接設定在class裡面,不用像constructor一樣prototype要另外設。
class的方式比較好閱讀,但底層還是用constructor的方式在實作的。
Object.keys()
,如果使用Object.getOwnPropertyNames()
就可以。console.log(Object.getOwnPropertyNames(Dog.prototype));
// [ 'constructor', 'setName', 'getName' ]
console.log(Object.keys(c.__proto__));
//也可以這樣寫
console.log(Object.keys(dog.prototype));
// [ 'getName', 'setName' ]
class Dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
setName(name) {
this.name = name;
}
getName() {
console.log(`${this.name} say hello to you`);
}
static sayHello() {
console.log(`${this.name} Hello`);
}
}
const d = new Dog("dd", 22);
d.sayHello();
// d.sayHello is not a function
Dog.sayHello();
// Dog Hello
要加s
class Cow extends Dog {
// 裡面沒有東西
}
Cow.sayHello();
// Cow Hello
// 可以使用class Dog的靜態方法
[[prototype]]
是class DogsayHello()
不屬於Dog的prototype,而是他的方法。sayHello()
,但不代表子class的instance可以用喔!MDN上的解釋:
The super keyword is used to access properties on an object literal or class's [[Prototype]], or invoke a superclass's constructor.
翻譯:super 關鍵字被使用於通過函式存取父層constructor。
super()
會自動參考父constructor(以子class this的情境呼叫一個父constructor)super()
被呼叫之前都不能取用沒有定義的this
現在有一個class Dog
class Dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
setName(name) {
this.name = name;
}
getName() {
console.log(`${this.name} say hello to you`);
}
static sayHello() {
console.log(`${this.name} Hello`);
}
}
再讓class Cow extends Dog,並新增一個屬性weight。其他兩個屬性想要用繼承class Dog的方式,就只有新增this.weight = weight
class Cow extends Dog {
constructor(name, age, weight) {
this.weight = weight;
}
}
const cat = new Cow("cat", 15, 10);
console.log(cat);
// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
結果卻報錯,是因為現在有多了一個新的屬性,所以需要再打constructor的this.weight = weight
,但是前面沒有用super()
去呼叫class Dog的constructor,就等於現在的this不知道要綁定class Dog的constructor還是class Cow的constructor。
MDN上的節錄:
...the super keyword may appear as a "function call" (super(...args)), which must be called before the this keyword is used, and before the constructor returns. It calls the parent class's constructor and binds the parent class's public fields, after which the derived class's constructor can further access and modify this.
翻譯:super在當成function使用的時候,一定要在this前或consturctor被返回前被呼叫。他呼叫父class的constructor綁定父class的公共區域,再這之後子class才能去存取、修改this
如果今天沒有新增新的屬性的話是可以直接用到class Dog的constructor。
class Cow extends Dog {
}
const cat = new Cow("cat", 15);
console.log(cat);
// Cow { name: 'cat', age: 15 }
正確方法:
super()
來呼叫父class的constructorclass Cow extends Dog {
constructor(name, age, weight) {
super(name, age); // => 要放在this前面
this.weight = weight;
}
}
const cat = new Cow("cat", 15, 10);
console.log(cat);
// Cow { name: 'cat', age: 15, weight: 10 }
class Cow裡面沒有去定義this.name = name
,但他卻可以透過super()
去找class Dog裡面定的this.name = name
,可以產生同樣的結果。
super()
當作物件使用的話,就會參考父物件,就能存取他的特性或方法。class Dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
setName(name) {
this.name = name;
}
getName() {
console.log(`${this.name} say hello to you`);
}
sayHello() {
console.log(`${this.name} Hello`);
}
}
class Cow extends Dog {
constructor(name, age, weight) {
super(name, age);
this.weight = weight;
}
sayGoodbye() {
super.sayHello(); // => 當成物件的方式
console.log(`${this.name} Goodbye`);
}
}
const cat = new Cow("cat", 15, 10);
cat.sayGoodbye();
// cat Hello
// cat Goodbye
在方法裡面使用super.sayHello()
就會去找到父class的方法並在這裡使用,所以可以印出父class sayHello()
的cat Hello,也可以印出自己的方法 cat Goodbye。
終於學到class拉!寫法上比constructor簡單清楚,又多了extends, super可以使用,一邊看也一邊練習會更清楚喔!
明天見拉~
參考資料:008
[教學] 深入淺出 JavaScript ES6 Class (類別)
胡立 JS201