JavaScript高級(jí)程序設(shè)計(jì) 擴(kuò)展--關(guān)于動(dòng)態(tài)原型
更新時(shí)間:2010年11月09日 09:10:39 作者:
前文是基于《JavaScript高級(jí)程序設(shè)計(jì)》中關(guān)于對(duì)象創(chuàng)建的筆記和總結(jié)。
但是作者Nicholas C. Zakas在【動(dòng)態(tài)原型】方式創(chuàng)建對(duì)象的時(shí)候沒(méi)有深究可能會(huì)存在的問(wèn)題和解決方案。而僅僅在繼承的時(shí)候?qū)Α緞?dòng)態(tài)原型】的瓶頸作了說(shuō)明。即在作子類(lèi)繼承的時(shí)候,不能通過(guò)動(dòng)態(tài)原型的方式來(lái)實(shí)現(xiàn)。
原文大致如下:
繼承機(jī)制不能采用動(dòng)態(tài)化的原因是:prototype對(duì)象的唯一性。實(shí)例代碼:
function A (i) {
this.a = i;
if (typeof A._init == 'undefined') {
A.prototype.func = function () {
return 0;
}
A._init = 1;
}
}
function subA (j) {
A.call(this, 1);
this.j = j;
if (typeof subA._init == 'undefined') {
subA.prototype = new A();
subA.prototype.func_sub = function () {
return ++j;
}
subA._init = 1;
}
}
var sub_a = new subA(1);
alert(sub_a.func_sub()); //error: sub_a.func_sub is not a function
Nicholas解釋說(shuō)在代碼運(yùn)行前,對(duì)象已被實(shí)例,并與prototype聯(lián)系,在當(dāng)前對(duì)prototype對(duì)象替換不會(huì)對(duì)它產(chǎn)生任何影響,即當(dāng)前的替換是訪(fǎng)問(wèn)不到的,只有未來(lái)對(duì)象的實(shí)例才會(huì)反映出這種改變。于是第一個(gè)實(shí)例對(duì)象就會(huì)不正確。但第二個(gè)及以后的子類(lèi)實(shí)例都沒(méi)問(wèn)題。
解決方法就是在構(gòu)造函數(shù)外賦予新的prototype對(duì)象:
function A (i) {
this.a = i;
if (typeof A._init == 'undefined') {
A.prototype.func = function () {
return 0;
}
A._init = 1;
}
}
function subA (j) {
A.call(this, 1);
this.j = j;
if (typeof subA._init == 'undefined') {
subA.prototype.func_sub = function () {
return ++j;
}
subA._init = 1;
}
}
subA.prototype = new A();
var sub_a = new subA(1);
alert(sub_a.func_sub()); //2
可惜這違反了我們?yōu)槭裁词褂脛?dòng)態(tài)原型的初衷。
使用動(dòng)態(tài)原型的初衷本來(lái)就是要讓構(gòu)造函數(shù)能“統(tǒng)一江山”,在視覺(jué)上讓人覺(jué)得原型方法是類(lèi)構(gòu)造的一部分。
以上是《JavaScript高級(jí)程序設(shè)計(jì)》中對(duì)動(dòng)態(tài)原型繼承小節(jié)的大概內(nèi)容。
<! -- ========== 分割線(xiàn) ============ -->
可是Nicholas在先前的章節(jié)講對(duì)象構(gòu)造的【動(dòng)態(tài)原型】方式中,似乎忘了提這個(gè)同樣的問(wèn)題。我們看看上文中最后一個(gè)例子:
var Obj = function (name) {
this.name = name;
this.flag = new Array('A', 'B');
if (typeof Obj._init == 'undefined') {
Obj.prototype = {
showName : function () {
alert(this.name);
}
};
Obj._init = true;
}
}
var obj1 = new Obj('aa');
var obj2 = new Obj('bb');
obj1.showName(); //error: is not a function
obj2.showName(); // bb;
是的,這個(gè)問(wèn)題其實(shí)和子類(lèi)繼承中出現(xiàn)的問(wèn)題如出一轍,prototype在當(dāng)前的替換是不會(huì)對(duì)該對(duì)象有任何影響的,只有在未來(lái)的實(shí)例中可見(jiàn)。如果按照Nicholas處理動(dòng)態(tài)原型繼承的方式中說(shuō)的一樣,那就意味著只能在構(gòu)造函數(shù)外邊重新賦予prototype對(duì)象。那么這不就成了【構(gòu)造函數(shù)/原型混合】方式了嗎?所謂的【動(dòng)態(tài)原型】方式也就不存在了...
其實(shí)我們可以想想,為什么在【構(gòu)造函數(shù)/原型混合】這種已經(jīng)基本沒(méi)有副作用的構(gòu)建對(duì)象方式后面還要在寫(xiě)一節(jié)【動(dòng)態(tài)原型】方式。作者的意圖無(wú)非就是想讓構(gòu)造函數(shù)在視覺(jué)上更為統(tǒng)一么。其實(shí)僅僅要視覺(jué)上的統(tǒng)一可以不用動(dòng)態(tài)原型的。
var Obj = function () {
function __initialize (name) {
this.name = name;
this.flag = new Array('A', 'B');
}
__initialize.prototype = {
showName : function () {
alert(this.name);
},
showFlag : function () {
alert(this.flag);
}
}
return __initialize;
}();
var obj1 = new Obj('aa');
var obj2 = new Obj('bb');
obj1.showName(); // aa
obj2.showName(); // bb
其實(shí)上面的方式就可以算是視覺(jué)的統(tǒng)一了,Obj的構(gòu)造函數(shù)內(nèi)通過(guò)__initialize來(lái)初始化屬性,通過(guò)__initialize.prototype原型初始化方法。只不過(guò)稍微有點(diǎn)“小作弊”的感覺(jué),__initialize代理了Obj的初始化...
下面是來(lái)自tangoboy的“構(gòu)造類(lèi)”的封裝,其實(shí)思路和上面基本一致,唯一不同的是他把屬性也用原型方式創(chuàng)建了,同時(shí)把初始化屬性和方法都扔到了構(gòu)造函數(shù)參數(shù)對(duì)象里。方便自定義:
/* == form tangoboy == */
window['$Class'] = {
//創(chuàng)建一個(gè)類(lèi) 混合構(gòu)造函數(shù)/原型方式
create: function(config) {
var obj = function(){},config = config||{};
//過(guò)濾構(gòu)造方法和原型方法
obj = obj.prototype.constructor = config["__"]||obj;
delete config["__"];
obj.prototype = config;
return obj;
}
}
/* -- eg -- */
var man = $Class.create({
__ : function (name) {
this.name = name;
},
sex : 'male',
showName : function () {
alert(this.name);
}
});
var me = new man('ru');
me.showName(); //ru
其實(shí)如果硬要追求視覺(jué)的統(tǒng)一也可以不用動(dòng)態(tài)原型的方式。說(shuō)到底看看上面的思路,已經(jīng)回溯到了我們最常用的“類(lèi)構(gòu)造”方式:
var Class = {
create : function () {
return function () {
this.initialize.apply(this, arguments);
}
}
}
相信上面這段代碼大家或許都不會(huì)陌生,如果細(xì)究下去,會(huì)發(fā)現(xiàn)其實(shí)和上面的代碼都一致,用initialize函數(shù)作了初始化的代理,從而完成了視覺(jué)的統(tǒng)一。
原文大致如下:
繼承機(jī)制不能采用動(dòng)態(tài)化的原因是:prototype對(duì)象的唯一性。實(shí)例代碼:
復(fù)制代碼 代碼如下:
function A (i) {
this.a = i;
if (typeof A._init == 'undefined') {
A.prototype.func = function () {
return 0;
}
A._init = 1;
}
}
function subA (j) {
A.call(this, 1);
this.j = j;
if (typeof subA._init == 'undefined') {
subA.prototype = new A();
subA.prototype.func_sub = function () {
return ++j;
}
subA._init = 1;
}
}
var sub_a = new subA(1);
alert(sub_a.func_sub()); //error: sub_a.func_sub is not a function
Nicholas解釋說(shuō)在代碼運(yùn)行前,對(duì)象已被實(shí)例,并與prototype聯(lián)系,在當(dāng)前對(duì)prototype對(duì)象替換不會(huì)對(duì)它產(chǎn)生任何影響,即當(dāng)前的替換是訪(fǎng)問(wèn)不到的,只有未來(lái)對(duì)象的實(shí)例才會(huì)反映出這種改變。于是第一個(gè)實(shí)例對(duì)象就會(huì)不正確。但第二個(gè)及以后的子類(lèi)實(shí)例都沒(méi)問(wèn)題。
解決方法就是在構(gòu)造函數(shù)外賦予新的prototype對(duì)象:
復(fù)制代碼 代碼如下:
function A (i) {
this.a = i;
if (typeof A._init == 'undefined') {
A.prototype.func = function () {
return 0;
}
A._init = 1;
}
}
function subA (j) {
A.call(this, 1);
this.j = j;
if (typeof subA._init == 'undefined') {
subA.prototype.func_sub = function () {
return ++j;
}
subA._init = 1;
}
}
subA.prototype = new A();
var sub_a = new subA(1);
alert(sub_a.func_sub()); //2
可惜這違反了我們?yōu)槭裁词褂脛?dòng)態(tài)原型的初衷。
使用動(dòng)態(tài)原型的初衷本來(lái)就是要讓構(gòu)造函數(shù)能“統(tǒng)一江山”,在視覺(jué)上讓人覺(jué)得原型方法是類(lèi)構(gòu)造的一部分。
以上是《JavaScript高級(jí)程序設(shè)計(jì)》中對(duì)動(dòng)態(tài)原型繼承小節(jié)的大概內(nèi)容。
<! -- ========== 分割線(xiàn) ============ -->
可是Nicholas在先前的章節(jié)講對(duì)象構(gòu)造的【動(dòng)態(tài)原型】方式中,似乎忘了提這個(gè)同樣的問(wèn)題。我們看看上文中最后一個(gè)例子:
復(fù)制代碼 代碼如下:
var Obj = function (name) {
this.name = name;
this.flag = new Array('A', 'B');
if (typeof Obj._init == 'undefined') {
Obj.prototype = {
showName : function () {
alert(this.name);
}
};
Obj._init = true;
}
}
var obj1 = new Obj('aa');
var obj2 = new Obj('bb');
obj1.showName(); //error: is not a function
obj2.showName(); // bb;
是的,這個(gè)問(wèn)題其實(shí)和子類(lèi)繼承中出現(xiàn)的問(wèn)題如出一轍,prototype在當(dāng)前的替換是不會(huì)對(duì)該對(duì)象有任何影響的,只有在未來(lái)的實(shí)例中可見(jiàn)。如果按照Nicholas處理動(dòng)態(tài)原型繼承的方式中說(shuō)的一樣,那就意味著只能在構(gòu)造函數(shù)外邊重新賦予prototype對(duì)象。那么這不就成了【構(gòu)造函數(shù)/原型混合】方式了嗎?所謂的【動(dòng)態(tài)原型】方式也就不存在了...
其實(shí)我們可以想想,為什么在【構(gòu)造函數(shù)/原型混合】這種已經(jīng)基本沒(méi)有副作用的構(gòu)建對(duì)象方式后面還要在寫(xiě)一節(jié)【動(dòng)態(tài)原型】方式。作者的意圖無(wú)非就是想讓構(gòu)造函數(shù)在視覺(jué)上更為統(tǒng)一么。其實(shí)僅僅要視覺(jué)上的統(tǒng)一可以不用動(dòng)態(tài)原型的。
復(fù)制代碼 代碼如下:
var Obj = function () {
function __initialize (name) {
this.name = name;
this.flag = new Array('A', 'B');
}
__initialize.prototype = {
showName : function () {
alert(this.name);
},
showFlag : function () {
alert(this.flag);
}
}
return __initialize;
}();
var obj1 = new Obj('aa');
var obj2 = new Obj('bb');
obj1.showName(); // aa
obj2.showName(); // bb
其實(shí)上面的方式就可以算是視覺(jué)的統(tǒng)一了,Obj的構(gòu)造函數(shù)內(nèi)通過(guò)__initialize來(lái)初始化屬性,通過(guò)__initialize.prototype原型初始化方法。只不過(guò)稍微有點(diǎn)“小作弊”的感覺(jué),__initialize代理了Obj的初始化...
下面是來(lái)自tangoboy的“構(gòu)造類(lèi)”的封裝,其實(shí)思路和上面基本一致,唯一不同的是他把屬性也用原型方式創(chuàng)建了,同時(shí)把初始化屬性和方法都扔到了構(gòu)造函數(shù)參數(shù)對(duì)象里。方便自定義:
復(fù)制代碼 代碼如下:
/* == form tangoboy == */
window['$Class'] = {
//創(chuàng)建一個(gè)類(lèi) 混合構(gòu)造函數(shù)/原型方式
create: function(config) {
var obj = function(){},config = config||{};
//過(guò)濾構(gòu)造方法和原型方法
obj = obj.prototype.constructor = config["__"]||obj;
delete config["__"];
obj.prototype = config;
return obj;
}
}
/* -- eg -- */
var man = $Class.create({
__ : function (name) {
this.name = name;
},
sex : 'male',
showName : function () {
alert(this.name);
}
});
var me = new man('ru');
me.showName(); //ru
其實(shí)如果硬要追求視覺(jué)的統(tǒng)一也可以不用動(dòng)態(tài)原型的方式。說(shuō)到底看看上面的思路,已經(jīng)回溯到了我們最常用的“類(lèi)構(gòu)造”方式:
復(fù)制代碼 代碼如下:
var Class = {
create : function () {
return function () {
this.initialize.apply(this, arguments);
}
}
}
相信上面這段代碼大家或許都不會(huì)陌生,如果細(xì)究下去,會(huì)發(fā)現(xiàn)其實(shí)和上面的代碼都一致,用initialize函數(shù)作了初始化的代理,從而完成了視覺(jué)的統(tǒng)一。
相關(guān)文章
BootStrap柵格系統(tǒng)、表單樣式與按鈕樣式源碼解析
這篇文章主要為大家詳細(xì)解析了BootStrap柵格系統(tǒng)、表單樣式與按鈕樣式源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01File, FileReader 和 Ajax 文件上傳實(shí)例分析(php)
File, FileReader 和 Ajax 文件上傳實(shí)例分析(php),需要的朋友可以參考下。2011-04-04動(dòng)態(tài)讀取JSON解析鍵值對(duì)的方法
這篇文章主要介紹了動(dòng)態(tài)讀取JSON解析鍵值對(duì)的方法,需要的朋友可以參考下2014-06-06如何利用JavaScript編寫(xiě)更好的條件語(yǔ)句詳解
這篇文章主要給大家介紹了關(guān)于如何利用JavaScript編寫(xiě)更好的條件語(yǔ)句的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JavaScript具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08layui 根據(jù)后臺(tái)數(shù)據(jù)動(dòng)態(tài)創(chuàng)建下拉框并同時(shí)默認(rèn)選中的實(shí)例
今天小編就為大家分享一篇layui 根據(jù)后臺(tái)數(shù)據(jù)動(dòng)態(tài)創(chuàng)建下拉框并同時(shí)默認(rèn)選中的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09