這是ES6 In Depth的閱讀筆記,只記錄程式的範例方便語法的查詢,但我強列推薦去讀讀這系列的原始文章,它對於ES6的語法有很深入的介紹,非常值得一讀。
傳統使用 prototype 建立物件的方式:
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}
Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }
Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},
set: function(val) {
this._count = val;
}
});
Circle.prototype = {
area: function area() {
return Math.pow(this.radius, 2) * Math.PI;
}
};
Object.defineProperty(Circle.prototype, "radius", {
get: function() {
return this._radius;
},
set: function(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
});
在ES6中提供了新的語法來定義物件的屬性,所以上面的例子可以簡化成:
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}
Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }
Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},
set: function(val) {
this._count = val;
}
});
Circle.prototype = {
area() {
return Math.pow(this.radius, 2) * Math.PI;
},
get radius() {
return this._radius;
},
set radius(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
};
但在ES6有提供了一個更直覺定義物件的方式,就是所謂的 class ,範例如下:
class Circle {
constructor(radius) {
this.radius = radius;
Circle.circlesMade++;
};
static draw(circle, canvas) {
// Canvas drawing code
};
static get circlesMade() {
return !this._count ? 0 : this._count;
};
static set circlesMade(val) {
this._count = val;
};
area() {
return Math.pow(this.radius, 2) * Math.PI;
};
get radius() {
return this._radius;
};
set radius(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
};
}
使用 class 有幾點要注意:
constructor() {}
) 會自動被建立。let 是ES6宣告變數的另一種方式,與原本的 var 有一些行為上的不同:
function varTest() {
var x = 31;
if (true) {
var x = 71; // same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // different variable
console.log(x); // 71
}
console.log(x); // 31
}
var x = 'global';
let y = 'global';
console.log(this.x); // global
console.log(this.y); // undefined
if (x) {
let foo;
let foo; // SyntaxError
}
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // SyntaxError
break;
}
function do_something() {
console.log(foo); // ReferenceError
let foo = 2;
}
for (let i = 0; i<10; i++) {
console.log(i); // 0, 1, 2, 3, 4 ... 9
}
console.log(i); // i is not defined
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i); // 5, 5, 5, 5, 5
}, 1000);
}
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i); // 1, 2, 3, 4, 5
}, 1000);
}
const 即是宣告一個常數,宣告的時候一定要給初始值,而且之後如果出現想更改常數值的程式就會出 SyntaxError 。
const MAX_CAT_SIZE_KG = 3000;
MAX_CAT_SIZE_KG = 5000; // SyntaxError
MAX_CAT_SIZE_KG++; // SyntaxError
const theFairest; // SyntaxError
在ES6還沒出來以前,可以使用 Object.create 來模擬繼承的行為:
var proto = {
value: 4,
method() { return 14; }
}
var obj = Object.create(proto);
obj.value; // 4
obj.method(); // 14
obj.value = 5;
obj.value; // 5
proto.value; // 4
而在ES6中,有新的 class 語法可以使用,舉個例子:
class Shape {
get color() {
return this._color;
}
set color(c) {
this._color = parseColorAsRGB(c);
this.markChanged(); // repaint the canvas later
}
}
如果要讓上面的 Circle class 繼承的 Shape class 的行為,有一種方式是使用 Object.setPrototypeOf :
// Hook up the instance properties
Object.setPrototypeOf(Circle.prototype, Shape.prototype);
// Hook up the static properties
Object.setPrototypeOf(Circle, Shape);
不過這種方式顯然有點醜,另一個方式就是使用 extends 的新語法:
class Circle extends Shape {
// As Circle class above
}
extends 後面不只可以接一個 class,只要是任何有 prototype 這個屬性的 contructor,都可以使用 extends 來繼承,例如:
我們可以使用 super 來取得來自 parent class 的值,例如:
class ScalableCircle extends Circle {
get radius() {
return this.scalingFactor * super.radius;
}
set radius() {
throw new Error("ScalableCircle radius is constant." +
"Set scaling factor instead.");
}
// Code to handle scalingFactor
}
super 也可以用在定義在 object 裡的 method:
var obj = {
toString() {
return "MyObject: " + super.toString();
}
}
obj.toString(); // MyObject: [object Object]
var a = obj.toString;
a(); // MyObject: [object Object]
class VersionedArray extends Array {
constructor() {
super();
this.history = [[]];
}
commit() {
// Save changes to history.
this.history.push(this.slice());
}
revert() {
this.splice(0, this.length, this.history[this.history.length - 1]);
}
}
特別注意的是,即使是來自原本 Array 的 method,例如:Array.prototype.slice(),回傳的也會是 VersionedArray 而非 Array 。