摘要:由一個問題引發的思考這個方法是從哪兒蹦出來的首先我們要清楚數組也是對象,而且是對象的實例也就是說,下面兩種形式是完全等價的只不過是一種字面量的寫法,在深入淺出面向對象和原型概念篇文章里,我們提到過類會有一個屬性,而這個類的實例可以通過屬性訪
1.由一個問題引發的思考
let arr1 = [1, 2, 3] let arr2 = [4, 5, 6] arr1.concat(arr2) // [1, 2, 3, 4, 5, 6]concat這個方法是從哪兒蹦出來的??
首先我們要清楚 數組也是對象,而且是Array對象的實例 console.log(arr1 instanceof Array) // true 也就是說,下面兩種形式是完全等價的 let arr1 = [1, 2, 3] let arr2 = new Array(1, 2, 3) 只不過[1,2,3]是一種字面量的寫法
ok,在深入淺出面向對象和原型【概念篇2】文章里,我們提到過
類會有一個prototype屬性,而這個類的實例可以通過__proto__屬性訪問類的prototype屬性
既然arr1是Array對象的實例,那么arr1自然可以通過其__proto__屬性訪問到其類Array的屬性
只要Array的prototype里有concat()這個方法,那么arr1自然能用
console.log(arr1.__proto__ === Array.prototype) // true2.第二個問題 arr1.valueOf(),valueOf從哪兒來的?
按照第一個問題的解決思路,valueOf應該是Array的prototype屬性里的 但是在Array的prototype屬性里并沒有找到valueOf方法 但是我們又看到了Array的prototype里竟然也有__proto__屬性,并且指向Object,在這里我們找到了valueOf這是為什么呢?
現在我們需要知道的是Array是Object的實例
console.log(Array instanceof Object) // true
好了,現在我們知道了 1.arr1 是 Array 的實例 2.Array 是 Object 的實例 當arr1找不到valueOf時,會通過其自身的__proto__屬性去找Array的prototype,看看里面有沒有 如果Array的prototype里沒有的話,接下來會通過Array的prototype里的__proto__屬性去找Object的prototype,看看里面有沒有 而反觀arr1尋找concat時,因為直接就在Array的prototype里找到了,所以不會再去通過__proto__尋找Object的prototype里有沒有
我們把這個過程抽象化表達
當一個實例調用一個方法時
如果實例本身沒有這個方法,那么這個實例會通過自身的__proto__屬性去訪問其類的prototype屬性
如果該實例的類的prototype屬性也沒有,那么會通過該類prototype屬性里的__proto__屬性去訪問該類的類的prototype屬性
若還沒有找到,則以此類推,直到找到該方法或者父級類的prototype內的__proto__為null時為止
這個不斷通過__proto__屬性和prototype屬性尋找的過程,叫做原型鏈
3.自己實現繼承 3.1 什么是繼承繼承是指一個對象直接使用另一對象的屬性和方法3.2 繼承的目的
繼承的目的其實就是省代碼,什么意思呢? 假設你現在是js設計師,你已經設計了Object對象 現在你需要基于Object對象再設計一個Array對象 并且Array對象可以直接使用Object對象的屬性和方法 這個時候你肯定希望能夠通過一種方式能直接讓Array對象使用Object對象的屬性和方法 而不是把Object對象的屬性和方法重新寫到Array里去,這樣做太累了 而這種方式就叫繼承3.3 實現繼承的要點
由繼承的定義,我們可知,實現繼承必須滿足兩個條件 1.得到一個類的屬性 2.得到一個類的方法第一步:我們先定義兩個類
// 第一個類 function People(name, age) { this.name = name this.age = age } People.prototype = { printName: function () { console.log(this.name) } } // 第二個類 function Male(sex) { this.sex = sex } Male.prototype = { printSex: function () { console.log(this.sex) } }第二步:讓Male的每個實例都獲取到People的屬性
function Male(sex, name, age) { // 注意Male的實例在這里就是this // 所以我們要讓this獲取到People的屬性即可 // 實現方式——讓People的this被替換成Male的this,且讓People被直接調用 People.bind(this)(name, age) this.sex = sex } console.log(new Male("man", "bruce", "16")) // {name: "bruce", age: "16", sex: "man"}第三步:讓Male的每個實例都獲取到People的方法
Object.create()
// 例子 let a = Object.create({"zxz": 1}) console.log(a.__proto__) // {zxz: 1} // Object.create() 方法會返回一個對象,這個對象擁有被指定的原型 // 且這個對象可通過自身的__proto__屬性訪問這個原型
Male.prototype = Object.create(People.prototype) // 這一步結束后,Male的實例就可以通過原型鏈擁有People的方法 // 接著,我們才能對Male自己要新增的方法進行添加,否則會被覆蓋掉 Male.prototype.printSex = function () { console.log(this.sex) } console.log((new Male("man", "bruce", "16")).__proto__.__proto__ === People.prototype)第四步:注意constructor屬性
我們都知道constructor屬性指向其類 當我們完成第三步后 需要注意的是 console.log(Male.prototype.constructor === People.prototype.constructor) // true 因為執行了Object.create()的原因,此時Male原型上的constructor被指定成People了 現在需要我們手動賦值將其更改 Male.prototype.constructor = Male現在,我們可以把上述步驟統一起來
function People(name, age) { this.name = name this.age = age } People.prototype = { printName: function () { console.log(this.name) } } function Male(sex, name, age) { People.bind(this)(name, age) this.sex = sex } Male.prototype = Object.create(People.prototype) Male.prototype.constructor = Male Male.prototype.printSex = function () { console.log(this.sex) }3.4 把繼承獲取方法的關鍵步驟封裝起來
function inherit(fatherObject, childObject) { let _prototype = Object.create(fatherObject.prototype); _prototype.constructor = childObject; childObject.prototype = _prototype; }3.5 完整代碼
function inherit(fatherObject, childObject) { let _prototype = Object.create(fatherObject.prototype); _prototype.constructor = childObject; childObject.prototype = _prototype; } function People(name, age) { this.name = name this.age = age } People.prototype = { printName: function () { console.log(this.name) } } function Male(sex, name, age) { People.bind(this)(name, age) this.sex = sex } inherit(People, Male) Male.prototype.printSex = function () { console.log(this.sex) } let bruce = new Male("man", "bruce", 16)4.實現繼承的其它方法
function People(name, age) { this.name = name this.age = age } People.prototype.sayName = function () { console.log(this.name) } function Student(name, age, score) { People.call(this, name, age) this.score = score } function create(prototypeObj) { let empty = function () {} empty.prototype = prototypeObj return new empty() // return值如下 // { // __proto__:prototypeObj // } } Student.prototype = create(People.prototype) Student.prototype.work = function () { console.log("work") }5.hasOwnProperty
當Male的實例繼承了Male的方法和People的屬性和方法后,如何分辨某個屬性或方法是自己的還是通過原型鏈繼承來的呢? 這就要用到 hasOwnProperty 該方法可以判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性 并且會忽略掉那些從原型鏈上繼承到的屬性 console.log(bruce.hasOwnProperty("age")) // true console.log(bruce.hasOwnProperty("printSex")) // false 因為printSex方法是bruce實例通過原型鏈從Male類的原型上獲取的,因此會被hasOwnProperty忽略
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92945.html
摘要:在使用原型鏈實現繼承時有一些需要我們注意的地方注意繼承后的變化。在了解原型鏈時,不要忽略掉在末端還有默認的對象,這也是我們能在所有對象中使用等對象內置方法的原因。 在上一篇post中,介紹了原型的概念,了解到在javascript中構造函數、原型對象、實例三個好基友之間的關系:每一個構造函數都有一個守護神——原型對象,原型對象心里面也存著一個構造函數的位置,兩情相悅,而實例呢卻又...
摘要:原文地址詳解的類博主博客地址的個人博客從當初的一個彈窗語言,一步步發展成為現在前后端通吃的龐然大物。那么,的類又該怎么定義呢在面向對象編程中,類是對象的模板,定義了同一組對象又稱實例共有的屬性和方法。這個等同于的屬性現已棄用。。 前言 生活有度,人生添壽。 原文地址:詳解javascript的類 博主博客地址:Damonare的個人博客 ??Javascript從當初的一個彈窗語言,一...
摘要:原文地址詳解的類博主博客地址的個人博客從當初的一個彈窗語言,一步步發展成為現在前后端通吃的龐然大物。那么,的類又該怎么定義呢在面向對象編程中,類是對象的模板,定義了同一組對象又稱實例共有的屬性和方法。這個等同于的屬性現已棄用。。 前言 生活有度,人生添壽。 原文地址:詳解javascript的類 博主博客地址:Damonare的個人博客 ??Javascript從當初的一個彈窗語言,一...
摘要:接下來我們來聊一下的原型鏈繼承和類。組合繼承為了復用方法,我們使用組合繼承的方式,即利用構造函數繼承屬性,利用原型鏈繼承方法,融合它們的優點,避免缺陷,成為中最常用的繼承。 JavaScript是一門面向對象的設計語言,在JS里除了null和undefined,其余一切皆為對象。其中Array/Function/Date/RegExp是Object對象的特殊實例實現,Boolean/N...
閱讀 2847·2021-09-27 13:35
閱讀 624·2021-09-23 11:22
閱讀 2892·2019-08-30 15:54
閱讀 1612·2019-08-29 16:27
閱讀 2468·2019-08-29 15:05
閱讀 2350·2019-08-23 18:11
閱讀 3523·2019-08-23 16:32
閱讀 2941·2019-08-23 14:56