语法糖 Syntactic sugar,指电脑语言中添加的某种语法,这种语法对语言的功能没有影响,但是让程序更加简洁,有更高的可读性。
而 class 称为 ES6 新增的语法糖,也意味着没有新功能的变动,而是对於物件的继承与建造有更方便的写法,今天就来比较一下使用 constructor 与 ES6 增加的 class 有什麽写法上的差异吧!
图片来源 CLEANPNG
本文段落分为:
七大差异比较:
函式可以在定义呼叫使用,这称为 function declaration 有 hoisting 的缘故,但是在 class 使用 class declaration 并不会有 hoisting 效果,必须先定义 class 後才能呼叫!否则抛出 error 讯息 not defined
,这个错误讯息意味着不允许在宣告之前实体化 instantiate 一个类别。
// constructor
let cookie1 = new Cookie(); // chocolate
function Cookie(){
console.log('chocolate')
}
// class
let cake1 = new CakeClass(); // error; CakeClass is not defined
class Cake {
constructor(){
console.log('tiramisu')
}
}
建立新物件使用 constructor 时,若忘记加上 new
关键字,虽然不会产生新的物件但也不会报错,而是变成的 function 赋值,若没有 return 印出会是 undefined;
但在 class 使用上规定要加上new
,否则抛出错误讯息
Uncaught TypeError: Class constructor Cake cannot be invoked without 'new'
// constructor
function Cookie(flavor) {
this.flavor = flavor;
}
let cookie1 = Cookie('mocha'); // 没有加上 new,cookie1 印出 undefined
let cookie2 = new Cookie('chocolate'); // cooke2 -> Cookie {flavor: 'chocolate'}加上 new 後,Cookie 为 constructor 建立出实例 cookie2
// class
class Cake {
constructor(flavor) {
this.flavor = flavor;
}
}
let cake1 = Cake('cheezeCake'); // error; 必须加上 new
let cake2 = new Cake('tiramisu'); // cake2 -> Cake {flavor: 'tiramisu'}
constructor 的共享属性需要透过 constructor.prototype
的方式写入原型物件中,class 可以直接写在 class body 内 {}
,会自动写入该类别的 prototype 中。
// constructor
function Cookie(flavor){
this.flavor = flavor;
}
Cookie.prototype.price = (cost) => `NTD ${cost * 1.5}`
let cookie1 = new Cookie('chocolate')
console.log( cookie1 ) // {flavor: 'chocolate'}
console.log( cookie1.price(50)) // NTD 75
// class
class Cake {
constructor(flavor){
this.flavor = flavor;
}
price(cost) {
return `NTD ${cost * 1.5}`
}
}
let cake1 = new Cake('tiramisu')
console.log( cake1.price(50) ) // NTD 75
static
静态方法静态方法只能透过 constructor 或是 class 呼叫,并不会继承,因此由 constructor、class 建立的实例 instance 无法取得,constructor 可以使用 .
方法写入; 而 class 只要加上 static
关键字就可直接在 class body 内定义。
// constructor
function Cookie(flavor) {
this.flavor = flavor;
}
Cookie.secret = ()=> {console.log('老板娘很票酿')}
let cookie1 = new Cookie('chocolate')
cookie1.secret() // error
Cookie.secret() // 老板娘很票酿
// class
class Cake {
constructor(flavor) {
this.flavor = flavor;
}
price(cost) {
return `NTD${cost* 1.5 }`
}
static secret(){
console.log('老板娘很票酿')
}
}
let cake1 = new Cake('tiramisu')
cake1.secret() // error
Cake.secret() // 老板娘很票酿
extends
扩增原型链在 ES6 以前,若想扩增原型链,必须在 constructor 的原型中创立一个新物件,再指定继承母阶的 prototype,并且须重新设定 constructor 属性指回自己; 而 class 大大简化了这个步骤,透过 extend
及 super
语法绑定 parentClass 。
// constructor
function Pastry(category){
this.category = category;
}
Pastry.prototype.owner = ()=> {console.log('Hoo')}
function Cookie(flavor){
this.flavor = flavor;
}
// 新增 Pastry 到原型链上
Cookie.prototype = Object.create(Pastry.prototype)
Cookie.prototype.constructor = Cookie
// 之後
let cookie1 = new Cookie('chocolate')
cookie1.owner() // Hoo,成功继承到 Pastry prototype
cookie1.__proto__.__proto__ === Pastry.prototype // true
// class 使用 extend 和 super 简化写法
class Cake extends Pastry{
constructor(flavor){
super('cake');
this.flavor = flavor;
}
}
let cake1 = new Cake('tiramisu')
cake1.owner() // Hoo
cake1.__proto__.__proto__ === Pastry.prototype // true
使用 class 不管是 declaration 或 expression 都会自动开启严格模式,就算没有加上 use strict
字样,所有严格模式禁止行为在 class 区块内都会抛出错误!
严格模式禁止行为请看-> D4 - 加盐不加价 严格模式开启
以严格模式下禁止使用的保留字 package
测试看看
// 一般 function declaration
function func(){
let package = '一般模式 package 不是保留字';
console.log(package)
}
func() // 一般模式 package 不是保留字
// class declaration
class ClassFunc {
constructor(){
let package = '印不出来';
console.log(package)
}
}
// Uncaught SyntaxError: Unexpected strict mode reserved word
在 Chrome DevTools 印出可以很明确的知道这个实例是由 constructor 还是 class 建立。
由 constructor 建立的实例,[[Prototype]] 属性下的 constructor 显示为 function
由 class 建立的属性,[[Prototype]] 属性下的 constructor 显示为 class
difference | constructor | class |
---|---|---|
code | function 开头 | class 开头,实例内容写在 constructor 下 |
hoisting | function declaration 有 hoisting | 没有 hoisting |
new | 没有加上 new 不会生成新物件,但也不会报错 |
必须加上 new 才能呼叫 class,否则抛出错误讯息 |
严格模式 | 加上 use strict 才是严格模式 |
class 函式自动成为严格模式 |
原型方法 | 原型方法写在 prototype 物件内 | 原型方法写在 class body 内 |
静态方法 | 静态方法须另外写在 constructor 属性内 | 在 class body 中加上 static 就可增加静态方法 |
继承 | 另外指派 prototype 来增加继承对象 | 使用 extends 增加 parentClass |
Devtools | 显示 function | 显示 class |
class
比较纯粹的 class-based 物件导向语言,物件是由 class 创造,因此所有方法及成员须一开始写在 class 内,无法在 class 外的区域定义,因此当实例已经建立完成,就无法再额外新增属性。
而 JavaScript 就不限定啦!基於 prototype 设定,物件被创立後可以自己再新增内部属性,或是跟着继承来的 prototype 内容变动。
举例:建立一个 Employee class 并 new 出实例 john,分别以 Java 及 JavaScript 写法做比较:
// 建立 Employee class
public class Employee {
public String employeeName = "Default name";
public int employeeId = 0;
public Employee(String name, String id) {
System.out.println("Employee class instantiated");
this.employeeName = name;
this.employeeId = id ;
}
public void printEmployee() {
System.out.println("Name: " + employeeName + " Id: " + employeeId);
}
setEmployeName(String name, String id){}
getEmployeeName(String name, String id){}
}
// new + 建构子建立实例
Employee john = new Employee("John”, 123); // output: "Employee class instantiated"
john.printEmployee(); // output: "Name: John Id: 123"
class Employee {
constructor (name, id) {
this.name = name;
this.id = id;
}
getDetails() {
return `${this.name} , ${this.id}`;
}
}
// new + class 建立物件
let john = new Employee("John", 123);
// john 建立後可以继续增加属性
john.saysHello = function() {
console.log(this.name + " says: Hello World!");
}
john.getDetails() // "John , 123"
john.saysHello() // John says: Hello World!
忍者JavaScript 开发技巧探秘 2
MDN - class
As a JS Developer, This Is What Keeps Me Up at Night
The Difference Between Java And JavaScript
从物件导向开始到原型链、 class 与 constructor 比较,这系列终於结束!
终於又把一个大坑填平,填坑挖坑的过程真是又累又满足啊~
<<: Re: 新手让网页 act 起来: Day18 - React Hooks 之 useRef
学习一门全新的东西,势必要先打好根基,为了求快而省略了一些基础,那麽在之後的学习上,可能会碰上一些需...
七年前,我在跟主管讨论我手头目前使用开源函式库来进行程序编辑时,老板问了一个问题:『为何他们愿意提供...
原文在这: Title: How WhatsApp enables multi-device cap...
30 天的铁人赛终於来到尾声,回想自己能够连续 30 天都上传文章实在太不可思议! 以学习 Git ...
待完成... ...