JavaScript的繼承實現(xiàn)小結(jié)
最近在忙前端的工作,因為之前做.net和php的開發(fā)比較多,前端開發(fā)喜歡把庫拿來就用,幾次事實證明,不懂原理,連改代碼也改不好,所以還是下定決心研究下JavaScript的幾個技術(shù)難點(diǎn)。
0x1.JavaScript的對象和構(gòu)造函數(shù)
定義一個JavaScript對象可以這么定義
var a = {
x : 1,
y : 2,
add : function () {
return this.x + this.y;
},
mul : function () {
return this.x * this.y;
}
}
這樣,你就定義了一個變量a,這個變量除了有x和y兩個公有成員外,還有兩個add和mul兩個函數(shù)(公有方法)。但是這樣的定義方法的缺點(diǎn)有2條:
1.批量生成對象很不方便,如果你var b=a;那么你每次修改b的成員,都會同時改掉a的成員,因為JavaScript的引用機(jī)制
2.如果每次生成對象需要自定義一些成員,都要寫出相應(yīng)的賦值操作,增加代碼行數(shù)。
所以,在定義一個JavaScript對象之前,我們可以先定義一個構(gòu)造函數(shù)。
function A(x, y) {
this.x = x;
this.y = y;
this.add = function () {
return this.x + this.y;
}
this.mul = function () {
return this.x * this.y;
}
}
然后,定義一個對象
a = new A(1, 2);
上面這句代碼看起來簡單,但是要和C++等面向?qū)ο蟮恼Z言做個區(qū)分,A并不是嚴(yán)格意義上“類”的概念,因為JavaScript是沒有類的,只是調(diào)用了構(gòu)造函數(shù)而已。
現(xiàn)在問題來了,我們怎么實現(xiàn)繼承?C++把封裝,繼承,多態(tài)這三個面向?qū)ο蟮奶卣鲗崿F(xiàn)得清清楚楚。但是對于JavaScript這樣一個比較浪的語言,沒有一個很嚴(yán)格的繼承機(jī)制,而是采用以下幾種方式來模擬。
0x2.JavaScript的prototype
為了能夠講清后面的apply或call函數(shù),這里先引入prototype。prototype是只有Function才有的。
要用好繼承,首先要明白為什么要設(shè)計繼承這個東西?無非就是“把公共的部分”提取出來,實現(xiàn)代碼復(fù)用。
所以在JavaScript里,也是把公共部分放在Function的prototype里。
我們來比較兩個用prototype來實現(xiàn)繼承的例子
function A(x, y) {
this.x = x;
this.y = y;
this.add = function () {
return this.x + this.y;
}
this.mul = function () {
return this.x * this.y;
}
}
function B(x,y){
}
B.prototype=new A(1,2);
console.log(new B(3,4).add()); //3
這個例子中,子類的prototype指向一個A類對象
我們再實現(xiàn)一個B繼承A的例子:
function A() {
}
A.prototype = {
x : 1,
y : 2,
add : function () {
return this.x + this.y;
},
mul : function () {
return this.x * this.y;
}
}
A.prototype.constructor=A;
function B(){
}
B.prototype=A.prototype;
B.prototype.constructor=B;
B的prototype對象引用了A的prototype對象,因為是引用,所以如果修改了B的prototype對象,A的prototype對象也隨之修改,因為本質(zhì)上他們都指向一塊內(nèi)存。所以每次改動B類型的prototype都要手動將constructor改回,防止錯亂。相比兩個例子,上一個例子因為沒有引用,所以不會發(fā)生這個問題。
創(chuàng)建一個B類型的對象
b=new B();
b對象具有A類型的一切成員
console.log(b.add()); //3
因為每個prototype對象都有兩個重要成員:constructor和_proto_,constructor本質(zhì)上是一個函數(shù)指針,所以B.prototype=A.prototype執(zhí)行后,覆蓋掉了constructor,所以后面要讓constructor重新指向B類型的構(gòu)造函數(shù)。
0x3.JavaScript的構(gòu)造函數(shù)綁定
在定義完一個A類型的構(gòu)造函數(shù)后,再定義一個B類型,然后在B類型構(gòu)造函數(shù)內(nèi)部,“嵌入執(zhí)行”A類型的構(gòu)造函數(shù)。
function A(x, y) {
this.x = x;
this.y = y;
this.add = function () {
return this.x + this.y;
}
this.mul = function () {
return this.x * this.y;
}
}
function B(x, y, z) {
A.apply(this, arguments);
this.z = z;
}
console.log(new B(1,2,3));
apply函數(shù)和call函數(shù)基本一樣,可以在B類型構(gòu)造函數(shù)內(nèi)部執(zhí)行A類型構(gòu)造函數(shù)。同時也可以繼承A的所有成員。
顯示結(jié)果:

這里給個公式:在B構(gòu)造函數(shù)里寫A.apply(this),可以讓B構(gòu)造出來的對象可以擁有A構(gòu)造函數(shù)里的所有成員。
談到apply和call,還可以實現(xiàn)多重繼承
function IA(){
this.walk=function(){
console.log("walk");
}
}
function IB(){
this.run=function(){
console.log("run");
}
}
function Person(){
IA.apply(this);
IB.apply(this);
}
var p=new Person();
p.walk(); //walk
p.run(); //run
以上就是本文的全部內(nèi)容了,希望大家能夠喜歡
相關(guān)文章
JavaScript String.replace函數(shù)參數(shù)實例說明
JavaScript String.replace函數(shù)作用是將源自符串中的match替換為replacement并返回替換后的字符串,使用介紹如下,不會的朋友可以了解下哈2013-06-06
簡介JavaScript中的getUTCFullYear()方法的使用
這篇文章主要介紹了簡介JavaScript中的getUTCFullYear()方法的使用,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-06-06
Javascript學(xué)習(xí)筆記6 prototype的提出
所以你還會再說是否用prototype都是一樣的么?其實我以前也是這么理解的,在這次偶然的試驗中看到了這個問題。2010-01-01
JavaScript高級程序設(shè)計(第3版)學(xué)習(xí)筆記11 內(nèi)建js對象
內(nèi)建對象是指由ECMAScript實現(xiàn)提供的、不依賴于宿主環(huán)境的對象,這些對象在程序運(yùn)行之前就已經(jīng)存在了2012-10-10

