Posted in Javascript onNovember 09, 2010
但是作者Nicholas C. Zakas在【动态原型】方式创建对象的时候没有深究可能会存在的问题和解决方案。而仅仅在继承的时候对【动态原型】的瓶颈作了说明。即在作子类继承的时候,不能通过动态原型的方式来实现。
原文大致如下:
继承机制不能采用动态化的原因是:prototype对象的唯一性。实例代码:
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解释说在代码运行前,对象已被实例,并与prototype联系,在当前对prototype对象替换不会对它产生任何影响,即当前的替换是访问不到的,只有未来对象的实例才会反映出这种改变。于是第一个实例对象就会不正确。但第二个及以后的子类实例都没问题。
解决方法就是在构造函数外赋予新的prototype对象:
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
可惜这违反了我们为什么使用动态原型的初衷。
使用动态原型的初衷本来就是要让构造函数能“统一江山”,在视觉上让人觉得原型方法是类构造的一部分。
以上是《JavaScript高级程序设计》中对动态原型继承小节的大概内容。
<! -- ========== 分割线 ============ -->
可是Nicholas在先前的章节讲对象构造的【动态原型】方式中,似乎忘了提这个同样的问题。我们看看上文中最后一个例子:
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;
是的,这个问题其实和子类继承中出现的问题如出一辙,prototype在当前的替换是不会对该对象有任何影响的,只有在未来的实例中可见。如果按照Nicholas处理动态原型继承的方式中说的一样,那就意味着只能在构造函数外边重新赋予prototype对象。那么这不就成了【构造函数/原型混合】方式了吗?所谓的【动态原型】方式也就不存在了...
其实我们可以想想,为什么在【构造函数/原型混合】这种已经基本没有副作用的构建对象方式后面还要在写一节【动态原型】方式。作者的意图无非就是想让构造函数在视觉上更为统一么。其实仅仅要视觉上的统一可以不用动态原型的。
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
其实上面的方式就可以算是视觉的统一了,Obj的构造函数内通过__initialize来初始化属性,通过__initialize.prototype原型初始化方法。只不过稍微有点“小作弊”的感觉,__initialize代理了Obj的初始化...
下面是来自tangoboy的“构造类”的封装,其实思路和上面基本一致,唯一不同的是他把属性也用原型方式创建了,同时把初始化属性和方法都扔到了构造函数参数对象里。方便自定义:
/* == form tangoboy == */ window['$Class'] = { //创建一个类 混合构造函数/原型方式 create: function(config) { var obj = function(){},config = config||{}; //过滤构造方法和原型方法 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
其实如果硬要追求视觉的统一也可以不用动态原型的方式。说到底看看上面的思路,已经回溯到了我们最常用的“类构造”方式:
var Class = { create : function () { return function () { this.initialize.apply(this, arguments); } } }
相信上面这段代码大家或许都不会陌生,如果细究下去,会发现其实和上面的代码都一致,用initialize函数作了初始化的代理,从而完成了视觉的统一。
JavaScript高级程序设计 扩展--关于动态原型
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@