Prototype是js面向对象中实现多态的核心,或者说是区别于普通面向对象语言如java等的灵魂所在,越是这种玩意,越是坑,因为如果对这些理解不深,反而会被它的一些表面现象所迷惑,导致各种深层bug,不断入坑。
这里对自己之前所做的项目中所用到的此部分内容做一些总结
思考
开篇我想就我之前用JS的一些体会,简单说几个点
- 人们常说,JS中万物皆对象,所以很多时候如果我们用Java等语言的(类-对象)逻辑去套JS反而会让自己无法理解。
这里,我们需要破而后立,先树立一个概念,JS万物皆对象,记住是对象,不是类!即使我们会定义一些function,然后使用new去定义初始化一些对象,但是这里的function仍然是对象,只是刚好能够用来帮助初始化一类对象而已
JS中的对象
通常来说,javascript中的对象就是一个指向prototype的指针和一个自身的属性列表。javascript创建对象时采用了写时复制的理念。
只有 构造器 才具有prototype属性,原型链继承就是创建一个新的指针,指向构造器的prototype属性。
prototype属性之所以特别,是因为javascript时读取属性时的遍历机制决定的。本质上它就是一个普通的指针。
构造器包括以下对象:
- Object
- Function
- Array
- Date
- String
Prototype
Prototype是啥
这里强烈推荐参考[关于JS中的constructor与prototype][],这应该是全网写的最直白的,帮助我们小白看懂其中的逻辑
首先,prototype我的理解是一个对象的属性,根据上文,我们知道只有构造器才有prototype属性。
prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。
看图(本文图均出自[关于JS中的constructor与prototype][]文章,下文不再说明)
Sample Code:
function Person(name)
{
this.name=name;
this.showMe=function() {
alert(this.name);
}
};
var one=new Person('js');
alert(one.prototype)//undefined
alert(typeof Person.prototype);//object
alert(Person.prototype.constructor);//function Person(name) {...};
分析:
其中one是具体的构造器弄出来的对象,所以没有prototype属性。而Function有(Function是构造器),并且prototype中有只想具体构造函数的属性
JS中对象的定义过程
按照《悟透javascript》书中说的,new形式创建对象的过程实际上可以分为三步:
第一步是建立一个新对象(叫A吧);
第二步将该对象(A)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象;
第三步就是将该对象(A)作为this 参数调用构造函数(就是Person),完成成员设置等初始化工作。
其中第二步中出现了一个新名词就是内置的原型对象,注意这个新名词跟prototype对象不是一回事,为了区别我叫它inobj,inobj就指向了函数Person的prototype对象。在person的prototype对象中出现的任何属性或者函数都可以在one对象中直接使用,这个就是javascript中的原型继承了。
这样one对象通过内置的原型对象inobj就可以直接访问Person的prototype对象中的任何属性与方法了。这也就解释了上面的代码中为什么one可以访问form函数了。因为prototype对象中有一个constructor属性,那么one也可以直接访问constructor属性。
JS继承(原型链)
继承的实现很简单,只需要把子类的prototype设置为父类的一个对象即可。注意这里说的可是对象哦!
那么通过prototype属性实现继承的原理是什么呢?还是先看图形说明,然后编写代码进行验证。
Sample Code:
function Person(name) {
this.name=name;
this.showMe=function() {
alert(this.name);
}
};
Person.prototype.from=function() {
alert('I come from prototype.');
}
function SubPerson() {
}
SubPerson.prototype=new Person();
var subOne=new SubPerson();
subOne.from();//I come from prototype.
alert(subOne.constructor);//function Person(name) {...};
alert(SubPerson.prototype.constructor);//function Person(name) {...};
这个所谓的原型链就是从最下方的子对象开始,往自己的原型上溯。找调用的属性或者方法,如果找不到就继续找原型的原型。一直下去。这个比较好理解,和普通的父类回溯是差不多的逻辑。
What's More
至此,prototype的大概应该有所理解了。那么还有几个非常重要的细节,并且是使用最多的特性需要再强调一下,其实已经隐藏在上面的code里了。
- 每一个对象(被构造器创造出来的对象)姑且成为myObj,即new+某个function弄出的对象,可以直接访问constructor属性,这玩意指向的就是构造器
但是同时,这玩意是没有prototype属性的,因为prototype只在构造器里才有。不过我们可以这样找到他的prototype,那就是myObj. constructor. prototype - 函数对象中自身声明的方法和属性与prototype声明的对象有什么差别?
有下面几个差别:
- 自身声明的方法和属性是 静态的, 也就是说你在声明后,试图再去增加新的方法或者修改已有的方法,并不会 对由其已经创建的对象产生影响
- 而prototype可以动态地增加新的方法或者修改已有的方法, 从而是 动态的 ,一旦 父函数对象 声明了相关 的prototype属性,由其创建的对象会 自动继承 这些prototype的属性.
总结下,结合上文开头所述,prototype是指针,指向的是一个prototype对象。而具体的调用在在具体使用的时候,才去根据原型链一个一个去找的。所以在你update了prototype的属性和方法后,所有继承了这个prototype的对象都能动态的自动继承这些新update的玩意。这个才是JS作为动态的面向对象语言最有价值的玩意。
Javascript的方法
JS中有三类方法
a. 类方法
b. 对象方法
c. 原型方法
Sample Code:
function People(name) {
this.name=name;
//对象方法
this.Introduce=function(){
alert("My name is "+this.name);
}
}
//类方法
People.Run=function(){
alert("I can run");
}
//原型方法
People.prototype.IntroduceChinese=function(){
alert("我的名字是"+this.name);
}
参考文章
[JS中的prototype][]
[关于JS中的constructor与prototype][]
[javascript必知必会之prototype][]
[JS中的prototype]:http://www.cnblogs.com/yjf512/archive/2011/06/03/2071914.html
[关于JS中的constructor与prototype]:http://blog.csdn.net/niuyongjie/article/details/4810835
[javascript必知必会之prototype]:http://www.cnblogs.com/mindsbook/archive/2009/09/19/javascriptYouMustKnowPrototype.html