【原】javascript prototype 解析

来源:互联网 发布:淘宝哪个玩具店好 编辑:程序博客网 时间:2024/05/01 17:26

2010年03月12日 星期五 19:03

搬离写了5年的渣度空间,准备把技术性的文章定在CSDN了↖(^ω^)↗。这些都是文章备

份。勿怪。。

随着ajax的愈演愈烈,众多程序员们也开始注意起这个曾经不起眼的另类语言了。js有很多跟传统oop不同的特性,比如原型继承,是面向对象函数式语言,还有众多语言都有的闭包。我总结了下prototype的特性。


prototype,翻译为“原型”,这很符合它的特点。首先讲讲js(最普遍,其实总共有4种)继承方法如下:

function a(x)

{...}

funtion b(x)

{.....}

b.prototype=new a();

现在的js中并没有class关键字,暂时只是作为保留字,构建类的方法是使用function,而继承也不是用extends,而是使用将a的一个实例赋给b类的prototype。首先,你先要理解,将b的原型赋a的一个对象,可以实现js的继承关系。现在让我们来看个例子:
<script>
<!--
function dw(s)
{
document.write(s + "<br/>");
}

//爷爷类,赋予默认身高160↓
function Grandfa()
{
this.height = 160;
}
//父亲类↓
function Father()
{

}
Father.prototype=new Grandfa()

function Son(x)
{
if(x) this.height = x;

}
Son.prototype=new Father()


//第一个人不赋予↓
var person1 = new Son;
//第2个人有赋予身高↓
var person2 = new Son(180);
dw(person1.height);//将输出“爷爷”的身高“160”
dw(person2.height);//将输出自己的身高“180”

dw(person1 instanceof Grandfa)//全为true .instanceof的作用是判别一个对象是否是某个类的实例
dw(person1 instanceof Father)
dw(person1 instanceof Son)

</script>
我们可以看到,它很好的符合了继承的一些基本概念,即继承父类和祖先的属性,并且可以掩盖相同属性(注意是掩盖而不是覆盖,下面会说到。方法一样可以被掩盖)

prototype到底是如何实现js继承的呢?首先,任何一个类A,都会自动有一个默认的prototype属性,它暂时是个空对象(Object),当被赋值时,所有这个类A的对象,都会获得相应的值(想象一下原型的意思)并且这个值是共享的。例如:

function Test(val){

if( !val ) { this.x = val } //实例化时如果val非空就赋值给this.x

}

Test.prototype.x=1

t1=new Test()

t2=new Test()

t3=new Test(3)

alert(t2.x);alert(t2.x) //output:1 1

alert(t3.x) //output 3

注意:例子中this.x和Test.prototype.x并不一样。上面的例子中,只有当实例化Test时this.x将被赋值给对象实例,就如实例t3。并且会“暂时掩盖”住t3中原有的原型值。this.x的值在内存中,每个实例保存一份x值。而Test.prototype.x只有一处。

如果我们把t3.x销毁的话:

delete(t3.x) //将t3.x值删除

dw(t3.x) //输出1!原型的值没有变~

理解上了上面再来说说prototype究竟是如何实现继承的:

结合第二个爷爷,父亲,孙子的例子:

首先:一个对象的属性查找首先是先找自身内存区域的this.height,如果找不到,将会在prototype里进行按继承关系从下往上的递归查找

例如:person1是孙子实例。当查找person1.height时,首先查找的是自身的this.height。因为初始化时并没有赋值,所以自身的this.height为“无”接下来将查找的是person1的原型中对象的this.height(注意,其实person1并没有prototype这个属性,这个属性是类属性,而不是对象属性,对象无法直接访问它自己的类属性。将会在以后的文章里说说)。person1.prototype 实际上是father的一个实例。但是father实例也没有this.height 。编译器将递归的查找father对象的原型(grandfa的一个实例),终于,在这个实例中找到了this.height(160)于是返回。

最后,下面是一个比较常用的js类构建和继承的方法↓:

function MyClass(x,y)

{

this.val1=x

this.val2=y

}

MyClass.prototype.method1=function () { return this.val1 + this.val2 }

//↑上面是定义一个类的属性和方法的例子。使用this.val1定义属性,而使用MyClass.prototype.method1来定义一个method1()。原因是this.val1将在每个实例中保存一份。而方法methond1将成为一个原型而所有实例公用(没有人会把方法都单独保存在每个实例中吧)

//继承:↓

MyClass2.prototype=new MyClass();

当然如果你希望提前为MyClass类的对象属性给初始值,

也可以是:

MyClass.prototype.val1=123;

但必须牢记:一旦改变prototype.val1的值,所有对象的值都会改变。但不会影响已经如this.val1=xxx这样赋值的对象的值。因为这样会“掩盖”属性,将val1的值保存进自己的内存空间。

比如:


总结 prototype的作用:继承,对象变量共享,初始化,类方法定义。

下面一篇讲讨论constructor,prototype和类方法,对象方法和动态绑定。