Js中Prototype、__proto__、Constructor、Object、Function关系介绍

时间:2022-02-10 18:20:33
Function、Object、Prototype、__proto__内存关系图
Js中Prototype、__proto__、Constructor、Object、Function关系介绍

        上面的内存图跟堆栈结构可以参照文章Javascript_01_理解内存分配
        堆区图说明:
Js中Prototype、__proto__、Constructor、Object、Function关系介绍 
        Function.prototype函数对象图内部表示prototype属性的红色虚框,只是为了说明这个属性不存在。

        通过上图Function、Object、Prototype关系图中,可以得出一下几点:
  1. 所有对象所有对象,包括函数对象的原型链最终都指向了Object.prototype,而Object.prototype.__proto__===null,原型链至此结束。
  2. Animal.prototype是一个普通对象。
  3. Object是一个函数对象,也是Function构造的,Object.prototype是一个普通对象。
  4. Object.prototype.__type__指向null。
  5. Function.prototype是一个函数对象,前面说函数对象都有一个显示的prototype属性,但是Function.prototype却没有prototype属性,即Function.prototype.prototype===undefined,所有Function.prototype函数对象是一个特例,没有prototype属性。
  6. Object虽是Function构造的一个函数对象,但是Object.prototype没有指向Function.prototype,即Object.prototype!==Function.prototype。

二    Prototype跟Constructor关系介绍

         在 JavaScript 中,每个函数对象都有名为“prototype”的属性(上面提到过Function.prototype函数对象是个例外,没有prototype属性),用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数本身。这是一种循环引用(i.e. Animal.prototype.constructor===Animal)。        通过以下例子跟内存效果图来分析Prototype、constructor间的关系。    console.log('**************constructor****************'); 

    console.log('anim.constructor===Animal:'+(anim.constructor===Animal))    ;    //true
    console.log('Animal===Animal.prototype.constructor:'+(Animal===Animal.prototype.constructor))    ;    //true
    console.log('Animal.constructor===Function.prototype.constructor:'+(Animal.constructor===Function.prototype.constructor));   //true
    console.log('Function.prototype.constructor===Function:'+(Function.prototype.constructor===Function));    //true
    console.log('Function.constructor===Function.prototype.constructor:'+(Function.constructor===Function.prototype.constructor));    //true

    console.log('Object.prototype.constructor===Object:'+(Object.prototype.constructor===Object));    //true
    console.log('Object.constructor====Function:'+(Object.constructor===Function));    //true

 prototype、constructor内存关系图(在Function、Object、Prototype关系图上加入constructor元素):
Js中Prototype、__proto__、Constructor、Object、Function关系介绍

        上图中,红色箭头表示函数对象的原型的constructor所指向的对象。
  1. 注意Object.constructor===Function;本身Object就是Function函数构造出来的        
  2. 如何查找一个对象的constructor,就是在该对象的原型链上寻找碰到的第一个constructor属性所指向的对象。
参考:http://www.cnblogs.com/fool/category/264215.html (javascript原理介绍)http://www.libuchao.com/2012/05/14/prototypes-in-javascript/ (JavaScript 的原型对象 Prototype)http://rockyuse.iteye.com/blog/1426510 (理解js中的原型链,prototype与__proto__的关系)

我们接着看代码:

[javascript] view plain copy
  1. function Person(name)  
  2. {  
  3.    this.name=name;  
  4.    this.showMe=function()  
  5.         {  
  6.            alert(this.name);  
  7.         }  
  8. };  
  9.   
  10. Person.prototype.from=function()  
  11. {  
  12.   alert('I come from prototype.');  
  13. }  
  14.   
  15. var one=new Person('js');  
  16.   
  17. one.showMe();//js,这个结果没有什么好奇怪的  
  18. one.from();//I come from prototype.,这个结果有一点奇怪吧  

 

要解释这个结果就要仔细研究一下new这个操作符了.var one=new Person('js');这个语句执行的过程可以分成下面的语句:

[javascript] view plain copy
  1. var one={};  
  2. Person.call(one,'js');  

按照《悟透javascript》书中说的,new形式创建对象的过程实际上可以分为三步:
第一步是建立一个新对象(叫A吧);

第二步将该对象(A)内置的原型对象设置为构造函数(就是Person)prototype 属性引用的那个原型对象

第三步就是将该对象(A)作为this 参数调用构造函数(就是Person),完成成员设置等初始化工作。

其中第二步中出现了一个新名词就是内置的原型对象,注意这个新名词跟prototype对象不是一回事,为了区别我叫它inobj,inobj就指向了函数Person的prototype对象。在person的prototype对象中出现的任何属性或者函数都可以在one对象中直接使用,这个就是javascript中的原型继承了。

又头晕了,上图吧!inobj就是__proto__ 即,one.__proto__ == Person.prototype

Js中Prototype、__proto__、Constructor、Object、Function关系介绍

这样one对象通过内置的原型对象inobj就可以直接访问Person的prototype对象中的任何属性与方法了。这也就解释了上面的代码中为什么one可以访问form函数了。因为prototype对象中有一个constructor属性,那么one也可以直接访问constructor属性。

代码:

 

 

 

 

 

 

[javascript] view plain copy
  1. function Person(name)  
  2. {  
  3.    this.name=name;  
  4.    this.showMe=function()  
  5.         {  
  6.            alert(this.name);  
  7.         }  
  8. };  
  9.   
  10. Person.prototype.from=function()  
  11. {  
  12.   alert('I come from prototype.');  
  13. }  
  14.   
  15. var one=new Person('js');  
  16.   
  17. one.showMe();//js,这个结果没有什么好奇怪的  
  18. one.from();//I come from prototype.,这个结果有一点奇怪吧  
  19. alert(one.constructor);//function Person(name) {...}  
  20. alert(Person.prototype.constructor);//function Person(name) {...}  

 接着看继承是如何实现的。

[javascript] view plain copy
  1. function Person(name)  
  2. {  
  3.    this.name=name;  
  4.    this.showMe=function()  
  5.         {  
  6.            alert(this.name);  
  7.         }  
  8. };  
  9.   
  10. Person.prototype.from=function()  
  11. {  
  12.   alert('I come from prototype.');  
  13. }  
  14.   
  15. function SubPerson()  
  16. {  
  17. }  
  18. SubPerson.prototype=new Person();  
  19.   
  20. var subOne=new SubPerson();  
  21. subOne.from();//I come from prototype.  
  22. alert(subOne.constructor);//function Person(name) {...};  
  23. alert(SubPerson.prototype.constructor);//function Person(name) {...};  

继承的实现很简单,只需要把子类的prototype设置为父类的一个对象即可。注意这里说的可是对象哦!

 那么通过prototype属性实现继承的原理是什么呢?还是先看图形说明,然后编写代码进行验证。

Js中Prototype、__proto__、Constructor、Object、Function关系介绍

 

 

  用图像直观表示如下:

Js中Prototype、__proto__、Constructor、Object、Function关系介绍
                                                                          图 1.3

到此说明了a,b两点是正确的。

同事上面的也可以论证一下观点(该观点摘自JavaScript权威指南):

1)  同一个类的所有实例都从同一个原型对象上继承属性

2)  当且仅当两个对象继承自同一个原型对象时,他们才是属于同一个类的实例

特别说明:只有构造函数才有prototype属性,而构造函数的实例是没有该属性的,也就是说console.log(a1.prototype)输出的是undefined。在javascript中,每个函数都自动有一个prototype属性,而不是每一个对象拥有prototype属性

  原型属性与实例对象的创建与否没有关系,它在对象创建之前就已经存在


  直观表示如下图:

   Js中Prototype、__proto__、Constructor、Object、Function关系介绍

                                                                      图 1.5


注意:新定义Prototype对象的话,该Prototype对象原有的constructor属性会丢失,该Prototype此时所指的对象(即{getName:function(){return this.name}}对象)的constructor就是Object,事实上{getName:function()}就是一个Object对象。相信如果A.prototype={getName:function(){returnthis.name}}换成这样写的话,理解起来会方便写:

Var obj = {getName:function(){}}

A.prototype = obj;

//或者这样写:

Var obj = new Object();

Obj.getName = function(){

};

A.prototype = obj;

此时用图形表示a1,a2,prototype对象的关系如下:


Js中Prototype、__proto__、Constructor、Object、Function关系介绍

通过这个图可以很直观的看出:

a1.constructor ===  A//=>false

a1.constructor === Object//=>true

A.prototype.constructor === Object

.修改这个问题的办法就行给重定义的Prototype对象显式的添加一个constructor属性,修改其constructor的指向:

 

/**
* Created by duqiong on 17/3/13.
*/
function A(name){
this.name = name;
this.show=function () {//只是实例方法,不实例化无法调用
console.log('inner-'+this.name+' inner show');
// A.show();
}
}
A.shows=function () {
console.log('static-'+this.name+' static show');
}
var a2 = new A("aa2");
A.prototype.show1=function () {//只是在A的原型对象(实例)增加实例方法,不需要实例化不能调用
console.log('prototype show');
}
A.alarms=function () {
console.log('sdfsdfs');
this.alarm=function () {//只是修改了类方法,类方法不需要实例化
console.log('inner dynamic alarm');
}
//this.alarm();
}
var a1 = new A("aa1");
// console.log(a1);
console.log(a2);
console.log(a1.constructor);
console.log(a2.constructor);
console.log(a2.constructor.constructor);
console.log(A.prototype);
console.log(a1.__proto__);
console.log(A.constructor);
console.log(a1.prototype);
//
A.shows();
a1.__proto__.constructor.shows();//通过实例调用类静态方法
a1.constructor.shows();//通过实例调用类静态方法
a1.show();

A.alarms();
A.alarms();
console.log(a1.__proto__===A.prototype);console.log(a1.constructor===A);console.log(A.prototype);a1.show1();
////print//////
A { name: 'aa2', show: [Function] }
{ [Function: A] shows: [Function], alarms: [Function] }
{ [Function: A] shows: [Function], alarms: [Function] }
[Function: Function]
A { show1: [Function] }
A { show1: [Function] }
[Function: Function]
undefined
static-A    static show
static-A    static show
static-A    static show
inner-aa1   inner show
sdfsdfs
inner dynamic alarm
true
true
A { show1: [Function] }
prototype show