JavaScript高级程序设计 扩展--关于动态原型

但是作者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函数作了初始化的代理,从而完成了视觉的统一。

(0)

相关推荐

  • JavaScript高级程序设计 扩展--关于动态原型

    但是作者Nicholas C. Zakas在[动态原型]方式创建对象的时候没有深究可能会存在的问题和解决方案.而仅仅在继承的时候对[动态原型]的瓶颈作了说明.即在作子类继承的时候,不能通过动态原型的方式来实现. 原文大致如下: 继承机制不能采用动态化的原因是:prototype对象的唯一性.实例代码: 复制代码 代码如下: function A (i) { this.a = i; if (typeof A._init == 'undefined') { A.prototype.func = fu

  • JavaScript高级程序设计(第三版)学习笔记1~5章

    第2章,在html中使用JavaScript Html引入外部js脚本 <script type="text/javascript" src="test.js">两个</script>之间不应放脚本,因为并不会被执行</script> <script>标签有一个defer属性可以延迟脚本执行,但是并不保证会按脚本排列顺序执行 建议:将脚本引入放在<body>标签的所有内容之后,而不放在<head>

  • JavaScript高级程序设计(第3版)学习笔记8 js函数(中)

    6.执行环境和作用域 (1)执行环境(execution context):所有的JavaScript代码都运行在一个执行环境中,当控制权转移至JavaScript的可执行代码时,就进入了一个执行环境.活动的执行环境从逻辑上形成了一个栈,全局执行环境永远是这个栈的栈底元素,栈顶元素就是当前正在运行的执行环境.每一个函数都有自己的执行环境,当执行流进入一个函数时,会将这个函数的执行环境压入栈顶,函数执行完之后再将这个执行环境弹出,控制权返回给之前的执行环境. (2)变量对象(variable ob

  • javascript中类的定义及其方式(《javascript高级程序设计》学习笔记)

    关于javascript中类的继承可以参考阮一峰的Blog<Javascript继承机制的设计思想>,说的很透. 一.在javascript中实例化遇到的问题: 下面用<javascript高级程序设计>中的例子来做说明,假如现在定义了一个car的对象,它是Object类的实例.像下面这样的: 复制代码 代码如下: var oCar=new Object(); oCar.color = "red"; oCar.doors = 4; oCar.mpg = 23;

  • JavaScript高级程序设计(第3版)学习笔记7 js函数(上)

    变量类型 在说函数之前,先来说说变量类型. 1.变量:变量在本质上就是命名的内存空间. 2.变量的数据类型:就是指变量可以存储的值的数据类型,比如Number类型.Boolean类型.Object类型等,在ECMAScript中,变量的数据类型是动态的,可以在运行时改变变量的数据类型. 3.变量类型:是指变量本身的类型,在ECMAScript中,变量类型就只有两种:值类型和引用类型.当变量的数据类型是简单数据类型时,变量类型就是值类型,当变量的数据类型是对象类型时,变量类型就是引用类型.在不引起

  • JavaScript高级程序设计(第3版)学习笔记6 初识js对象

    在房子里面可以放你想放的任意事物--如果你有足够的美学造诣,你甚至可以弄一个房中房试试--当然,为了方便管理,我们会给房子里存放的所有事物都会取上一个不重复的名字,比如医药房间里的各种药品名称.在ECMAScript中,你可以在对象中存放任意你想放的数据,同样,我们需要给存放的数据取一个名字--也就是对象的属性名,再存放各种数据.再看看ECMA-262中对象的定义:无序属性的集合,其属性可以包含简单数据类型值.对象或者函数. 进入对象,我开始有些激动了,说实话,让我想起做这系列学习笔记的最初原因

  • JavaScript高级程序设计(第3版)学习笔记 概述

    在JavaScript面世之初,没有人会想到它会被应用的如此广泛,也远比一般人想象中的要复杂强大的多,在我自己学习的过程中,曾经有过多次震撼,只是常常没有过多久,很多美轮美奂的用法就又模糊起来,希望通过对JavaScript高级程序设计(第3版)的专题学习笔记,能够较为系统的将基础知识梳理一次,也能够将自己平常学习与工作过程中遇到的一些美妙用法记录下来,便于自己再次学习,当然,也希望可以给有需要的朋友们一些力所能及的帮助. 相关术语 先简要说一下和JavaScript相关的一些背景术语,就不详细

  • JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型

    ECMAScript是一种动态类型的语言,构建于5种简单数据类型(Undefined.Null.Boolean.Number.String)和一种复杂数据类型(Object)的基础之上.这篇文章就来复习一下简单数据类型,我会尽量从编程实践的角度来描述,下面代码运行环境为FireFox 14.0.1. 简单数据类型 简单数据类型 取值 Undefined undefined(只有一个值) Null null(只有一个值) Boolean true|false(只有两个值) Number 数值 St

  • javascript高级程序设计(第三版)学习笔记(一) 正则表达式整理

    1.创建正则表达式 第一种方式:注意这里的正则表达式不能使用单引号或者双引号,如下 var pattern1 = /[abc]/i; // 匹配第一个"a"或"b"或"c",不区分大小写 第二种方式:使用RegExp构造函数创建,该构造函数传入两个参数,都是字符串,所以需要特别注意"\"符号的转换,所有元字符(下面有讲元字符)需要双重转义,如下 复制代码 代码如下: var patt1 = new RegExp("[

  • JavaScript高级程序设计(第三版)学习笔记6、7章

    第6章,面向对象的程序设计 对象: 1.数据属性 configurable,表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为true enumerbale,表示能否通过for-in访问属性,默认true ƒwritable,表示能否修改属性值,默认true „value,数据存储位置,默认undefined 修改默认属性特性:Object.defineProperty(),接收三个参数:属性所在对象,属性名,描述符对象,描述符对象属性必

随机推荐