在 ES6 後,新增了 class 类别,一个更简洁的语法来建立物件,也是建立继承的语法糖。
必须再次强调,JavaScript 的 class 用法与其他 class-based OOP 语言中的 class 并不相同!虽然乍看写法类似,但在 JS 中的 class 还是以原型 prototype 为基础打造出来的,与 Java 等使用的 class 可不相同,可说是远看像匹狼近看是只羊,可别傻呼呼的冲进狼群内(如:Java) 咩咩咩的喊着我们都一样唷~
(忘记 OOP 的请前往 D15 - 那个圆圆的东西 - OOP 物件导向程序设计 结束再回来)
Class 语法预计分为上下两篇,此篇主要为基础语法介绍
段落分为:
class 创建物件的方法与 constructor 相同,都是 new
+ class 名称
建立实例,主要差异在定义的写法上。
在 class 定义中也分为 class declaraion 与 class expression 两种不同写法:
class 定义时会将预计加入实例中的属性写入关键字 constructor
内,当使用 new
关键字时,便会走访 constructor 内的程序码并回传入新物件,constructor 内怎麽写实例内的 body code 就长怎样。
一个 class 内只会有一个 constructor
// constructor 写法
function Cake(size, flavor) {
this.size = size;
this.flavor = flavor;
}
let cake1 = new Cake('M','cream')
console.log( cake1 ) // Cake {size: "M", flavor: "cream"}
// class declaration 写法
class Cake {
constructor(size, flavor) {
this.size = size;
this.flavor = flavor;
}
}
// class expression 写法
let Cake = class {
constructor(size, flavor) {
this.size = size;
this.flavor = flavor;
}
}
let cake2 = new Cake('M','choco');
console.log(cake2) // Cake {size: "M", flavor: "choco"}
使用 constructor 时,共享的 method 会另外放在 prototype 物件内,因此建立的实例 instance 都可以透过 __proto__
在原型链连结到此物件; 而 class 语法将这做法简化,shared method 不需另外写在 prototype 物件上,可以直接写入 class body 内。
// construcotr 的共享属性
function Cake(flavor) {
this.flavor = flavor;
}
Cake.prototype = {
price: (cost) => `NTD ${cost* 1.5 }`
}
let cake1 = new Cake('cream')
cake1.price(50) // NTD 75
// class 将共享的 method 写在 class 内
class Cake {
constructor(flavor) {
this.flavor = flavor;
}
price(cost) {
return `NTD$ {cost* 1.5 }`
}
}
let cake1 = new Cake('choco')
cake1.price(50) // NTD 75
透过 devTool 查看,果然 price method 是放在 Cake 的 prototype 物件内
extends
, super
扩充类别用 extends
关键字可以扩充继承的类别,将 extends
後的类别加入原型链上!
先写个简单版看看怎麽使用 extends
增加类别
class Pastry {}
class Cake extends Pastry {} // 增加一个类别 Pastry
let cake1 = new Cake()
透过 extends
可以很方便地将任何的 constructor 或 class 加入原型链成为 parentClass,使用 proto 查访新加入的原型链成员。
Cake.prototype.__proto__ === Pastry.prototype
cake1.__proto__.__proto__ === Pastry.prototype // true
cake1 instanceof Pastry // true
接下来,试试除了扩增类别外,也在本身 class 内定义属性,刚刚学过的,写在 constructor 内,像这样吗?
class Cake extends Pastry {
constructor(flavor) {
this.flavor = flavor;
}
price(cost) {
return `NTD${cost* 1.5 }`
}
}
噢,不!
这可是会出现错误讯息哦
Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
错误讯息显示,在使用子类别的 this 前,必须先调用母阶建构函式。
先说解法就是:在子类别的 constructor 中先使用 super
呼叫母类别。
那,这是为什麽呢?
我自己的记法是 extends
指定了类别的阶层,而 super
这个动作才是真正在物件上依照原型链顺序建立属性。
当要在子类别中使用 this 时,须先使用 super
呼叫并执行母类别的物件,先建立好母类别指定的属性後,再接续建立子类别中的属性,这样也才符合为什麽 childClass 可以覆盖 parentClass 的特性呀。
正确的程序码应该像这样:
class Pastry {
constructor(name){
this.slogan = `${name}爱吃不怕胖`
}
calories(piece){
return `热量${piece*480}k`
}
}
class Cake extends Pastry {
constructor(flavor, name) {
super(name) // 先使用 super 呼叫并执行 Pastry
this.flavor = flavor;
}
price(cost) {
return `NTD${cost* 1.5 }`
}
}
let cake1 = new Cake('choco', 'Hoo')
console.log( cake1 ) // Cake {slogan: "Hoo爱吃不怕胖", flavor: "choco"}
console.log( cake1.calories(3)) // 热量1440k
static
定义静态方法 Static Method静态方法 static method 存在定义的 class 中,只能由 class 存取,建立的 实例 instance 不能取到,静态方法的语法是在前面加上 static
。
class Cake {
constructor(flavor) {
this.flavor = flavor;
}
price(cost) {
return `NTD${cost* 1.5 }`
}
static changeFlavor(flavor){
this.flavor = flavor;
console.log(`flavor has changed to ${flavor}`)
}
}
let cake1 = new Cake('choco')
cake1.changeFlavor('mocha') // error; cake1.private() is not a function
Cake.changeFlavor('mocha') // flavor has changed to mocha
// 可以透过 call 的方式绑定 this 为实例 cake1 进而修改
Cake.changeFlavor.call(cake1, 'mocha') // flavor has changed to mocha
console.log(cake1.flavor) // mocha
constructor
内,class 大括号内只有一个 construtor。extends
扩充类别,写在 extends
前的为 childClass,extends
後的为 parentClasssuper
用来呼叫 parentClass,加在 constructor 内的 this 程序码之前static
开头後的 method 只有 class 可以呼叫,称为静态方法JavaScript | ES6 中最容易误会的语法糖 Class - 基本用法
MDN - class
Class basic syntax
[JS] JavaScript 类别(Class)
JavaScript Class
基本的 Class 语法介绍完毕!
明天内容为 constructor 与 class 的差异比较、真正的 class 与 prototyped-based 的 class 差异。
>>: 第17车厢-超实用!tab页签切换:data-*应用篇
前言 Rust 是一个现代版的 C/C++ 程序语言,它加入物件导向、套件安装(cargo)、函数式...
一样要说明这是由彭彭影片撷取出来的例题 <!DOCTYPE html> <html...
魔鬼藏在细节 在上一篇中的最後我问了一个问题:“为什麽没有使用 Flowable 而是继续用 Obs...
Random随机变数 Java里面本身有个语法就是可以帮助我们产生随机变数,这个语法是Math.ra...
这一两年很常在网页设计中看到这种不规则的小东西出现,甚至还会像波浪一样动。 之前傻傻地用Svg做,然...