国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

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

javascript基礎--對象(Object)繼承

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

引言

上一節《javascript基礎–對象(Object)封裝》講了如何造人,這一節將會總結怎么理清人與人之間的血緣關系,即繼承關系。對象繼承簡單地分為兩類,構造函數繼承和非構造函數繼承。所謂構造函數繼承就是利用this綁定對象的模式創建的對象之間的繼承關系;而非構造函數繼承就是利用對象字面量創建的對象之間的繼承關系。上代碼:

//Person父對象利用this綁定,子對象Tom,Linda也同樣采用這種模式創建function Person(name,sex){ this.name = name; this.sex = sex;}Person.PRototype.say = function(){ console.log(this.name +' is ' + this.sex);}//創建的Tom,Linda去繼承Person父對象,此種繼承成為構造函數繼承//Person采用對象字面量創建,子對象Tom,Linda也同樣采用這種模式創建var Person = { Pname: 'Person', Psex: 'Person sex is male or female', say: function(){ console.log('haha'); }}//創建的Tom,Linda去繼承Person父對象,此種繼承成為非構造函數繼承

1.構造函數繼承

首先確定父對象,即引言內的父對象,copy下來

function Person(name,sex){ this.name = name; this.sex = sex;}Person.prototype.say = function(){ console.log(this.name +' is ' + this.sex);}

我們的任務是創建Tom和Linda去繼承Person,我們先創建子對象模型

//Tom構造函數function Tom(name,sex){}Tom.prototype = {}var T = new Tom('Tom','male');//Linda構造函數function Linda(name,sex){}Linda.prototype = {}var L = new Linda('Linda','female');

分析:父對象Person中屬性和方法分為兩部分,name和sex屬性是在構造函數Person上,而say方法是在Person.prototype上。所以要繼承Person所有屬性和方法,分為兩步,即繼承構造函數上的屬性和繼承原型對象上的方法。

2.構造函數繼承–對象冒充

對象冒充,是利用call或者apply方法,將父對象的屬性和方法綁定在子對象上,從而完成構造函數上屬性和方法的繼承,但是person原型對象上的say方法不會繼承。這里簡單介紹一下call和apply

//call定義A.call(B,Object);//釋義:調用A對象的方法,以B對象替換當前的A對象,其中參數是Object//apply定義A.apply(B,array);//釋義:調用A對象的方法,以B對象替換當前的A對象,其中參數必須是Array類型//so call和apply區別就在參數的形式上不同

現在來完善Tom和Linda構造函數

//Tom構造函數function Tom(name,sex){ Person.apply(this,arguments);}Tom.prototype = {}var T = new Tom('Tom','male');//Linda構造函數function Linda(name,sex){ Person.apply(this,arguments);}Linda.prototype = {}var L = new Linda('Linda','female');

測試:

//可見Tom和Linda成功繼承了Person構造函數上的屬性,Person原型對象屬性怎么繼承呢?T.name;//'Tom'T.sex;//'male'T.say;//undefinedL.name;//'Linda'L.sex;//'female'L.say;//undefined

3.構造函數繼承–原型繼承

原型繼承,顧名思義肯定要在子對象原型上做文章,首先想到的是直接繼承Person.protype,就像這樣

//Tom構造函數function Tom(name,sex){ Person.apply(this,arguments);}Tom.prototype = Person.prototype;var T = new Tom('Tom','male');//Linda構造函數function Linda(name,sex){ Person.apply(this,arguments);}Linda.prototype = Person.prototype;var L = new Linda('Linda','female');

測試:

//say被繼承了,完了??沒完T.say();//Tom is maleL.say();//Linda is female

我們為Tom原型對象上添加一個run方法,Linda原型對象上不添加,期望結果是T.run返回function,L.run返回undefined

Tom.prototype.run = function(){ console.log('Tom running');}//測試T.run();//'Tom running'L.run();//'Tom running'Person.prototype.run();//'Tom running'//可見修改Tom的原型對象也同樣修改了Person.prototype

原因是,prototype是一個地址,子對象原型繼承的是地址,而非數值。在C語言中,指針就是這樣一個概念。那怎么避免呢?既然地址不行,那就用值,不就ok了。所以讓子對象原型繼承父對象實例是一種方式。

//Tom構造函數function Tom(name,sex){ Person.apply(this,arguments);}//想想封裝函數的原型鏈,任何一個prototype對象都有一個constructor屬性,指向它的構造函數。如果沒有"Tom.prototype = new Person();"這一行,Tom.prototype.constructor是指向Tom的;加了這一行以后,Tom.prototype.constructor指向Person。所以我們必須手動糾正Tom.prototype = new Person();Tom.prototype.constructor = Tom;var T = new Tom('Tom','male');//Linda構造函數function Linda(name,sex){ Person.apply(this,arguments);}//同理Linda.prototype = new Person();Linda.prototype.constructor = Linda;var L = new Linda('Linda','female');Tom.prototype.run = function(){ console.log('Tom running');}

測試:

//測試結果表明,我們成功了T.say();//'Tom is male'L.say();//'Linda is female'T.run();//'Tom running'L.run();//undefinedPerson.prototype.run();//undefined

除了利用繼承值以外,還有別的方法嗎?肯定有,那就是利用空對象作為中介,讓子對象的原型繼承空對象,空對象繼承父對象原型,這樣間接實現繼承,而且避免了直接繼承prototype的缺點,給子對象添加自身額外的方法時只會改變空對象,而不會改變父對象,這樣就達到和繼承值一樣的目的。

var Empty = function(){};Empty.prototype = Person.prototype;Tom.prototype = new Empty();Tom.prototype.constructor = Tom;

將此方法進行封裝成extend方法

function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; //為了保證繼承函數的完整性,增添一個通向父對象原型的接口 Child.uber = Parent.prototype;}

現在將Tom和Linda構造函數改變

//Tom構造函數function Tom(name,sex){ Person.apply(this,arguments);}//調用extend函數extend(Tom,Person);var T = new Tom('Tom','male');//Linda構造函數function Linda(name,sex){ Person.apply(this,arguments);}//同理extend(Linda,Person);var L = new Linda('Linda','female');Tom.prototype.run = function(){ console.log('Tom running');}

測試:

//成功了T.name;//'Tom'T.say();//'Tom is male'T.run;//functionL.name;//'Linda'L.say();//'Linda is female'L.run;//undefinedPerson.prototype.run;//undefined

4.構造函數繼承–拷貝繼承

上面講了為了繼承原型上的屬性和方法,我們使用了原型繼承。這里有一個新思路,就是拷貝繼承,即將父對象原型上的屬性和方法直接拷貝到子對象上,從而實現繼承。這樣也避免了改變子對象原型的時候不會改變父對象原型,因為拷貝的是值而不是地址。

function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } //為了保證繼承函數的完整性,增添一個通向父對象原型的接口 c.uber = p;}

現在改變Tom,Linda構造函數

//Tom構造函數function Tom(name,sex){ Person.apply(this,arguments);}//調用extend2函數extend2(Tom,Person);var T = new Tom('Tom','male');//Linda構造函數function Linda(name,sex){ Person.apply(this,arguments);}//同理extend2(Linda,Person);var L = new Linda('Linda','female');Tom.prototype.run = function(){ console.log('Tom running');}

測試,不出意外,結果一樣

//意料之中,成功了T.name;//'Tom'T.say();//'Tom is male'T.run;//functionL.name;//'Linda'L.say();//'Linda is female'L.run;//undefinedPerson.prototype.run;//undefined

5.非構造函數繼承

還是先確定父對象,即引言內的父對象,copy下來

var Person = { Pname: 'Person', Psex: 'Person sex is male or female', say: function(){ console.log('haha'); }}

我們的任務是創建Tom和Linda去繼承Person,我們先創建子對象模型

var Tom = {};var Linda = {};

分析:這里要注意,這兩個對象都是普通對象,不是構造函數,無法使用構造函數方法實現”繼承”。

6.非構造函數繼承–object()方法

json格式的發明人Douglas Crockford,提出了一個object()函數

function object(o){ function F(){}; F.prototype = o; return new F();}

添加Tom和Linda對象私有方法

Tom = object(Person);Tom.name = 'Tom';Linda = object(Person);Linda.name = 'Linda';

測試:

Tom.Pname;//'Person'Tom.Psex;//'Person sex is male or female'Tom.say;//functionTom.name;//'Tom'Linda.Pname;//'Person'Linda.Psex;//'Person sex is male or female'Linda.say;//functionLinda.name;//'Linda'

7.非構造函數繼承–淺拷貝

和構造函數的實現思路一樣,拷貝也是實現繼承的一種方式

function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } //為了保證繼承函數的完整性,增添一個通向父對象的接口 c.uber = p; return c;}

改變Tom, Linda對象

Tom = extendCopy(Person);Tom.name = 'Tom';Linda = extendCopy(Person);Linda.name = 'Linda';

測試:

//同樣成功了Tom.Pname;//'Person'Tom.Psex;//'Person sex is male or female'Tom.say;//functionTom.name;//'Tom'Linda.Pname;//'Person'Linda.Psex;//'Person sex is male or female'Linda.say;//functionLinda.name;//'Linda'

為什么叫做淺拷貝呢,現在假設父對象中有一個屬性是數組或者對象,為父對象增加一個屬性color數組

Person.color = [yellow,white,black];//Tom繼承PersonTom = extendCopy(Person);//測試Tom.color;//[yellow,white,black]//現在我們為Tom.colorTom.color.push('blue');//測試Tom.color;//[yellow,white,black,blue]Person.color;//[yellow,white,black,blue]//???為什么改變子對象,父對象也跟著改變了呢?//原因是數組或對象是地址,相當于拷貝了地址,所以會出現這種情況。為了避免這種情況,也就有了深拷貝

8.非構造函數繼承–深拷貝

深拷貝是在前拷貝的基礎上增添了對父對象屬性的判斷,判斷是否為地址,如果是,則遞歸拷貝函數

function deepCopy(p, c) { var c = c || {}; for (var i in p) { if (typeof p[i] === 'object') { c[i] = (p[i].constructor === Array) ? [] : {}; deepCopy(p[i], c[i]); } else { c[i] = p[i]; } } return c;}

改變Tom和Linda對象

Tom = deepCopy(Person,Tom);Tom.name = 'Tom';Linda = deepCopy(Person,Tom);Linda.name = 'Linda';

測試:

Person.color = [yellow,white,black];//Tom繼承PersonTom = extendCopy(Person);//測試Tom.color;//[yellow,white,black]//現在我們為Tom.colorTom.color.push('blue');//測試Tom.color;//[yellow,white,black,blue]//父對象沒有改變Person.color;//[yellow,white,black]

jQuery使用的深拷貝

結束語

對象的繼承到這里就結束了,其實也蠻簡單的。針對兩種情況,構造函數繼承其實就是對象冒充,原型,拷貝三種方式;非構造函數就是object()和淺深拷貝。只要深入理解,記憶起來還是蠻easy


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 慈溪市| 建昌县| 青神县| 赞皇县| 宿松县| 锡林郭勒盟| 新竹县| 宁晋县| 安仁县| 铁岭县| 北京市| 延吉市| 美姑县| 孟村| 光泽县| 禹城市| 中江县| 彭州市| 泸州市| 吴堡县| 开远市| 政和县| 阿拉善左旗| 成武县| 万安县| 清河县| 莎车县| 新蔡县| 溆浦县| 雷波县| 无棣县| 浮山县| 阿鲁科尔沁旗| 固镇县| 民丰县| 香格里拉县| 荥阳市| 济阳县| 伊春市| 怀来县| 孝昌县|