ES6 類別語法並沒有改變 JavaScript 基於 prototype-based 的本質,只是在此提供一種語法糖,主要的目的是可以更貼近傳統物件導向語言的寫法。
今天,我們同樣會分別使用 ES5、ES6 來讓各位看一下差異
// ES5
function Shape1(id, x, y) {
this.id = id;
this.move(x, y);
};
Shape1.prototype.move = function(x, y) {
this.x = x;
this.y = y;
};
var a = new Shape1("a", 0, 0);
console.log(a); // Shape1 {id: "a", x: 0, y: 0}
// ES6
class Shape2 {
constructor(id, x, y) {
this.id = id
this.move(x, y)
}
move(x, y) {
this.x = x
this.y = y
}
}
var a = new Shape2("a", 0, 0);
console.log(a); // Shape2 {id: "a", x: 0, y: 0}
ES6 中繼承更簡潔。
// ES5
Function.prototype.inherits = function(superCtor) {
Object.setPrototypeOf(this.prototype, superCtor.prototype);
this.super = superCtor.prototype;
};
function Shape1(id, x, y) {
this.id = id;
this.move(x, y);
};
Shape1.prototype.move = function(x, y) {
this.x = x;
this.y = y;
};
function Rectangle1(id, x, y, width, height) {
Shape1.call(this, id, x, y);
this.width = width;
this.height = height;
};
Rectangle1.inherits(Shape1);
function Circle1(id, x, y, radius) {
Shape1.call(this, id, x, y);
this.radius = radius;
};
Circle1.inherits(Shape1);
var a = new Rectangle1("a", 0, 0, 100, 100);
console.log(a); // Rectangle1 {id: "a", x: 0, y: 0, width: 100, height: 100}
var b = new Circle1("b", 0, 0, 100);
console.log(b); // Circle1 {id: "b", x: 0, y: 0, radius: 100}
// ES6
class Shape2 {
constructor(id, x, y) {
this.id = id
this.move(x, y)
}
move(x, y) {
this.x = x
this.y = y
}
}
class Rectangle2 extends Shape2 {
constructor(id, x, y, width, height) {
super(id, x, y)
this.width = width
this.height = height
}
}
class Circle2 extends Shape2 {
constructor(id, x, y, radius) {
super(id, x, y)
this.radius = radius
}
}
var a = new Rectangle2("a", 0, 0, 100, 100);
console.log(a); // Rectangle2 {id: "a", x: 0, y: 0, width: 100, height: 100}
var b = new Circle2("b", 0, 0, 100);
console.log(b); // Circle2 {id: "b", x: 0, y: 0, radius: 100}
原型方法則是透過 super.move(x,y) 就能呼叫父類別的原型方法。
ES6 是不是把繼承變得更簡潔了呢?
接下來,讓我們來看一下 Getter 與 Setter 寫法上有什麼差異吧!
// ES5
function Rectangle1(width) {
this._width = width;
};
Rectangle1.prototype = {
set width(width) {
this._width = width;
},
get width() {
return this._width;
}
};
var r = new Rectangle1(50);
r.width = 1000;
console.log(r.width); // 1000
// ES6
class Rectangle2 {
constructor(width) {
this._width = width
}
set width(width) {
this._width = width
}
get width() {
return this._width
}
}
var r = new Rectangle2(50)
r.width = 1000;
console.log(r.width); // 1000
JavaScript 會產生非同步的方法有 setTimeout、setInterval、XMLHttpRequest、fetch、Promise...等等。
在 ES5 我們處理非同步行為會使用 Callback ,但這會讓程式碼難以維護,相信大家對於波動拳應該不陌生 XDD, 而 ES6 提供了 Promise 是將非同步行為統一相同介面接口,透過 then 或 catch 去接收或捕捉非同步的結果,因此不會產生多層的 Callback 結構。
ES7 開始支援 Async...Await 語法讓程式碼閱讀起來都是同步的,但實際上他仍是用非同步處理。
說了這麼多,還是讓我們先看一段程式碼吧!
// ES5
function asyncTask1(success, time, callback) {
setTimeout(function() {
if (success) {
callback(true, time);
} else {
callback(false, time);
}
}, time);
}
// 非同步同時完成
function fetchAll(tasks, success, error) {
var results = Array(tasks.length);
var count = 0;
for (var i = 0; i < tasks.length; i++) {
(function(i) {
tasks[i](function(resolve, data) {
if (resolve) {
count++;
results[i] = data;
if (count === tasks.length) {
success(results);
}
} else {
error(data);
}
});
})(i);
}
}
fetchAll([
function(cb) { asyncTask1(true, 1000, cb) },
function(cb) { asyncTask1(true, 5000, cb) },
function(cb) { asyncTask1(true, 2500, cb) }
],function(data) {
console.log("ok:", data);
},function(err) {
console.log("err:", err);
});
// 非同步依序完成
asyncTask1(true, 1000, function(resolve, data) {
resolve && console.log("ok:", data);
resolve && asyncTask1(true, 5000, function(resolve, data) {
resolve && console.log("ok:", data);
resolve && asyncTask1(true, 2500, function(resolve, data) {
resolve && console.log("ok:", data);
});
});
});
// ES6
function asyncTask2(success, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {
resolve(time);
} else {
reject(time);
}
}, time);
});
}
// 非同步同時完成
Promise.all([
asyncTask2(true, 1000),
asyncTask2(true, 5000),
asyncTask2(true, 2500)
]).then(data => {
console.log("ok:", data);
}, err => {
console.log("err:", err);
});
// 非同步依序完成
asyncTask2(true, 1000).then(result => {
console.log(result);
return asyncTask2(true, 5000);
}).then(result => {
console.log(result);
return asyncTask2(true, 2500);
}).then(result => {
console.log(result);
console.log("依序完成");
}).catch(err => {
console.log("err:" + err);
});
// ES7 的 Async 和 Await 用法
(async () => {
console.log("begin");
var a = await asyncTask2(true, 1000);
console.log("ok:", a);
var b = await asyncTask2(true, 5000);
console.log("ok:", b);
var c = await asyncTask2(true, 2500);
console.log("ok:", c);
console.log("end");
})()
console.log("other code");
對於 Promise.all 覺得陌生的讀者別緊張,我會在下方提供相關參考的資料,相信看完以上範例的朋友,都會覺得越往下看越直覺越好懂,對吧?
鐵人賽在這邊也正式告一個段落了,很慶幸一個月前的自己衝動地做了這個決定,我覺得鐵人賽是一段自我驗證的旅程,在這段旅程中,逐漸增強自己的信心、改善了拖延的習慣,也藉由這次的系列文,我發現了自己的幾個缺點
希望明年的鐵人賽,我能針對這些缺點做改進,造就更強悍的自己,謝謝各位。( 下台一鞠躬
參考資料:
Tommy - 深入 JavaScript 核心課程
MDN - Promise
MDN - async function