麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 網站 > WEB開發 > 正文

JavaScript 原型繼承

2024-04-27 15:17:43
字體:
來源:轉載
供稿:網友

簡介

繼承是面向對象語言的一個重要的特性和概念。許多的面向對象語言中都支持兩種繼承方式:接口繼承和實現繼承。

javaScript 僅支持實現繼承,主要是靠原型鏈來實現。

原型: Javascript 的所有函授都有一個PRototype屬性,這個屬性引用了一個對象,即原型對象,也簡稱原型。 注:在JavaScript中對象可以分為函數對象普通對象。只用函數對象包含prototype屬性。凡是通過 new Function() 創建的對象都是函數對象,其他的都是普通對象。

原型鏈:由于原型對象本身也是對象,它也有自己的原型。而它自己的原型對象又可以有自己的原型,這樣就組成了一條鏈,這個就是原型鏈。 JavaScritp引擎在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中查找。如果找到,直接返回值;如果整個鏈都遍歷且沒有找到屬性,則返回undefined。原型鏈一般實現為一個鏈表,這樣就可以按照一定的順序來查找。

構造函數:當任意一個普通函數用于創建一類對象時,它就被稱作構造函數,或構造器。構造函數必須滿子以下幾點: 1、在函數內部對新對象(this)的屬性進行設置,通常是添加屬性和方法。 2、構造函數可以包含返回語句(不推薦),但返回值必須是this,或者其它非對象類型的值。

關系: 1、構造函數通過 new 來創建實例對象;構造函數包含一個原型對象(prototype); 2、原型對象包含一個指向構造函數的指針(constructor); 3、實例對象包含一個指向原型對象的內部指針(_ _ proto _ _); 4、圖一: 這里寫圖片描述 5、圖二: 這里寫圖片描述

通過圖二來驗證圖一: 1、構造函數 A,通過 new 創建了實例對象 B。A.prototype 為 Object{}(原型對象); 2、Object{}(原型對象)的 constructor 指向構造函數 function A(); 3、B. _ _ proto _ _指向Object{}(原型對象)

原型鏈

圖三: 這里寫圖片描述 注:每個函數都是Function函數創建的對象,所以每個函數也有一個 _ _ proto _ _ 屬性指向Function函數的原型。這里需要指出的是,真正形成原型鏈的是每個對象的 _ _ proto _ _ 屬性,而不是函數的prototype屬性,這是很重要的。

圖四: 這里寫圖片描述 我們通過圖四來驗證圖三的正確性:

構造函數 A 創建了實例對象 B;B 的原型對象為 Object;Object.prototype 的原型對象指向 null;函數 A 的原型對象為 Function ;Function.prototype 的原型對象為 Object; 完全符合圖三。

綜圖所述

所有的對象都有 _ _ proto _ _ 屬性,該屬性對應該對象的原型.所有的函數對象都有prototype屬性,該屬性的值會被賦值給該函數創建的對象的 _ _ proto _ _ 屬性.所有的原型對象都有constructor屬性,該屬性對應創建所有指向該原型的實例的構造函數.函數對象和原型對象通過prototype和constructor屬性進行相互關聯.

繼承

1、基本模式:

var Parent = function() { this.name = 'parent';};Parent.prototype.getName = function() { return this.name;};Parent.prototype.obj = { a: 1};var Child = function() { this.name = 'child';};Child.prototype = new Parent();var parent = new Parent();var child = new Child();console.log(parent.getName()); //parentconsole.log(child.getName()); //child

這種是最簡單實現原型繼承的方法,直接把父類的對象賦值給子類構造函數的原型,這樣子類的對象就可以訪問到父類以及父類構造函數的prototype中的屬性。

圖五:這里寫圖片描述 如圖五可知:child -> Parent -> Function -> Object -> null

注:這種方法的優點很明顯,實現十分簡單,不需要任何特殊的操作;同時缺點也很明顯,如果子類需要做跟父類構造函數中相同的初始化動作,那么就得在子類構造函數中再重復一遍父類中的操作。如果初始化工作不斷增加,這種方式是很不方便的。

2、使用構造函數

var Parent = function(name) { this.name = name || 'parent';};Parent.prototype.getName = function() { return this.name;};Parent.prototype.obj = { a: 1};var Child = function(name) { // 在子類構造函數中通過apply調用父類的構造函數來進行相同的初始化工作。 // 這樣不管父類中做了多少初始化工作,子類也可以執行同樣的初始化工作 Parent.apply(this, arguments);};// Child.prototype = new Parent() ;// 使用這種方式父類構造函數被執行了兩次,一次是在子類構造函數中,一次在賦值子類原型時,這是很多余的。// 所以我們還需要做一個改進Child.prototype = Parent.prototype;var parent = new Parent('myParent');var child = new Child('myChild');console.log(parent.getName()); //myParentconsole.log(child.getName()); //myChild

圖六:這里寫圖片描述 根據圖六得知,此時的原型鏈為:child/parent -> Object -> null

注:上面借用構造函數模式最后改進的版本還是存在問題,它把父類的原型直接賦值給子類的原型,這就會造成一個問題,就是如果對子類的原型做了修改,那么這個修改同時也會影響到父類的原型,進而影響父類對象,這個肯定不是大家所希望看到的。

3、臨時構造函數模式(圣杯模式)

var Parent = function(name) { this.name = name || 'parent';};Parent.prototype.getName = function() { return this.name;};Parent.prototype.obj = { a: 1};var Child = function(name) { Parent.apply(this, arguments);};var F = function() {};F.prototype = Parent.prototype;Child.prototype = new F();var parent = new Parent('myParent');var child = new Child('myChild');console.log(parent.getName()); //myParentconsole.log(child.getName()); //myChild

圖七:這里寫圖片描述 如上圖:此時的原型鏈為:child -> parent -> Object -> null

4、再次改進

console.log(child.obj.a) ; //1console.log(parent.obj.a) ; //1child.obj.a = 2 ;console.log(child.obj.a) ; //2console.log(parent.obj.a) ; //2

我們在圣杯模式下添加上述代碼。查看結果會發現,當我們改變child.obj.a的值的時候,parent對應的也會改變。 出現這個情況是因為當訪問child.obj.a的時候,我們會沿著原型鏈一直找到父類的prototype中,然后找到了obj屬性,然后對obj.a進行修改。 改進方式:對父類原型進行拷貝然后再賦值給子類原型,這樣當子類修改原型中的屬性時就只是修改父類原型的一個拷貝,并不會影響到父類原型。

var deepClone = function(source, target) { source = source || {}; target = target || {}; var toStr = Object.prototype.toString, arrStr = '[object array]'; for (var i in source) { if (source.hasOwnProperty(i)) { var item = source[i]; if (typeof item === 'object') { target[i] = (toStr.apply(item).toLowerCase() === arrStr) ? [] : {}; deepClone(item, target[i]); } else { target[i] = item; } } } return target;};var Parent = function(name) { this.name = name || 'parent';};Parent.prototype.getName = function() { return this.name;};Parent.prototype.obj = { a: 1};var Child = function(name) { Parent.apply(this, arguments);};Child.prototype = deepClone(Parent.prototype);var parent = new Parent('myParent');var child = new Child('myChild');console.log(child.obj.a); //1console.log(parent.obj.a); //1child.obj.a = 2;console.log(child.obj.a); //2console.log(parent.obj.a); //1

總結:

說了這么多,其實Javascript中實現繼承是十分靈活多樣的,并沒有一種最好的方法,需要根據不同的需求實現不同方式的繼承,最重要的是要理解Javascript中實現繼承的原理,也就是原型和原型鏈的問題,只要理解了這些,自己實現繼承就可以游刃有余。

參考來源: 再談Javascript原型繼承:https://segmentfault.com/a/1190000000766541


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 国产美女视频一区 | 视频在线中文字幕 | 国内成人自拍视频 | 最近免费观看高清韩国日本大全 | 久久国产精品久久久久 | av在线大全| 国产亚洲美女精品久久久2020 | 极品xxxx欧美一区二区 | 成人黄色网战 | 黄色av片三级三级三级免费看 | 国产1区在线 | 久久精品女人天堂av | 国产成人精品区一区二区不卡 | av电影免费在线 | 天天躁狠狠躁夜躁2020挡不住 | 国产一国产精品一级毛片 | 成熟女人特级毛片www免费 | 国产精品99久久久久久大便 | 久久精品日韩一区 | 性生活视频一级 | 韩国十九禁高潮床戏在线观看 | 久久综合av | 视频在线91 | 91精品国产91久久久久久丝袜 | 国产一区在线观看视频 | 在线观看免费视频麻豆 | 国产成人在线观看免费网站 | 91午夜在线观看 | 免费永久看羞羞片网站入口 | 美女av在线免费观看 | 污黄视频在线观看 | 欧美一级黄色网 | 欧美在线观看视频网站 | 毛片免费在线播放 | 99精品视频一区二区三区 | 黄色久| 九九热在线视频免费观看 | 中国性xxx | 麻豆91精品91久久久 | 午夜视频在线免费观看 | 国产精品jk白丝蜜臀av软件 |