四虎精品视频-四虎精品成人免费网站-四虎黄色网-四虎国产视频-国产免费91-国产蜜臀97一区二区三区

JavaScript 繼承詳解(三)

注:本章中的jClass的實(shí)現(xiàn)參考了Simple JavaScript Inheritance的做法。

首先讓我們來(lái)回顧一下第一章中介紹的例子:

 function Person(name) {
this.name = name;
}
Person.prototype = {
getName: function() {
return this.name;
}
}

function Employee(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
}
Employee.prototype = new Person();
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};
var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"

 

修正constructor的指向錯(cuò)誤

 

從上一篇文章中關(guān)于constructor的描述,我們知道Employee實(shí)例的constructor會(huì)有一個(gè)指向錯(cuò)誤,如下所示:

 var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.constructor === Employee); // false
console.log(zhang.constructor === Object); // true
我們需要簡(jiǎn)單的修正:
 function Employee(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
}
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};
var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.constructor === Employee); // true
console.log(zhang.constructor === Object); // false

 

創(chuàng)建Employee類時(shí)實(shí)例化Person是不合適的

 

但另一方面,我們又必須依賴于這種機(jī)制來(lái)實(shí)現(xiàn)繼承。 解決辦法是不在構(gòu)造函數(shù)中初始化數(shù)據(jù),而是提供一個(gè)原型方法(比如init)來(lái)初始化數(shù)據(jù)。

 // 空的構(gòu)造函數(shù)
function Person() {
}
Person.prototype = {
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
}
// 空的構(gòu)造函數(shù)
function Employee() {
}
// 創(chuàng)建類的階段不會(huì)初始化父類的數(shù)據(jù),因?yàn)镻erson是一個(gè)空的構(gòu)造函數(shù)
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.prototype.init = function(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
};
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};
這種方式下,必須在實(shí)例化一個(gè)對(duì)象后手工調(diào)用init函數(shù),如下:
 var zhang = new Employee();
zhang.init("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"

 

如何自動(dòng)調(diào)用init函數(shù)?

 

必須達(dá)到兩個(gè)效果,構(gòu)造類時(shí)不要調(diào)用init函數(shù)和實(shí)例化對(duì)象時(shí)自動(dòng)調(diào)用init函數(shù)。看來(lái)我們需要在調(diào)用空的構(gòu)造函數(shù)時(shí)有一個(gè)狀態(tài)標(biāo)示。

 // 創(chuàng)建一個(gè)全局的狀態(tài)標(biāo)示 - 當(dāng)前是否處于類的構(gòu)造階段
var initializing = false;
function Person() {
if (!initializing) {
this.init.apply(this, arguments);
}
}
Person.prototype = {
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
}
function Employee() {
if (!initializing) {
this.init.apply(this, arguments);
}
}
// 標(biāo)示當(dāng)前進(jìn)入類的創(chuàng)建階段,不會(huì)調(diào)用init函數(shù)
initializing = true;
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
initializing = false;
Employee.prototype.init = function(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
};
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};

// 初始化類實(shí)例時(shí),自動(dòng)調(diào)用類的原型函數(shù)init,并向init中傳遞參數(shù)
var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"
但是這樣就必須引入全局變量,這是一個(gè)不好的信號(hào)。

 

如何避免引入全局變量initializing?

 

我們需要引入一個(gè)全局的函數(shù)來(lái)簡(jiǎn)化類的創(chuàng)建過(guò)程,同時(shí)封裝內(nèi)部細(xì)節(jié)避免引入全局變量。

 // 當(dāng)前是否處于創(chuàng)建類的階段
var initializing = false;
function jClass(baseClass, prop) {
// 只接受一個(gè)參數(shù)的情況 - jClass(prop)
if (typeof (baseClass) === "object") {
prop = baseClass;
baseClass = null;
}
// 本次調(diào)用所創(chuàng)建的類(構(gòu)造函數(shù))
function F() {
// 如果當(dāng)前處于實(shí)例化類的階段,則調(diào)用init原型函數(shù)
if (!initializing) {
this.init.apply(this, arguments);
}
}
// 如果此類需要從其它類擴(kuò)展
if (baseClass) {
initializing = true;
F.prototype = new baseClass();
F.prototype.constructor = F;
initializing = false;
}
// 覆蓋父類的同名函數(shù)
for (var name in prop) {
if (prop.hasOwnProperty(name)) {
F.prototype[name] = prop[name];
}
}
return F;
};
使用jClass函數(shù)來(lái)創(chuàng)建類和繼承類的方法:
 var Person = jClass({
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
},
getEmployeeID: function() {
return this.employeeID;
}
});

var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"
OK,現(xiàn)在創(chuàng)建類和實(shí)例化類的方式看起來(lái)優(yōu)雅多了。 但是這里面還存在明顯的瑕疵,Employee的初始化函數(shù)init無(wú)法調(diào)用父類的同名方法。

 

如何調(diào)用父類的同名方法?

 

我們可以通過(guò)為實(shí)例化對(duì)象提供一個(gè)base的屬性,來(lái)指向父類(構(gòu)造函數(shù))的原型,如下:

 // 當(dāng)前是否處于創(chuàng)建類的階段
var initializing = false;
function jClass(baseClass, prop) {
// 只接受一個(gè)參數(shù)的情況 - jClass(prop)
if (typeof (baseClass) === "object") {
prop = baseClass;
baseClass = null;
}
// 本次調(diào)用所創(chuàng)建的類(構(gòu)造函數(shù))
function F() {
// 如果當(dāng)前處于實(shí)例化類的階段,則調(diào)用init原型函數(shù)
if (!initializing) {
// 如果父類存在,則實(shí)例對(duì)象的base指向父類的原型
// 這就提供了在實(shí)例對(duì)象中調(diào)用父類方法的途徑
if (baseClass) {
this.base = baseClass.prototype;
}
this.init.apply(this, arguments);
}
}
// 如果此類需要從其它類擴(kuò)展
if (baseClass) {
initializing = true;
F.prototype = new baseClass();
F.prototype.constructor = F;
initializing = false;
}
// 覆蓋父類的同名函數(shù)
for (var name in prop) {
if (prop.hasOwnProperty(name)) {
F.prototype[name] = prop[name];
}
}
return F;
};
調(diào)用方式:
 var Person = jClass({
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
// 調(diào)用父類的原型函數(shù)init,注意使用apply函數(shù)修改init的this指向
this.base.init.apply(this, [name]);
this.employeeID = employeeID;
},
getEmployeeID: function() {
return this.employeeID;
},
getName: function() {
// 調(diào)用父類的原型函數(shù)getName
return "Employee name: " + this.base.getName.apply(this);
}
});

var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "Employee name: ZhangSan"

 

目前為止,我們已經(jīng)修正了在第一章手工實(shí)現(xiàn)繼承的種種弊端。 通過(guò)我們自定義的jClass函數(shù)來(lái)創(chuàng)建類和子類,通過(guò)原型方法init初始化數(shù)據(jù), 通過(guò)實(shí)例屬性base來(lái)調(diào)用父類的原型函數(shù)。

唯一的缺憾是調(diào)用父類的代碼太長(zhǎng),并且不好理解, 如果能夠按照如下的方式調(diào)用豈不是更妙:

 var Employee = jClass(Person, {
init: function(name, employeeID) {
// 如果能夠這樣調(diào)用,就再好不過(guò)了
this.base(name);
this.employeeID = employeeID;
}
});

 

優(yōu)化jClass函數(shù)

 

 // 當(dāng)前是否處于創(chuàng)建類的階段
var initializing = false;
function jClass(baseClass, prop) {
// 只接受一個(gè)參數(shù)的情況 - jClass(prop)
if (typeof (baseClass) === "object") {
prop = baseClass;
baseClass = null;
}
// 本次調(diào)用所創(chuàng)建的類(構(gòu)造函數(shù))
function F() {
// 如果當(dāng)前處于實(shí)例化類的階段,則調(diào)用init原型函數(shù)
if (!initializing) {
// 如果父類存在,則實(shí)例對(duì)象的baseprototype指向父類的原型
// 這就提供了在實(shí)例對(duì)象中調(diào)用父類方法的途徑
if (baseClass) {
this.baseprototype = baseClass.prototype;
}
this.init.apply(this, arguments);
}
}
// 如果此類需要從其它類擴(kuò)展
if (baseClass) {
initializing = true;
F.prototype = new baseClass();
F.prototype.constructor = F;
initializing = false;
}
// 覆蓋父類的同名函數(shù)
for (var name in prop) {
if (prop.hasOwnProperty(name)) {
// 如果此類繼承自父類baseClass并且父類原型中存在同名函數(shù)name
if (baseClass &&
typeof (prop[name]) === "function" &&
typeof (F.prototype[name]) === "function") {

// 重定義函數(shù)name -
// 首先在函數(shù)上下文設(shè)置this.base指向父類原型中的同名函數(shù)
// 然后調(diào)用函數(shù)prop[name],返回函數(shù)結(jié)果

// 注意:這里的自執(zhí)行函數(shù)創(chuàng)建了一個(gè)上下文,這個(gè)上下文返回另一個(gè)函數(shù),
// 此函數(shù)中可以應(yīng)用此上下文中的變量,這就是閉包(Closure)。
// 這是JavaScript框架開(kāi)發(fā)中常用的技巧。
F.prototype[name] = (function(name, fn) {
return function() {
this.base = baseClass.prototype[name];
return fn.apply(this, arguments);
};
})(name, prop[name]);

} else {
F.prototype[name] = prop[name];
}
}
}
return F;
};
此時(shí),創(chuàng)建類與子類以及調(diào)用方式都顯得非常優(yōu)雅,請(qǐng)看:
 var Person = jClass({
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
this.base(name);
this.employeeID = employeeID;
},
getEmployeeID: function() {
return this.employeeID;
},
getName: function() {
return "Employee name: " + this.base();
}
});

var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "Employee name: ZhangSan"

 

至此,我們已經(jīng)創(chuàng)建了一個(gè)完善的函數(shù)jClass, 幫助我們?cè)?a href=/itjie/Javajishu/ target=_blank class=infotextkey>JavaScript中以比較優(yōu)雅的方式實(shí)現(xiàn)類和繼承。

在以后的章節(jié)中,我們會(huì)陸續(xù)分析網(wǎng)上一些比較流行的JavaScript類和繼承的實(shí)現(xiàn)。 不過(guò)萬(wàn)變不離其宗,那些實(shí)現(xiàn)也無(wú)非把我們這章中提到的概念顛來(lái)簸去的“炒作”, 為的就是一種更優(yōu)雅的調(diào)用方式。

JavaScript技術(shù)JavaScript 繼承詳解(三),轉(zhuǎn)載需保留來(lái)源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 用力快点| 青娱乐视视频| 女娲怀孕生孩子视频| 兰陵王电影| 超级飞侠17季| 天下第一楼结局| 炊事班的故事演员表| xxxxxxxxxxxxxxxxx69| 刀客家族的女人演员表| 电影儿媳| 关鹏| 傅首尔个人资料| 人口高质量发展形势与政策论文 | 欧美喜剧电影| 《欢·爱》郭晓东| 电视剧暗战在拂晓之前演员表| av线网| 加濑亮| k总直播间| 6套电影频道节目表| 我的新学校英语作文| 冰雪十一天| 赫伯曼电影免费观看| 第一财经在线直播电视| 故乡之恋简谱| 给我| 秀人网 官网门户免费| 烽火流金电视剧免费观看| 以下关于宏病毒说法正确的是| 李泽峰| 张静初的三级未删减版| 海洋天堂电影免费观看高清| 密使所有演员表| 保证书怎么写才有法律效力| 我家来了个怪男人| 茶馆剧本完整版| 金三角电影| 夕雾| 秋天 课文| 公司减资从1000万减到10万| 唐朝艳妃电影|