簡(jiǎn)介
類的概念,本身在javascript的語(yǔ)言上是不存在的, 但由于最近人們使用ES6語(yǔ)法,TS語(yǔ)言上都會(huì)有的class extends 繼承的概念, 下面我們需要使用原生js, 結(jié)合原型鏈,實(shí)現(xiàn)類的 繼承,多態(tài)ES5實(shí)現(xiàn)繼承
原型繼承
借用構(gòu)造函數(shù)繼承
mixin 復(fù)制繼承
寄生繼承
原型繼承方式原型繼承, 主要利用對(duì)象的原型鏈 __proto__, 每一個(gè)對(duì)象都擁有__proto__, 它指向的是構(gòu)造函數(shù)的prototype 原型對(duì)象.
一個(gè)對(duì)象的屬性或函數(shù)的尋找會(huì)經(jīng)歷以下幾個(gè)步驟。
以定義 var o = {};, 執(zhí)行 var toString = o.toString 為例.
執(zhí)行 var tmp = o,作為臨時(shí)引用 (為了描述使用)
嘗試檢查 tmp 是否自定義toString(),如果存在自定義屬性則立即執(zhí)行。如果當(dāng)前對(duì)象無(wú)定義該屬性, 進(jìn)入第3步
嘗試檢查 tmp 是否使用 Object.defineProperty 定義toString 的屬性描述, 如果存在定義,則直接引用,如果不存在則進(jìn)入第4步
嘗試檢查 tmp 是否存在 __proto__,如果存在,則將tmp = o.__proto__, 執(zhí)行第2步; 如果不存在,則返回 undefined, 屬性查找結(jié)束;
具體案例
function Animal () { throw new Error("抽象類, 不允許直接實(shí)例化"); } Animal.prototype.voice = function () { console.log("the " + this.name + " sound"); } function Dog () { this.name = "dog"; } Dog.prototype = Object.create(Animal.prototype); // 顯示指向 Dog.prototype.constructor = Dog; var dog = new Dog(); dog.voice(); // the dog sound console.log(dog instanceof Dog); console.log(dog instanceof Animal); // 隱世指向 Animal.prototype.constructor console.log(dog.__proto__.constructor === Animal);
優(yōu)點(diǎn):
可以使用 instanceof 檢測(cè)是否是某一個(gè)父類
原型鏈實(shí)現(xiàn)方式
缺點(diǎn):
無(wú)法借用父類構(gòu)造函數(shù)
借用構(gòu)造函數(shù)
// 模擬調(diào)用父類函數(shù) Object.prototype.super = function (proto, name) { var args = Array.from(arguments).slice(1); var proto = proto.__proto__; while (proto && null == proto[name]) { proto = proto[name]; } if (proto && typeof proto[name] === "function") { return proto[name].apply(this, args); } console.warn("the instance have not super " + name + " function"); }; function Animal (name) { this.name = name; } Animal.prototype.voice = function () { console.log(this.name + " sound"); }; function Dog (name, type) { Animal.apply(this, [name]); this.type = type; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Animal; Dog.prototype.voice = function () { console.log("the dog type is " + this.type); this.super(Animal.prototype, "voice"); }; var dog = new Dog("二哈", "哈士奇"); dog.voice(); // the dog type is 哈士奇 // 二哈 sound
優(yōu)點(diǎn):
能夠借用父類構(gòu)造函數(shù)
具備鏈?zhǔn)秸{(diào)用函數(shù)
缺點(diǎn):
子類構(gòu)造函數(shù)需要調(diào)用父類構(gòu)造函數(shù)
mixin復(fù)制繼承
依靠對(duì)象拷貝屬性的方式, 給予一個(gè)源對(duì)象未有的屬性賦值
function mixin(source, target) { for (var name in target) { if (!source[name]) { source[name] = target[name]; } } return source; } var Animal = { name: "animal", voice: function () { console.log("the name is " , this.name); console.log("voice~~"); } }; var Cat = mixin({ name: "cat", sound: function () { return this.voice(); } }, Animal); var helloKitty = mixin({ name: "hello keitty" }, Cat); helloKitty.sound();
優(yōu)點(diǎn):
實(shí)現(xiàn)簡(jiǎn)單,只需要進(jìn)行復(fù)制屬性和方法
缺點(diǎn):
處理對(duì)象都為對(duì)象, 沒(méi)有處理構(gòu)造函數(shù)
無(wú)法實(shí)現(xiàn)子類調(diào)用父類的場(chǎng)景
寄生繼承
寄生繼承屬于重寫(xiě), 新增父類創(chuàng)建的對(duì)象的屬性, 返回?cái)U(kuò)展的對(duì)象
function Animal() { this.speed = 10; } Animal.prototype.run = function () { console.log("speed is " + this.speed); } function Cat () { var animal = new Animal(); var runFn = animal.run; animal.speed = 20; animal.run = function () { console.log("the cat will run"); runFn.apply(this, arguments); }; return animal; } var cat = new Cat(); console.log(cat instanceof Cat);
優(yōu)點(diǎn):
結(jié)合原型屬性和實(shí)例屬性實(shí)現(xiàn)方案
缺點(diǎn):
無(wú)法共享屬性, 每一個(gè)新的對(duì)象都創(chuàng)建新的實(shí)例屬性和方法
Object.createObject.create 是ES5定義的方法, 相比于字面量對(duì)象,構(gòu)造函數(shù)對(duì)象, 又一種新的創(chuàng)建對(duì)象的方式。
var prototype = {foo: 1}; var o = Object.create(prototype); console.log(o.foo); // 1 o.foo = 100; console.log(o.foo); // 100 delete o.foo; console.log(o.foo); // 1 console.log(o.__proto__ === prototype); // true
從上面可以看見(jiàn), Object.create 傳入一個(gè)對(duì)象,同時(shí)會(huì)返回一個(gè)新的對(duì)象,而這個(gè)新的對(duì)象的__proto__指向傳入對(duì)象
Object.create(null)
返回一個(gè)無(wú)原型鏈的空對(duì)象, 對(duì)象的所有屬性均為實(shí)例屬性
Object.create 的 polyfill
// 簡(jiǎn)化版 polyfill Object.create = Object.create || function (proto) { function F() {} F.prototype = proto; return new F(); };ES6 的 class extends
說(shuō)完ES5的實(shí)現(xiàn)方式,我們來(lái)聊聊ES6自帶的語(yǔ)法。 class 與 class extends
ES6里的類參照其它語(yǔ)言,如 java, 類中存在靜態(tài)屬性, 靜態(tài)方法, 實(shí)例屬性,實(shí)例方法.
聲明一個(gè)類
class Rectangle { // 類的構(gòu)造函數(shù) constructor(height, width) { this.height = height; this.width = width; } } let rect = new Reactangle(320, 240); console.log(rect.height, rect.width);
注意: 類不存在聲明提前的概念,必須先定義后使用
使用 extends 創(chuàng)建子類class Animal { constructor(name) { // 實(shí)例屬性 this.name = name; } // 原型屬性描述 get fullname () { console.log(this.name); } // 原型方法 speak() { console.log(this.name + " makes a noise."); } } // 靜態(tài)屬性 Animal.name = "Animal"; class Dog extends Animal { construcotr(name) { // 調(diào)用父類構(gòu)造函數(shù) super(name); } speak() { console.log(this.name + " barks."); } // 靜態(tài)方法只適合創(chuàng)建工具函數(shù) // 返回 undefined static eat() { return this; } } var d = new Dog("Mitzie"); // "Mitzie barks." d.speak();
實(shí)際上ES6的class 與 class extends 也是使用的原型鏈方式實(shí)現(xiàn)繼承關(guān)系。 super 是一個(gè)關(guān)鍵詞, 實(shí)際上是指向父類的prototype, 在constructor 使用super(), 可以調(diào)用父類的構(gòu)造函數(shù), 使用super.method() 可以調(diào)用父類的原型方法。
原型屬性采用ES5的defineProperty定義屬性描述來(lái)實(shí)現(xiàn)。
目前JS的使用場(chǎng)景越來(lái)越廣, 面向?qū)ο缶幊痰氖褂靡苍絹?lái)越多, 前端已經(jīng)Node.js都有需要用到類與繼承。 同時(shí)這也是多年來(lái)不變的前端JS考題。
相關(guān)知識(shí)推薦Object.create
ES6 Class
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/94015.html
摘要:繼承了如上,我們通過(guò)方法借調(diào)了超類的構(gòu)造函數(shù),實(shí)際上是在新創(chuàng)建的實(shí)力環(huán)境下調(diào)用了構(gòu)造函數(shù)。組合繼承組合繼承的基本思想將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長(zhǎng)的一種繼承模式。繼承方法在上面這個(gè)例子中,構(gòu)造函數(shù)定義了兩個(gè)屬性和。 在ECMAScript中只支持實(shí)現(xiàn)繼承,而且實(shí)現(xiàn)繼承主要是依靠原型鏈來(lái)實(shí)現(xiàn)的。 1. 什么是原型鏈 繼承基本思想:利用原型讓一個(gè)引用類型繼承另一個(gè)...
摘要:有了原型鏈,就有了繼承,繼承就是一個(gè)對(duì)象像繼承遺產(chǎn)一樣繼承從它的構(gòu)造函數(shù)中獲得一些屬性的訪問(wèn)權(quán)。這里其實(shí)就是一個(gè)原型鏈與繼承的典型例子,開(kāi)發(fā)中可能構(gòu)造函數(shù)復(fù)雜一點(diǎn),屬性定義的多一些,但是原理都是一樣的。 作用域、原型鏈、繼承與閉包詳解 注意:本章講的是在es6之前的原型鏈與繼承。es6引入了類的概念,只是在寫(xiě)法上有所不同,原理是一樣的。 幾個(gè)面試常問(wèn)的幾個(gè)問(wèn)題,你是否知道 insta...
摘要:有了原型鏈,就有了繼承,繼承就是一個(gè)對(duì)象像繼承遺產(chǎn)一樣繼承從它的構(gòu)造函數(shù)中獲得一些屬性的訪問(wèn)權(quán)。這里其實(shí)就是一個(gè)原型鏈與繼承的典型例子,開(kāi)發(fā)中可能構(gòu)造函數(shù)復(fù)雜一點(diǎn),屬性定義的多一些,但是原理都是一樣的。 作用域、原型鏈、繼承與閉包詳解 注意:本章講的是在es6之前的原型鏈與繼承。es6引入了類的概念,只是在寫(xiě)法上有所不同,原理是一樣的。 幾個(gè)面試常問(wèn)的幾個(gè)問(wèn)題,你是否知道 insta...
摘要:接下來(lái)我們看下類的寫(xiě)法,這個(gè)就很接近于傳統(tǒng)面向?qū)ο笳Z(yǔ)言了。如果你想了解傳統(tǒng)面向?qū)ο笳Z(yǔ)言,這里是一個(gè)好切入點(diǎn)。作為對(duì)象時(shí),指向父類的原型對(duì)象。這些就是為將來(lái)在中支持面向?qū)ο蟮念悪C(jī)制而預(yù)留的。 在ES5中,我們經(jīng)常使用方法或者對(duì)象去模擬類的使用,并基于原型實(shí)現(xiàn)繼承,雖然可以實(shí)現(xiàn)功能,但是代碼并不優(yōu)雅,很多人還是傾向于用 class 來(lái)組織代碼,很多類庫(kù)、框架創(chuàng)造了自己的 API 來(lái)實(shí)現(xiàn) c...
閱讀 4116·2023-04-26 02:07
閱讀 3742·2021-10-27 14:14
閱讀 2965·2021-10-14 09:49
閱讀 1686·2019-08-30 15:43
閱讀 2696·2019-08-29 18:33
閱讀 2432·2019-08-29 17:01
閱讀 976·2019-08-29 15:11
閱讀 678·2019-08-29 11:06