javascript难点之一:总结prototype
总结一下其实就是一句话,js完完全全的和java不沾边啊不沾边!其可修!!
“Js是面向对象的语言”还真是面向对象面向的彻底啊,js不是class-based的,是object-based的,prototype机制就是基于对象的继承机制。这里注意了,是基于对象的继承不是类的继承,类和对象的区别就不多说了。所以这就导致了js与java在本质上的区别。
言归正传,让我们看看prototype这个属性到底是怎么一回事。在此之前先介绍一个知识点: ECMA 标准基于一系列原创技术,其中最著名的是Netscape 公司的 JavaScript 和Microsoft 公司的JScript。
###################################我是华丽的分割线##############################
ECMA-262中有这样一段描述(想要搞明白必须认真看这一段):ECMAScript 并没有严格意义上的类,在这一点上不同于 C++、Smalltalk 或者 Java,不过作为替代,它支持构造函数(constructors),利用它,可通过执行代码创建对象:给对象 分配 存储,然后通过赋初始值来初始化对象属性的全部或部分。所有的构造函数都是对象,但并非所有的对象都是构造函数。每个构造函数都有一个 Prototype(原 型)属性,被用于实现基于原型继承(prototype-based inheritance)和共享属性(shared properties)。使用构造函数来创建对象要用到 new(新 建)表达句;举个例子,new String("A String") 创建了一个新的字符串对象。不使用 new 调用构造函数的后果取决于这个构造函数本身。举个例子,String("A String") 产生一个原语字符串而非对象。
ECMAScript 支持基于原型继承。每一个构造函数都有一个相关联的原型,它创建的所有对象都拥有一个隐含的引用指向那个与其构造函数相关联的原型(所谓的对象的原型)。更深一步讲,原型还可能会拥有 到它的原型的隐含的非空引用,依此类推;这被称为原型链(prototypechain)。若为某对象中的一个属 性建立引用,那么此引用指向该对象的原型链中,最先拥有同名属性的对象所包含的这个属性。换句话说,首先检查被直接提及的对象是否包含某个属性;如果那 个对象包含同名属性,被引用指向的就是这个属性;如果那个对象并没有包含同名属性,接下来检查它的原型,如此继续下去。
通常,在基于类的面向对象语言中,实例装载状态,类装载方法,且继承的仅仅是结构和行为。而在 ECMAScript 中,状态和方法均由对象装载,且结构、行为、状态都会被继承。所有没有直接包含某个它们的原型所包含的特定属性的对象,与它们的原型共享那个属性和它的值。
原型是一 种对象,被用在 ECMAScript 中实现继承结构、状态和行为。当构造函数创建对象时,那个对象隐含引用构造函数的关联原型,以此分解属性引用。通过程序中的表达式constructor.prototype 可以引用到构造函数的关联原型,通过继承,添加给对象的属性会被所有共享此原型的对象共享。
###################################我是华丽的分割线##############################
以上是照抄的=。=+。读完后应该会明白一点,var b = function(){this.a=1} 只是一个构造函数,从上面的定义来看,类似于JAVA中的类,不过,如果你真的认为他就是类,那么你一定会在学习js的时候感到痛苦万分,应为实际上,它和java的类的用法可以说是天上地下的差别。摆正心态吧,只是一个构造函数而已。b.prototype根据定义,它是一个对象!!注意!是对象不是类也不是构造函数!!这是个隐藏的对象。B.prototype是对象,var c = new b(); 这个c也是一个对象,b的构造函数对c进行了初始化。这时c也有一个c.prototype原型,你可以设置c.p=1,也可以设置c.prototype.p=1,至于区别,后面会讲到。
好,我理解的重点来了!(如有理解错误,请大家指教)。就如定义里所说的一样,prototype是为了实现继承而存在的,毕竟js里没有所谓的extend,有的只是new实例化一个对象而已,但是JS又被称作是面向对象的语言,没有继承怎么面相对象呢?
好,答案出来了,prototype就是为了继承而专门产生的一个东西。=。=+光是这一点就和JAVA相差十万八千里了。其实一搜知道,JS的比较完整的描述是
Javascript是一种由Netscape的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言。“原型化继承”看到了,这里的原型就是prototype。
Prototype怎么实现继承这个功能的呢?看下面的例子
Var a = function(){this.m = 1};
Var b = new a();//普通的实例化
Var c = function(){};
c.prototype = new a();//C的原型对象是a的实例,其实这里就已经完成了c对a的继承,只不过不是extend,而是用了prototype,稍后解释这种继承怎么运作。
Var d = new c ();
alert(d.m);//这里会输出1
好,来解释一下。当要输出d.m的时候,首先看看给d实例化的构造函数是否有m属性,很明显,c的构造函数是空的,什么属性都没写,于是就去c的原型里找,也就是向c所继承的new a()里找,这下可找到了。如果没找到呢?就去a.prototype里找,以此类推。知道了吧,是不是有点觉得又和JAVA的继承类似了呢,只能单继承。这就是所谓的原型链了,也不难理解了吧,自己构造函数不提供,就去“父类”里找,以此类推,就和JAVA里的继承差不多了。理解了吧。但是继承总有个头吧,是的,JS里一切都是对象,所有对象的的初始原型(prototype)就是object.prototype,哇,觉得和JAVA更像了!用a来说,a.prototype继承的是Function.prototype,而Function.prototype继承的是object.prototype,原型就是实例。
但是这里又有疑问了,为什么c.prototype = new a()而不是c.prototype = a.prototype?
new关键字用于产生一个新的实例这个实例的缺省属性中,(至少)会执有构造器函数的原型属性(prototype)的一个引用(在ECMA Javascript 规范中,对象的这个属性名定义为__proto__)。 每一个函数,无论它是否用作构造器,都会有一个独一无二的原型对象(prototype)。 每个对象都会有一个内部的属性_proto_(不同的javascript虚拟机实现用的名字可能不同),这个属性对js开发人员不可见,只在虚拟机 内部使用。每当创建一个对象的时候,这个对象的_proto_就会被赋值为这个对象的构造函数的prototype,这样对象的_proto_属性和构造 函数的prototype引用相同的对象,并且一旦对象创建完成,_proto_属性就不会改变。 这样通过对象的_proto_属性,以及_proto_所引用的对象的_proto_属性,就构成了一个_proto_链。 当访问一个对象的属性和方法的时候,js虚拟机正是通过这个_proto_链来查找的。
上面这段是在网上找的=。=+其实c.prototype = a.prototype这样写是可以的,但是呢,这样的话,只能得到a.prototype这个实例里面的属性,而a的构造函数里的属性m取不到,alert(d.m)就显示未定义。
写了这么多,粘了这么多,理解的差不多了吧。还不理解也正常,这几乎是JS里最难的东西了=。=+。