摘要:在節(jié)中,我們學(xué)習(xí)到了通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象的三個(gè)重要步驟,其中的一步是把構(gòu)造函數(shù)的對(duì)象設(shè)置為創(chuàng)建對(duì)象的原型。利用而不是直接用創(chuàng)建一個(gè)實(shí)例對(duì)象的目的是,減少一次調(diào)用父構(gòu)造函數(shù)的執(zhí)行。
JavaScript語(yǔ)言不像面向?qū)ο蟮木幊陶Z(yǔ)言中有類的概念,所以也就沒有類之間直接的繼承,JavaScript中只有對(duì)象,使用函數(shù)模擬類,基于對(duì)象之間的原型鏈來(lái)實(shí)現(xiàn)繼承關(guān)系,
ES6的語(yǔ)法中新增了class關(guān)鍵字,但也只是語(yǔ)法糖,內(nèi)部還是通過(guò)函數(shù)和原型鏈來(lái)對(duì)類和繼承進(jìn)行實(shí)現(xiàn)。
JavaScript對(duì)象上都有一個(gè)內(nèi)部指針[[Prototype]],指向它的原型對(duì)象,而原型對(duì)象的內(nèi)部指針[[Prototype]]也指向它的原型對(duì)象,直到原型對(duì)象為null,這樣形成的鏈條就稱為原型鏈。
這樣在訪問(wèn)對(duì)象的屬性時(shí),會(huì)現(xiàn)在自己的屬性中查找,如果不存在則會(huì)到上一層原型對(duì)象中查找。
注意:
根據(jù) ECMAScript 標(biāo)準(zhǔn),someObject.[[Prototype]] 符號(hào)是用于指派 someObject 的原型。這個(gè)等同于 JavaScript 的 proto 屬性(現(xiàn)已棄用)。從 ECMAScript 6 開始, [[Prototype]] 可以用Object.getPrototypeOf()和Object.setPrototypeOf()訪問(wèn)器來(lái)訪問(wèn)。
例如:
var obj2 = { height: 170 } var obj3 = { name: "obj3" } Object.setPrototypeOf(obj3, obj2); console.log(obj3.height); // 170 var isproto = Object.getPrototypeOf(obj3) === obj2; console.log(isproto); // true1.2 不同方法創(chuàng)建對(duì)象與生成原型鏈 1.2.1 使用 Object.create 創(chuàng)建對(duì)象
ECMAScript 5 中引入了一個(gè)新方法:Object.create()??梢哉{(diào)用這個(gè)方法來(lái)創(chuàng)建一個(gè)新對(duì)象。新對(duì)象的原型就是調(diào)用 create 方法時(shí)傳入的第一個(gè)參數(shù)。
例如:
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (繼承而來(lái)) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, 因?yàn)閐沒有繼承Object.prototype1.2.2 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象
在 JavaScript 中,構(gòu)造函數(shù)其實(shí)就是一個(gè)普通的函數(shù),一般函數(shù)名首字母大寫。當(dāng)使用 new 操作符 來(lái)作用這個(gè)函數(shù)時(shí),它就可以被稱為構(gòu)造方法(構(gòu)造函數(shù))。
例如:
function Person (name, age) { this.name = name; this.age = age; } Person.prototype = { sayName: function () { console.log(this.name); } } var person1 = new Person("yangyiliang", 23); person1.sayName(); // yangyiliang
使用構(gòu)造函數(shù)創(chuàng)建對(duì)象,經(jīng)歷了如下三個(gè)關(guān)鍵步驟:
var temp = {}; //1 創(chuàng)建空對(duì)象 Person.call(temp, "yangyiliang", 23); //2 以空對(duì)象為this執(zhí)行構(gòu)造函數(shù) Object.setPrototypeOf(temp, Person.prototype); //3 將構(gòu)造函數(shù)的prototype 設(shè)置為空對(duì)象的原型 return temp;1.2.3 使用字面量方法創(chuàng)建對(duì)象
使用字面量方法創(chuàng)建的對(duì)象,根據(jù)對(duì)象的類型,他們的原型都會(huì)指向相應(yīng)JavaScript內(nèi)置構(gòu)造函數(shù)的prototype,和直接使用內(nèi)置構(gòu)造函數(shù)創(chuàng)建對(duì)象生成的原型鏈相同,例如:
var o = {a: 1}; // o這個(gè)對(duì)象繼承了Object.prototype上面的所有屬性 // 所以可以這樣使用 o.hasOwnProperty("a"). // hasOwnProperty 是Object.prototype的自身屬性。 // Object.prototype的原型為null。 // 原型鏈如下: // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 數(shù)組都繼承于Array.prototype // (indexOf, forEach等方法都是從它繼承而來(lái)). // 原型鏈如下: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // 函數(shù)都繼承于Function.prototype // (call, bind等方法都是從它繼承而來(lái)): // f ---> Function.prototype ---> Object.prototype ---> null2 繼承
在面向?qū)ο蟮恼Z(yǔ)言當(dāng)中,繼承關(guān)系應(yīng)該指的是父類和子類之間的關(guān)系,子類繼承父類的屬性和方法,在JavaScript當(dāng)中是父構(gòu)造函數(shù)和子構(gòu)造函數(shù)之間的關(guān)系。
類本身是對(duì)象的抽象形式,類的使用價(jià)值最后也是在于通過(guò)它能夠創(chuàng)建對(duì)象,
所以子類能夠繼承父類的屬性和方法的意義,就是通過(guò)子類創(chuàng)建出來(lái)的對(duì)象能夠繼承通過(guò)父類創(chuàng)建出來(lái)的對(duì)象的屬性和方法。
而這種對(duì)象之間的繼承關(guān)系,就是通過(guò)原型鏈實(shí)現(xiàn)。
在1.2.2節(jié)中,我們學(xué)習(xí)到了通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象的三個(gè)重要步驟,其中的一步是把構(gòu)造函數(shù)的prototype對(duì)象設(shè)置為創(chuàng)建對(duì)象的原型。
因此我們將父類的實(shí)例對(duì)象作為子類的prototype即能夠達(dá)到繼承的目的,如下圖所示:
繼承的實(shí)現(xiàn)
function Person (name, age) { this.name = name; this.age = age } Person.prototype.sayName = function () { console.log("my name is " + this.name); } function Student (name, age, school) { Person.call(this, name, age); this.school = school; } Student.prototype = Object.create(Person.prototype); Student.prototype.saySchool = function () { console.log("my school is " + this.school); }
上面代碼實(shí)現(xiàn)的繼承,遵循了幾個(gè)原則:
1、因?yàn)闃?gòu)造函數(shù)創(chuàng)建的對(duì)象將公用同一個(gè)原型,所以將每個(gè)對(duì)象獨(dú)有的屬性寫在構(gòu)造函數(shù)中,將對(duì)象之間可以公用的方法寫在構(gòu)造函數(shù)的prototype中,也就是對(duì)象的原型中
2、子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)做了兩個(gè)地方的工作,一是在子構(gòu)造函數(shù)中利用call,調(diào)用父構(gòu)造函數(shù)的方法,二是利用Object.create方法創(chuàng)建一個(gè)以父構(gòu)造函數(shù)的prototype為原型的對(duì)象。
利用Object.create而不是直接用new 創(chuàng)建一個(gè)實(shí)例對(duì)象的目的是,減少一次調(diào)用父構(gòu)造函數(shù)的執(zhí)行。
3、先通過(guò)prototype屬性指向父構(gòu)造函數(shù)的實(shí)例,然后再向prototype添加想要放在原型上的方法。
最后上一張js高級(jí)程序設(shè)計(jì)第三版中的一張?jiān)从谠玩溊^承的圖
利用class實(shí)現(xiàn)繼承下面利用ES6引入的新語(yǔ)法糖,class、extends關(guān)鍵字對(duì)上述實(shí)現(xiàn)繼承的代碼進(jìn)行改寫:
class Person { constructor (name, age) { this.name = name; this.age = age; } sayName () { console.log("my name is " + this.name); } } class Student extends Person { constructor (name, age, school) { super(name, age); this.school = school; } saySchool () { console.log("my school is " + this.school); } }
class里的constructor 對(duì)應(yīng)原來(lái)的構(gòu)造函數(shù)
class里面的其他方法都是寫在原來(lái)構(gòu)造函數(shù)的prototype中的
子類直接通過(guò)extends 關(guān)鍵字進(jìn)行繼承
子類中可以通過(guò)super來(lái)調(diào)用父類中的方法
本文部分內(nèi)容來(lái)自 https://developer.mozilla.org...
后續(xù)
function A() { } //函數(shù)默認(rèn)會(huì)有一個(gè)prototype對(duì)象并且具有constructor屬性指向他本身 var a = new A() a instanceof A
function A() { } function B() { } var proto = {} B.prototype = proto A.prototype = proto var a = new A() a instanceof B //true a instanceof Object //true instanceof 是遍歷a 的原型鏈 尋找是否有和 B.prototype 是同一個(gè)對(duì)象的__proto__ 如果找到就為true
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/86638.html
摘要:忍者級(jí)別的函數(shù)操作對(duì)于什么是匿名函數(shù),這里就不做過(guò)多介紹了。我們需要知道的是,對(duì)于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...
摘要:有了原型鏈,就有了繼承,繼承就是一個(gè)對(duì)象像繼承遺產(chǎn)一樣繼承從它的構(gòu)造函數(shù)中獲得一些屬性的訪問(wèn)權(quán)。這里其實(shí)就是一個(gè)原型鏈與繼承的典型例子,開發(fā)中可能構(gòu)造函數(shù)復(fù)雜一點(diǎn),屬性定義的多一些,但是原理都是一樣的。 作用域、原型鏈、繼承與閉包詳解 注意:本章講的是在es6之前的原型鏈與繼承。es6引入了類的概念,只是在寫法上有所不同,原理是一樣的。 幾個(gè)面試常問(wèn)的幾個(gè)問(wèn)題,你是否知道 insta...
摘要:有了原型鏈,就有了繼承,繼承就是一個(gè)對(duì)象像繼承遺產(chǎn)一樣繼承從它的構(gòu)造函數(shù)中獲得一些屬性的訪問(wèn)權(quán)。這里其實(shí)就是一個(gè)原型鏈與繼承的典型例子,開發(fā)中可能構(gòu)造函數(shù)復(fù)雜一點(diǎn),屬性定義的多一些,但是原理都是一樣的。 作用域、原型鏈、繼承與閉包詳解 注意:本章講的是在es6之前的原型鏈與繼承。es6引入了類的概念,只是在寫法上有所不同,原理是一樣的。 幾個(gè)面試常問(wèn)的幾個(gè)問(wèn)題,你是否知道 insta...
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠?lái)都是中的主導(dǎo)范式。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠?lái)都是JavaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語(yǔ)言,然而,近幾年,函數(shù)式編程越來(lái)越多得受到開發(fā)者的青睞。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
閱讀 898·2021-10-09 09:44
閱讀 769·2019-08-30 13:55
閱讀 3238·2019-08-29 15:07
閱讀 3344·2019-08-29 13:09
閱讀 2481·2019-08-29 11:10
閱讀 1376·2019-08-26 14:05
閱讀 3768·2019-08-26 13:57
閱讀 2275·2019-08-23 16:42