摘要:使用類創建實例對象也是直接對類使用命令,跟中構造函數的用法一致。中沒有構造函數,作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。
1 Class in ES6
ES6提出了類(Class)的概念,讓對象的原型的寫法更像面向對象語言寫法。 ES6中通過class定義對象,默認具有constructor方法和自定義方法,但是包含在class中的方法不可枚舉。
class Point{ constructor(){ this.x=x; this.y=y; } toString(){ return this.x+this.y; } }
注意:constructor方法對應ES5的構造函數;創建類的方法不需要使用function關鍵字,方法之間不需要逗號分隔。
ES5中定義對象時組合使用構造函數模式和原型模式,構造函數模式用于定義實例屬性,原型模式用于定義共享方法和共享屬性,節省內存,而且支持向構造函數傳遞參數。
function Point (x,y){ this.x=x; this.y=y; } Point.prototype.toString= function(){ return this.x+this.y; }2 ES5和ES6創建實例和對象對比
1)ES5中的Person構造函數和Person原型對象以及實例Person1、Person2的關系:構造函數的prototype屬性以及實例的__proto__屬性指向原型對象,原型對象的constructor屬性指向構造函數。
構造函數的原型(prototype)屬性在ES6中依舊存在,這一點和ES5一樣:類的方法都定義在類的原型(prototype)上,在類的實例上面調用方法,其實就是調用原型上的方法。
//實例的constructor方法就是類的原型(prototype)的constructor方法 class B{ constructor(){} } let b = new B(); console.log(b.constructor === B.prototype.constructor);//true
2)ES6的類可以看做是構造函數的另一種寫法(和ES5中構造函數等同的并不是constructor方法):類是function類型,且類本身指向類的prototype對象的constructor屬性,這與ES5一樣。
class Point{ // ... } console.log(typeof Point) // "function" console.log(Point === Point.prototype.constructor) // true
3)使用類創建實例對象也是直接對類使用new命令,跟ES5中構造函數的用法一致。
4)類內部定義的方法都是不可枚舉的,這和ES5不同。
5)constructor方法
一個類必須有constructor方法,如果沒有就默認添加constructor(){}。雖然類是函數,但是和ES5不同,不通過new而直接調用類會導致類型錯誤,也就是說這個函數僅當在和new一起使用時才有意義。
6)類的實例
與ES5一樣,實例的屬性除非顯式定義在其本身(即定義在this對象上),否則都是定義在原型上(即定義在class上)。
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty("x") // true point.hasOwnProperty("y") // true point.hasOwnProperty("toString") // false point.__proto__.hasOwnProperty("toString") // true
以上代碼中,x和y都是實例對象point自身的屬性(因為定義在this變量上),所以hasOwnProperty方法返回true,而toString是原型對象的屬性(因為定義在Point類上),所以hasOwnProperty方法返回false。這些都與ES5的行為保持一致。
與ES5一樣,類的所有實例共享一個原型對象。
var p1 = new Point(2,3); var p2 = new Point(3,2); p1.__proto__ === p2.__proto__ //true
可以通過實例的__proto__屬性為Class原型添加方法,增加后所有的原型都具有這個方法,這和ES5一樣。
var p1 = new Point(2,3); var p2 = new Point(3,2); p1.__proto__.printName = function () { return "Oops" }; p1.printName() // "Oops" p2.printName() // "Oops"
7)Class不存在變量提升(hoist),這一點與ES5完全不同。
new Foo(); // ReferenceError class Foo {}
上面代碼中,Foo類使用在前,定義在后,這樣會報錯,因為ES6不會把類的聲明提升到代碼頭部。這種規定的原因與繼承有關,必須保證子類在父類之后定義。
8)嚴格模式
類和模塊的內部,默認就是嚴格模式,所以不需要使用use strict指定運行模式。只要你的代碼寫在類或模塊之中,就只有嚴格模式可用。考慮到未來所有的代碼,其實都是運行在模塊之中,所以ES6實際上把整個語言升級到了嚴格模式。
Class通過extends關鍵字實現繼承,這和ES5通過修改原型鏈實現繼承不同(巧合的是,SASS也通過@extend實現樣式繼承):
class ColorPoint extends Point{}
子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后對其進行加工。如果不調用super方法,子類就得不到this對象。
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調用父類的constructor(x, y) this.color = color; } toString() { return this.color + " " + super.toString(); // super代表父類原型,調用父類的toString() } }
上面代碼中,constructor方法和toString方法之中,都出現了super關鍵字,它在這里表示父類的構造函數,用來新建父類的this對象。
super這個關鍵字,既可以當作函數使用,也可以當作對象使用。第一種情況,super作為函數調用時,代表父類的構造函數,只能用在子類的構造函數中。ES6 要求,子類的構造函數必須執行一次super函數。第二種情況,super作為對象時,指代父類的原型對象。
ES5的繼承,實質是先創造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.apply(this))。(ES5通過原型模式的繼承:創建子類,然后將父類的實例賦值給子類原型,也就是重寫子類原型,代之以一個新類型的實例。)ES6的繼承機制完全不同,實質是先創造父類的實例對象this(所以必須先調用super方法),然后再用子類的構造函數修改this。
如果子類沒有定義constructor方法,這個方法會被默認添加。在子類的構造函數中,只有調用super之后,才可以使用this關鍵字,否則會報錯。這是因為子類實例的構建,是基于對父類實例加工,只有super方法才能返回父類實例。
class Point { constructor(x, y) { this.x = x; this.y = y; } } class ColorPoint extends Point { constructor(x, y, color) { this.color = color; // ReferenceError super(x, y); this.color = color; // 正確 } }
和ES5一樣,通過子類創建的實例是父類以及子類的實例:
let cp = new ColorPoint(25, 8, "green"); cp instanceof ColorPoint // true cp instanceof Point // true4 補充 1)類的prototype屬性和__proto__屬性(這段還沒看明白,太繞了。)
ES5中每一個對象都有__proto__屬性,指向對應的構造函數的prototype屬性。ES6中沒有構造函數,Class作為構造函數的語法糖,同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈。
(1)子類的__proto__屬性,表示構造函數的繼承,總是指向父類。
(2)子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。
這樣的結果是因為,類的繼承是按照下面的模式實現的。
class A { } class B { } // B的實例繼承A的實例 Object.setPrototypeOf(B.prototype, A.prototype); const b = new B(); // B的實例繼承A的靜態屬性 Object.setPrototypeOf(B, A); const b = new B();
這兩條繼承鏈,可以這樣理解:作為一個對象,子類(B)的原型(__proto__屬性)是父類(A);作為一個構造函數,子類(B)的原型(prototype屬性)是父類的實例。
2)Object.getPrototypeOfObject.getPrototypeOf方法可以用來從子類上獲取父類。
Object.getPrototypeOf(ColorPoint) === Point // true
因此,可以使用這個方法判斷,一個類是否繼承了另一個類。
3)實例的__proto__屬性子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性。也就是說,子類實例的原型的原型,是父類實例的原型。
var p1 = new Point(2, 3); var p2 = new ColorPoint(2, 3, "red"); p2.__proto__ === p1.__proto__ // false p2.__proto__.__proto__ === p1.__proto__ // true
總的來說,ES6是對ES5的形式上的改變,真實內容依舊不變,本質上依舊是通過原型鏈實現繼承。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81821.html
摘要:使用類創建實例對象也是直接對類使用命令,跟中構造函數的用法一致。中沒有構造函數,作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。 1 Class in ES6 ES6提出了類(Class)的概念,讓對象的原型的寫法更像面向對象語言寫法。 ES6中通過class定義對象,默認具有constructor方法和自定義方法,但是包含...
摘要:類的方法相當于之前我們定義在構造函數的原型上。的構造函數中調用其目的就是調用父類的構造函數。是先創建子類的實例,然后在子類實例的基礎上創建父類的屬性。 前言 首先歡迎大家關注我的Github博客,也算是對我的一點鼓勵,畢竟寫東西沒法獲得變現,能堅持下去也是靠的是自己的熱情和大家的鼓勵。 許久已經沒有寫東西了,因為雜七雜八的原因最近一直沒有抽出時間來把寫作堅持下來,感覺和跑步一...
摘要:的類使用熟悉的關鍵字指定類繼承的函數,并且可以通過方法訪問父類的構造函數。例如繼承一個的類繼承了,術語上稱為基類,為派生類。例如注意到上例中,不僅是派生類的實例,也是派生類的實例,內建對象繼承的實用之處是改變返回對象的類型。 和其它面向對象編程語言一樣,ES6 正式定義了 class 類以及 extend 繼承語法糖,并且支持靜態、派生、抽象、迭代、單例等,而且根據 ES6 的新特性衍...
摘要:前言見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。倘若用的是中文搜索。所以最終的實例對象仍然能進行正常的原型鏈回溯,回溯到原本的所有原型方法這樣通過一個巧妙的欺騙技巧,就實現了完美的繼承。 前言 見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。 20180201更新: 修改用詞描述,如組合寄生式改成寄生組合式,修改多處筆誤(感謝@Yao Ding的...
摘要:寄生組合式繼承的繼承方式有多種主要有原型鏈繼承借用構造函數組合式繼承寄生式繼承和寄生組合式繼承。中利用定義類,實現類的繼承子類里調用父類構造函數實現實例屬性和方法的繼承子類原型繼承父類原型,實現原型對象上方法的繼承。 JavaScript中實現繼承 ??在JavaScript中實現繼承主要實現以下兩方面的屬性和方法的繼承,這兩方面相互互補,既有共享的屬性和方法,又有特有的屬性和方法。 ...
閱讀 3250·2021-11-11 11:00
閱讀 2571·2019-08-29 11:23
閱讀 1453·2019-08-29 10:58
閱讀 2332·2019-08-29 10:58
閱讀 2959·2019-08-23 18:26
閱讀 2514·2019-08-23 18:18
閱讀 2047·2019-08-23 16:53
閱讀 3421·2019-08-23 13:13