您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)如何分析Js繼承與原型鏈,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
當(dāng)談到繼承時(shí),JavaScript 只有一種結(jié)構(gòu):對(duì)象。每個(gè)實(shí)例對(duì)象(object)都有一個(gè)私有屬性(稱之為 proto )指向它的構(gòu)造函數(shù)的原型對(duì)象(prototype)。該原型對(duì)象也有一個(gè)自己的原型對(duì)象(proto),層層向上直到一個(gè)對(duì)象的原型對(duì)象為 null。根據(jù)定義,null 沒有原型,并作為這個(gè)原型鏈中的最后一個(gè)環(huán)節(jié)。
幾乎所有 JavaScript 中的對(duì)象都是位于原型鏈頂端的 Object 的實(shí)例。
JavaScript 對(duì)象是動(dòng)態(tài)的屬性“包”(指其自己的屬性)。JavaScript 對(duì)象有一個(gè)指向一個(gè)原型對(duì)象的鏈。當(dāng)試圖訪問一個(gè)對(duì)象的屬性時(shí),它不僅僅在該對(duì)象上搜尋,還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象的原型的原型,依次層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾。
代碼實(shí)例
function fn() { this.a = 1; this.b = 2; } const o = new fn(); fn.prototype.b = 3; fn.prototype.c = 4; console.log(o.a); console.log(o.b); console.log(o.c); console.log(o.d); // 1 // 2 // 4 // undefined
a 和 b 是 o 的自身屬性可以直接返回值
為啥我們?cè)O(shè)置 fn.prototype.b=3,返回的還是 2 呢?,因?yàn)槲覀儾檎易陨碛羞@個(gè)屬性時(shí)就直接返回了,不會(huì)在往上面查找了。
c 不是 o 的自身屬性,所以會(huì)到 o.prototype 上去查找,發(fā)現(xiàn)有 c,直接返回值
d 不是 o 的自身屬性,所以會(huì)到 o.prototype 上去查找,發(fā)現(xiàn)沒有,再到 o.protype.prototype 上查找,發(fā)現(xiàn)為 null,停止搜索,返回 undefined
看下 o 構(gòu)造函數(shù)的打印
{ a: 1 b: 2 __proto__: b: 3 c: 4 constructor: ? fn() __proto__: constructor: ? Object() hasOwnProperty: ? hasOwnProperty() isPrototypeOf: ? isPrototypeOf() propertyIsEnumerable: ? propertyIsEnumerable() toLocaleString: ? toLocaleString() toString: ? toString() valueOf: ? valueOf() __defineGetter__: ? __defineGetter__() __defineSetter__: ? __defineSetter__() __lookupGetter__: ? __lookupGetter__() __lookupSetter__: ? __lookupSetter__() get __proto__: ? __proto__() set __proto__: ? __proto__() }
JavaScript 并沒有其他基于類的語(yǔ)言所定義的“方法”。在 JavaScript 里,任何函數(shù)都可以添加到對(duì)象上作為對(duì)象的屬性。函數(shù)的繼承與其他的屬性繼承沒有差別,包括上面的“屬性遮蔽”(這種情況相當(dāng)于其他語(yǔ)言的方法重寫)。
當(dāng)繼承的函數(shù)被調(diào)用時(shí),this 指向的是當(dāng)前繼承的對(duì)象,而不是繼承的函數(shù)所在的原型對(duì)象。
var o = { a: 2, m: function () { return this.a + 1; }, }; console.log(o.m()); // 3 // 當(dāng)調(diào)用 o.m 時(shí),'this' 指向了 o. var p = Object.create(o); // p是一個(gè)繼承自 o 的對(duì)象 p.a = 4; // 創(chuàng)建 p 的自身屬性 'a' console.log(p.m()); // 5 // 調(diào)用 p.m 時(shí),'this' 指向了 p // 又因?yàn)?nbsp;p 繼承了 o 的 m 函數(shù) // 所以,此時(shí)的 'this.a' 即 p.a,就是 p 的自身屬性 'a'
在 JavaScript 中,函數(shù)(function)是允許擁有屬性的。所有的函數(shù)會(huì)有一個(gè)特別的屬性 —— prototype 。默認(rèn)情況下是 Object 的原型對(duì)象
function doSomething() {} console.log(doSomething.prototype); // 和聲明函數(shù)的方式無(wú)關(guān), // JavaScript 中的函數(shù)永遠(yuǎn)有一個(gè)默認(rèn)原型屬性。 var doSomething = function () {}; console.log(doSomething.prototype);
在控制臺(tái)顯示的 JavaScript 代碼塊中,我們可以看到 doSomething 函數(shù)的一個(gè)默認(rèn)屬性 prototype。而這段代碼運(yùn)行之后,控制臺(tái)應(yīng)該顯示類似如下的結(jié)果:
{ constructor: ? doSomething(), __proto__: { constructor: ? Object(), hasOwnProperty: ? hasOwnProperty(), isPrototypeOf: ? isPrototypeOf(), propertyIsEnumerable: ? propertyIsEnumerable(), toLocaleString: ? toLocaleString(), toString: ? toString(), valueOf: ? valueOf() } }
我們可以給 doSomething 函數(shù)的原型對(duì)象添加新屬性,如下:
function doSomething() {} doSomething.prototype.foo = "bar"; console.log(doSomething.prototype);
可以看到運(yùn)行后的結(jié)果如下:
{ foo: "bar", constructor: ? doSomething(), __proto__: { constructor: ? Object(), hasOwnProperty: ? hasOwnProperty(), isPrototypeOf: ? isPrototypeOf(), propertyIsEnumerable: ? propertyIsEnumerable(), toLocaleString: ? toLocaleString(), toString: ? toString(), valueOf: ? valueOf() } }
現(xiàn)在我們可以通過 new 操作符來創(chuàng)建基于這個(gè)原型對(duì)象的 doSomething 實(shí)例。
代碼:
function doSomething() {} doSomething.prototype.foo = "bar"; // add a property onto the prototype var doSomeInstancing = new doSomething(); doSomeInstancing.prop = "some value"; // add a property onto the object console.log(doSomeInstancing);
運(yùn)行的結(jié)果類似于以下的語(yǔ)句。
{ prop: "some value", __proto__: { foo: "bar", constructor: ? doSomething(), __proto__: { constructor: ? Object(), hasOwnProperty: ? hasOwnProperty(), isPrototypeOf: ? isPrototypeOf(), propertyIsEnumerable: ? propertyIsEnumerable(), toLocaleString: ? toLocaleString(), toString: ? toString(), valueOf: ? valueOf() } } }
我們可以看到 prop 是 doSomeInstancing 的自身屬性,doSomeInstancing 中的proto就是 doSomething.prototype
我們打印下里面的屬性
console.log("doSomeInstancing.prop: " + doSomeInstancing.prop); console.log("doSomeInstancing.foo: " + doSomeInstancing.foo); console.log("doSomething.prop: " + doSomething.prop); console.log("doSomething.foo: " + doSomething.foo); console.log("doSomething.prototype.prop: " + doSomething.prototype.prop); console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);
結(jié)果如下:
// doSomeInstancing的自身屬性,直接返回值 doSomeInstancing.prop: some value // 不是doSomeInstancing的自身屬性,查看原型對(duì)象,發(fā)現(xiàn)有這個(gè)屬性直接返回值 doSomeInstancing.foo: bar // 不是函數(shù)自身的屬性,也不是原型對(duì)象上的屬性,一層層往上找,最后查找到prototype為null時(shí),表示沒有這個(gè)屬性,所以返回undefined doSomething.prop: undefined doSomething.foo: undefined doSomething.prototype.prop: undefined // 查找doSomething原型對(duì)象有foo屬性,所以直接返回值 doSomething.prototype.foo: bar
在原型鏈上查找屬性比較耗時(shí),對(duì)性能有副作用,這在性能要求苛刻的情況下很重要。另外,試圖訪問不存在的屬性時(shí)會(huì)遍歷整個(gè)原型鏈。
遍歷對(duì)象的屬性時(shí),原型鏈上的每個(gè)可枚舉屬性都會(huì)被枚舉出來。要檢查對(duì)象是否具有自己定義的屬性,而不是其原型鏈上的某個(gè)屬性,則必須使用所有對(duì)象從 Object.prototype 繼承的 hasOwnProperty 方法。下面給出一個(gè)具體的例子來說明它:
console.log(doSomeInstancing.hasOwnProperty("prop")); // true console.log(doSomeInstancing.hasOwnProperty("bar")); // false console.log(doSomeInstancing.hasOwnProperty("foo")); // false console.log(doSomeInstancing.__proto__.hasOwnProperty("foo")); // true
hasOwnProperty 是 JavaScript 中唯一一個(gè)處理屬性并且不會(huì)遍歷原型鏈的方法。
另一種這樣的方法:Object.keys()
注意:檢查屬性是否為 undefined 是不能夠檢查其是否存在的。該屬性可能已存在,但其值恰好被設(shè)置成了 undefined。
先說一下繼承,許多OO語(yǔ)言都支持兩張繼承方式:接口繼承、實(shí)現(xiàn)繼承。
|- 接口繼承:只繼承方法簽名
|- 實(shí)現(xiàn)繼承:繼承實(shí)際的方法
由于函數(shù)沒有簽名,在ECMAScript中無(wú)法實(shí)現(xiàn)接口繼承,只支持實(shí)現(xiàn)繼承,而實(shí)現(xiàn)繼承主要是依靠原型鏈來實(shí)現(xiàn)。
原型鏈基本思路:
利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)想指針(constructor),而實(shí)例對(duì)象都包含一個(gè)指向原型對(duì)象的內(nèi)部指針(__proto__)。如果讓原型對(duì)象等于另一個(gè)類型的實(shí)例,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型的指針(__proto__),另一個(gè)原型也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針(constructor)。假如另一個(gè)原型又是另一個(gè)類型的實(shí)例……這就構(gòu)成了實(shí)例與原型的鏈條。
原型鏈基本思路(圖解):
看完上述內(nèi)容,你們對(duì)如何分析Js繼承與原型鏈有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。