搜了两 个认为好的讲解 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
的继承机制是对象的原型继承而不是类型继承。
呵呵,欢迎看完,有不
对的地方欢迎大家讨论!