在JavaScript中,prototype对象是实现面向对象的一个重要机制。每个函数就是一个对象(Function),函数对象都有一个子对象prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合。在通过new创建一个类的实例对象的时候,prototype对象的成员都成为实例化对象的成员。
1、该对象被类所引用,只有函数对象才可引用;
2、在new实例化后,其成员被实例化,实例对象方可调用。
同时,函数是一个对象,函数对象若直接声明成员,不用被实例化即可调用。
第一。先看如下函数
- function createPerson(name,sex,birthday) {
- //通过参数传递赋予函数对象值
- this.name = name ;
- this.sex = sex;
- this.birthday = birthday;
- this.sayHi = function(){
- alert(this.name+"is saying hello to everyone.");
- };
- return this;
- }
上面函数通过定义Function类createPerson来创建一个人,并赋予人的姓名,性别,生日和说话的属性,切记,在JS中,这不同于其他的面向对象编程语言的地方,JS是没有方法这一说法。
然后,我们来创建一个人名叫Rose,定义如下
var rose = new createPerson('Rose ','female','1985-01-01');
这样子,我们就可以直接通过rose对象去直接访问rose具有的属性了。如rose.name,rose.sex,对于sayHi,相对特殊,因为 rose的sayHi是一个函数,所以对于sayHi的调用,就像一般的函数调用一样,rose.sayHi();
可能你会觉得,这样子写不是好完美了吗?的确,如果在其他面向对象编程语言中,类似于这样子的写法的确是完美了,但是对于JS却有一个重要的特性,就是原型。要理解原型,还得从分析var rose = new createPerson('Rose ','female','1985-01-01');这代码入手。这段代码执行过程大概如下,
第一步是建立一个新对象-rose
第二步将该对象内置的原型对象设置为构造函数prototype引用的那个原型对象,也就是因为JS每个函数,都具有原型对象如(createPeson.prototype)。这就是说rose对象的原型对象设置为构造函数createPeson.prototype
第三步就是将该对象作为this参数调用构造函数,完成成员设置等初始化工作。
换句话说就是
- functon createPerson(name,sex,birthday) {
- this.name = name ;
- this.sex = sex;
- this.birthday = birthday;
- this.sayHi = function(){
- alert(this.name+"is saying hello to everyone.");
- };
- return this;
- } 被替换成如下的调用形式,
- functon createPerson(name,sex,birthday) {
- rose.name = name ;
- rose.sex = sex;
- rose.birthday = birthday;
- rose.sayHi = function(){
- alert(rose.name+" is saying hello to everyone.");
- };
- return rose;
- }
对象在通过上述三个阶段创建后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。
我们再深入想想,如果我们要创建1000个rose这样子的对象,上述的三个阶段是不是要执行1000次呢?答案是。这样子就太消耗性能,那有什么方法解决呢。就是通过prototype来优化了,优化后的代码如下
------------------------------------------------------------------------
- functon createPerson(name,sex,birthday) {
- this.name = name ;
- this.sex = sex;
- this.birthday = birthday;
- return this;
- }
- createPerson.prototype.sayHi = function(){
- alert(this.name+" is saying hello to everyone.");
- };
------------------------------------------------------------------------
上述代码,在运行的过程中就赋予了createPerson对象具有sayHi属性,而不是在构造函数中再进行初始化设置,无疑是对性能的一个质的提升。
然后通过
var rose = new createPerson('Rose','female','1985-01-01');
var lily = new createPerson('Lily ','female','1985-01-01');
rose.sayHi();
lily.sayHi();
//输出的结果是
Rose is saying hello to everyone.
Lily is saying hello to everyone.
运行的结果正是我们想得到的结果。但这时可能你有疑问了,如果,函数对象和函数原型对象都同时具有sayHi方法,这时结果会怎么样子呢?还是先看如下代码
- function createPerson(name,sex,birthday,canSay) {
- this.name = name ;
- this.sex = sex;
- this.birthday = birthday;
- if(canSay) {
- this.sayHi = function(){
- alert(this.name+"is saying hello to everyone.");
- };
- }
- return this;
- }
- createPerson.prototype.sayHi = function(){
- alert(" Somebody is saying hello to everyone.");
- };
- var rose = new createPerson('Rose','female','1985-01-01',true);
- var lily = new createPerson('哑吧','female','1985-01-01',false);
- rose.sayHi();
- lily.sayHi();
//运行的结果是
Rose is saying hello to everyone.
Somebody is saying hello to everyone.
怎么去理解运行结果呢?原来JS对象,在查找自身属性的时候,会先从对象的内置对象链查找是否存在该属性。因此不难理解 rose.sayHi() 的运行结果:Rose is saying hello to everyone.但是对于lily.sayHi();的运行结果又是怎么回事呢?原来JS对象先从对象的内置对象链查找是否存在该属性,查找到则返回,如果查找不到,再去对象原型链查找,可见lily对象会从createPerson.prototype.sayHi = function(){
alert(" Somebody is saying hello to everyone.");
};原型链中查找返回然后再调用sayHi方法,因而得到的结果就是 Somebody is saying hello to everyone