搜了两 个认为好的讲解 Prototype 属性的文章 , 希望会对大伙有一点帮助 .
我们知道 JScript 中对象的 prototype 属性 , 是用来 返回对象类型原型的引用 的。我们使用 prototype 属性提供对象的类的一组基本功能。并且对象的新实例会 " 继承 " 赋予该对象原型的操作。但是这个 prototype 到底是怎么实现和被管理的呢?
对于对象的 prototype 属 性的说明, JScript 手册 上如是说:所有 JScript 内部 对象都有只读的 prototype 属 性。 可以向其原型中动态添加功能 ( 属性和方法 ) ,但该对象不能被赋予不同的原型。 然 而,用户定义的对象可以被赋给新的原型。
下面我们看三个经典的 prototype 属 性的使用示例。
1 、为脚本环境内建对象添加方法:
程序代码
Array .prototype.max = function()
{
var i, max = this [0];
for (i = 1; i < this.length; i++)
{
if (max < this[i])
max = this[i];
}
return max;
};
2 、为用户自定义类 添加方法 :
程序代码
function TestObject(name)
{
this.m_Name = name;
}
TestObject.prototype.ShowName = function()
{
alert(this.m_Name);
};
3 、更新自定义类的 prototype :
程序代码
function TestObjectA()
{
this.MethodA = function()
{
alert(' TestObjectA.MethodA() '); // 显示这一段文字而已
}
}
function TestObjectB()
{
this.MethodB = function()
{
alert('TestObjectB.MethodB()');
}
}
TestObjectB.prototype = new TestObjectA();
第三个很眼熟吧?对啊,它就是我们前面介绍的原型继承法呀 ~~ 不过今天我们不是研究 " 继承 " ,之所以可以这样来实现一种继承,只是利用了 prototype 属性的一个副作用而已。
prototype 还 有一个默认的属性: constructor , 是用来表示创建对象的函数的 ( 即我 们 OOP 里说 的构造函数 ) 。 constructor 属性是所有具有 prototype 属性的对象的成员。它们包括除 Global 和 Math 对象以外的所有 JScript 内部对象。 constructor 属性保存了对构造特定对象实例的函数的引用。
弄清楚了 JScript 中 prototype 属性怎么使用后,下面我们再来深入的研究它。
上面的文章中我罗列了一下 prototype 属 性在 JScript 中的 各种用法,但是 prototype 这个 东西却不是 JScript 创造 出来的, JScript 实际 上是使用了我们设计模式中 prototype pattern 的 一种衍生形式。下面我先简单的说一下 prototype pattern , 然后再来看到底 JScript 中的 prototype 是怎么回事 ?!
What's prototype pattern?
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节, 工作原理是 :通过将一个原型对象传给那个 要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
继续了解到底什么是 prototype pattern ,可以参看 ' 设 计模式之 Prototype( 原型 )' 这篇文章,即使不懂 Java 也没有关系,把它的代码都当 C# 看就行了。
搞清楚什么是原型了吧?反正记着一点, prototype pattern 是的实现是依赖于 clone 这个操作的,当然要 shallow copy 还是 deep copy 的 clone 看自己的需要了。
下面我们继续说 JScript 里 的 prototype ,为 什么我们说它和 prototype pattern 里的 prototype 不一样呢 ?! 这个不是我说就说出来的,也不是我吹出来的,看看这个示例,你就能大概糊 涂:
程序代码
<script language="javascript">
function RP()
{
RP.PropertyA = 1; // 相当于静态
RP.MethodA = function() // 相当于静态
{
alert("RP.MethodA ");
};
this.PropertyA = 100;
this.MethodA = function()
{
alert("this.MethodA");
};
}
RP.prototype.PropertyA = 10;
RP.prototype.MethodA = function()
{
alert("RP.prototype.MethodA");
};
</script>
不要着急,还没有开始做示例,只是给出了我们用来演示的一个类。 RP 是什么? rpwt 吗?当然不是了, RP 是 ResearchPrototype 了。 好了不废话了,看示例及结果分析。
程序代码
<script language="javascript">
rp = new RP();
alert(RP.PropertyA);
RP.MethodA();
alert(rp.PropertyA);
rp.MethodA();
</script>
运行结果闪亮登场:
1
RP.MethodA
100
this.MethodA
这个 %$@#^$%&^... , 不要着急,继续看哦!
程序代码
<script language="javascript">
rp = new RP();
delete RP.PropertyA; // 竟然还有删除属性
alert(RP.PropertyA);
delete RP.MethodA;
RP.MethodA();
delete rp.PropertyA;
alert(rp.PropertyA);
delete rp.MethodA;
rp.MethodA();
</script>
运行结果再次登场:
程序代码
undefined
A Runtime Error has occurred.
Do you wish to Debug?
Line: 32
Error: Object doesn't support this property or method
10
RP.prototype.MethodA
好玩吧,看出来什么名堂了吗? 这里的 RP.PropertyA 和 RP.MethodA 只是用来做参照的,可是怎么把 this.PropertyA 和 this.MethodA 都 delete 了,还能出来结果 ,而且还是 prototype 导入的属性和方法呢?
这就是 JScript 的 prototype 和 prototype pattern 中 prototype 最大的不同了, JScript 中的这个所谓的 prototype 属性其实是个语言本身支持的特性,这里没有发生任何的 copy ,不管 shallow 还是 deep 的。 对于 JScript 的解释引擎,它在处理 "." 或 "[keyName]" 引用的对象的属性和方法时,先在对象本身的实例 (this) 中查找,如果找到就返回或执行。如果没有查找到,就查找对象的 prototype(this.constructor.prototype) 里是否定义了被查找的对象和 方法,如果找到就返回或执行,如果没有查找到,就返回 undefined( 对于属性 ) 或 runtime error( 对于方法 ) 。
正因为 prototype 导 入类实例的属性或方法是动态查找的,所以我们才能对系统内部对象添加 prototype 属 性和方法,比如 给 String 对象添加 trim 方法:
程序代码
<script lanuage="javascript">
String.prototype.trim()
{
return this.replace(/(^"s+)|("s+$)/g, "");
}
</scritp>
显然 JScript 中 的这种用法也是 prototype pattern 中的 prototype 不能解释和支持的。
这下对于 JScript OOP 中 原型继承法的理解因该没有任何的障碍了吧?同时也应该明白为什么原型继承法有那么大的天生缺陷了吧?当然如果有任何问题,欢迎继续讨论。
附演示示例源代码:
程序代码
<html>
<head>
<meta name="author" content="birdshome@ 博客园 ">
<title>JScript Prototype Research</title>
</head>
<body>
<script language="javascript">
function RP()
{
RP.PropertyA = 1;
RP.MethodA = function()
{
alert("RP.MethodA ");
};
this.PropertyA = 100;
this.MethodA = function()
{
alert("this.MethodA");
};
}
RP.prototype.PropertyA = 10;
RP.prototype.MethodA = function()
{
alert("RP.prototype.MethodA");
};
</script>
<script language="javascript">
rp = new RP();
delete RP.PropertyA;
alert(RP.PropertyA);
delete RP.MethodA;
RP.MethodA();
delete rp.PropertyA;
alert(rp.PropertyA);
delete rp.MethodA;
rp.MethodA();
</script>
</body>
</html>
本文着重 解析 javascript 类继承机制,让你从底层了解 javascript 是怎样实现 “ 继承 ” 这一概念的。
转载自 jimichan
目前 javascript 的实现继承方式并不是通 过 “extend” 关键字来实现的,而是通过 constructor function 和 prototype 属性来实现继承。首先我们创建一个 animal 类
js 代码
- var animal = function(){ // 这就是 constructor function 了
- this.name = 'pipi';
- this.age = 10;
- this.height = 0;
- }
- // 建立一个动物的实例
- var a1 = new animal ();
构造函数与其他普通函数区别在于, 1. 构造函数里有 this 关键字, 2. 调用构造函数是使用的 new 关键字。通过 new 运算符调用构造函数 animal 后,系统就会返回一个对 象,这个对象就相当于
js 代码
- var a1 = { name:'pipi' ,age:10,height:0 }
- // 或者
- var a1 = new Object();
- a1.name='pipi';
- a1.age = 10;
- a1.height = 0;
等同这样的方式来产生 js 对象。
到这里我们知道如何在 js 中定义一个类了,接下来我们展示如何写 一个 cat
js 代码
- var cat = function(){
- this .play = function(){
- alert('cat play')
- }
- }
- cat .prototype = new animal ();
- //prototype 属性指向一个对象
- var c1 = new cat();
到这里, cat 就继承了 animal 对象,类 cat 的一个实例对象 c1 拥有属性 name,age,height, 和方法 play 了。
那么 prototype 起到了一个什么样的作用呢?
prototype 就 好比一个指针,它指向一个 object ,这个 object 就称为子类对象的原型。当 cat 的对象被创建的时候,由于 cat 的构造函数拥有 prototype 属性,那么 cat 的实例就会间接指向这个原型对象了(说成间接的是因为每个 object 都有一个 constructor 属性指向它的构造函数)。
那么问题来了, “ 当我们修改对象 c1 的 name 属性的时候,会不会修改它 prototype 的 name 属性值呢? ” ,答案是否定的。
接下来详细解析:
1. 访问 name 属性: 首先当我们第一次访问 c1.name 的属性的时候,我们会得到值 “pipi” ,这个和我们预料中的一样。但是计算过程你未必 知道。
它计算的过程是这样 的:第一步:检查 c1 对象中是否有 name 属性 , 找 到的话就返回值,没有就跳到第二步,显然没有找到,因为 cat 的构造函数中没有定义。第二步:当第一步没有找时,去间接访问 prototype 对象所指向的 object ,如果在 prototype 对象中找到的 name 属性的话,就返回找到的属性值。如果还是没有找到 的话,再去递归地寻找 prototype 对象的 prototype 对象(去找它的爷爷),一直到找到 name 属性或者没有 prototype 对象为止。如果到最后还是没有找到 name 属性的话就返回 undefined 。
2. 设定 name 属性:当我们设定 c1 对象的 name 属性时,及调用 c1.name= ' new name'; 这个 过程就简单多了。首先检查是否对象已有该属性,若已存在则修改当前值,若不存在则为该对象新增一个属性并设定当前值。值得一提的是,在设定值的过程中没有 去访问 prototype 属性。
为了加深理解,我们再 看一个 read-write-read 的过程,第一次 read 的时候,由于自己的对象没有 name 属性,那么就会返回的原型对象的 name 属性的值。第二步,写入 name 的值,同样没发现本身对象有 name 属性 , 那么就在本身对象上新建一个 name 属性,然后赋值。第三步,再次读取 name 属性,由于在第二步中已经新建了 name 属性,此时就返回在第二步中设定的值。值得一提的 是,在这三步中没有改变原型对象的值。
好了,到此详细分析了 javascript 对象是如果实现继承的,其实和其他的面向对象语 言不一样的是, javascript 的继承机制是对象的原型继承而不是类型继承。
呵呵,欢迎看完,有不 对的地方欢迎大家讨论!