class 相對 function 是后出來的,既然 class 出來了,顯然是為了解決 function 在處理面向對象設計中的缺陷而來。
下面通過對比,來看看 class 作為 ES6 中的重大升級之一的優勢在哪里:
為了更好的對比,請參見我的另外一篇博文: js面向對象設計之 function 類。
1、class 寫法更加簡潔、含義更加明確、代碼結構更加清晰。
2、class 盡管也是函數,卻無法直接調用(不存在防御性代碼了)。
3、class 不存在變量提升。
4、class 為污染 window 等全局變量(這點很贊啊)。
5、class 函數體中的代碼始終以嚴格模式執行(新手寫的代碼可靠性也必須上來了)
/* ReferenceError: Class01 is not defined */
try { var ins01 = new Class01(); } catch ( e ) { console.error( e ); }
class Class01 { }
console.log( typeof Class01 ); /* function */
/* Class constructor Class01 cannot be invoked without 'new' */
try { Class01() } catch ( e ) { console.error( e ); }
console.log( window.Class01 ); /* undefined */
6、可直接使用 set 和 get 函數。這比 function 要好用多了。
據我所知,vue 中的數據綁定是通過 set 和 get 來實現,而這里 class 可以使用便捷的如同普通的函數的寫法。
function 中則需要通過 Object.defineProperty 的方式來設置 set 和 get,繁瑣且代碼可讀性差。
class Class01{
constructor() { }
get name(){
console.log( 'getter' );
return this._name;
}
set name( v ){
this._name = v;
console.log( 'setter' );
return this;
}
}
var ins01 = new Class01();
ins01.name; /* getter */
ins01.name = 2; /* setter */
7、class 內部方法中若涉及到 this,則一定要注意。class 中的 this 永遠都不會指向 window。
熟悉 function 的應該知道,function 類的實例方法,可能指向 window(在某些情況下,具體讀者可自行百度,也可閱讀下述例子推敲)。
而 class 則在 this 可能指向 window 的情況下,將 this 指向 undefined。如下:
class Class01 {
constructor() {
this.a = 'a';
}
geta(){
return this.a;
}
}
let ins01 = new Class01();
console.log( ins01.geta() ); /* a */
let obj = {};
obj.a = 'objA';
obj.geta = ins01.geta;
console.log( obj.geta() ); /* 'objA' */
window.a = 'windowA';
window.geta = ins01.geta;
/* Cannot read property 'a' of undefined */
/* 若是 function 類此處會返回 'windowA' */
try { geta() } catch ( e ) { console.error( e ); }
8、class 可以從 javascript 中著名的幾大類中進行繼承:Array、number、string....,顯然 function 是做不到的。
下面給一個簡單的示例:
class Class01 extends Array { }
let ins01 = new Class01( 1, 2, 3 ); /* [1,2,3] */
let arr = ins01.shift(); /* [2,3] */
arr instanceof Class01; /* false */
ins01 instanceof Class01; /* true */
小tips:在 mozilla 的開發者指南中看到一種比較高端的東西(關於從原生類繼承肯定還有話題,會繼續學習):
static get [Symbol.species]() { return Array; }
參見 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
9、class 中有一個對象(姑且這么稱呼,關於 super 顯然還必須要有話題)super,這個對象可以取到父類的方法、構造函數等。
這顯然是 function 無法比擬的。在 class 的構造函數中,必須調用 super(),否則該子類就毫無用處。當然靜態方法還是可以用用。
下面有一個例子,可以重寫父類的方法,擴充父類方法的職責(注意是擴充而不是重寫,當然也可以選擇重寫)。
這一點相當的贊啊,有時候我們並不需要改寫父類的某個方法,我們可能會因為項目的需求而擴充父類接口的功能。
class Class01 {
constructor( name ){
this.name = name;
}
geta(){
console.log( this.name + ' 父類方法.' );
}
}
class Class02 extends Class01 {
geta(){
super.geta();
console.log( this.name + ' 子類方法.' );
}
}
var l = new Class02( 'Class02' );
l.geta();
10、class 中不存在實例方法,class 中定義所有方法都是原型方法。這些方法也都是不可枚舉的,使用 for in 這種方式無法遍歷到它們。
11、class 不能使用 return 來返回一個實例(等等,我還沒有試驗過,不好意思,我會馬上試驗一下)。
20180101編輯驗證,class 的 constructor 可以使用 return,因此 class 與 function 一樣可以返回任意的內容。
若不寫 return 語句,或返回是數值、字符串等非引用類型的值,則 constructor 任然會返回 this(實例)。return [] 或 return {} 都會使得 new 關鍵字並不會返回 class 的實例
說了這么多的不同點,再來說說 class 和 function 的相同之處:
1、靜態方法 在這點上,兩者還是有相似之處的。
盡管相似,顯然 class 要更加優雅,可讀性也更強,class 使用 static 關鍵詞指定靜態方法。
並且class 可以在函數體內定義靜態函數,而 function 不能,這無疑也讓 function 寫出來的代碼更加的復雜。
class Class01{
static geta(){
console.log( '01的靜態方法' );
}
}
function Class02 () { }
Class02.geta = function () { console.log( '02的靜態方法' ); }
Class01.geta();
Class02.geta();
2、私有屬性和方法
兩者都必須采用閉包的方式才能實現。下面給出 class 的私有屬性和方法。
var Class02 = ( function () {
let pVal ;
function sayHello(){
console.log( this ); /* this 指向 window */
console.log( '歡迎訪問nDos的博客' );
}
return class Class01{
constructor( v = '初始值' ){
pVal = v;
}
get val(){
sayHello();
return pVal;
}
set val( v ){
pVal = v;
}
};
} )();
let ins01 = new Class02();
console.log( ins01.val );
ins01.val = 'hello';
console.log( ins01.val );
ins01 instanceof Class02; /* true */
ins01.constructor.name; /* "Class01" */
小tips:上面的代碼顯示 ins01 是 Class02 的實例,但 ins01 的構造函數 name 屬性卻是 Class01。
顯然這在項目中不可行,會給類的使用者造成困惑。下例可解決這個問題:
var Class01 = ( function () {
return class Class01 { };
} )();
let ins01 = new Class01();
ins01 instanceof Class01; /* true */
ins01.constructor.name; /* "Class01" */
3、class 和 function 都有初始化的過程,也就是給實例 this 進行包裝處理的過程。二者盡管這個過程都存在,但還是有些區別,
class 只能在 constructor 中初始化,如果涉及繼承,還必須調用 super();function 的整個函數體都在執行初始化。
總結:
1、class 作為前端的面向對象設計之關鍵詞,使得前端大型項目的開發變得容易:類的管理更加清晰,功能更加強大。
2、顯然與 class 關鍵字一起出現的關鍵字 extends 是配套的。使得繼承關系更加清晰。
繼承再也不用寫一堆的prototype指着這個指着那個,項目代碼結構更加明確。 3、當然,關於 class 該博文並未挖的很深,留待下文。