js 原型 _proto_, prototype, contructor的联系

困扰了本人好久的原型,很让人头疼,苦心钻研后,总结如下:

下面理解下:

需要记住几点: 记住!!!记住!!!记住!!!(重要的说3遍)

1. 实例的_proto_属性指向构造函数的原型

2. Function的构造函数是它自己

3._proto_和contructor属性是对象所独有的

4.prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有_proto_和contructor属性

 

现在正式开始!让我们从如下一个简单的例子展开讨论,并配以相关的图帮助理解:

function Foo() {...};

let f1 = new Foo();

以上代码表示创建一个构造函数Foo(),并用new关键字实例化该构造函数得到一个实例化对象f1。虽然是简简单单的两行代码,然而它们背后的关系却是错综复杂的,如下图所示: 

 

 重要的有两点:

一、 创建一个function的过程

Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function prototype 属性中。这个 constructor 保存了指向 function 的一个引用。

在定义一个函数(代码如下所示)时,

 

function F() {

// some code

}

 JavaScript 内部会执行如下几个动作:

 

  1. 为该函数添加一个原形(即 prototype)属性
  2. prototype 对象额外添加一个 constructor 属性,并且该属性保存指向函数F 的一个引用

 

     

这样当我们把函数 F 作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数 F)的 prototype 对象的一个属性proto

 

所以我们在每一个对象实例中就可以访问构造函数的 prototype 所有拥有的全部属性和方法,就好像它们是实例自己的一样。当然该实例也有一个 constructor属性了(从 prototype 那里获得的),每一个对象实例都可以通过 construtor 对象访问它的构造函数

     

 二、new的理解

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

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

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

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

又头晕了,上图吧!

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

 

new操作符具体干了什么呢?其实很简单,就干了三件事情。

 

 var obj  = {};

 

obj.__proto__ = Base.prototype;

 

Base.call(obj);

 

第一行,我们创建了一个空对象obj
第二行,我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
第三行,我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法。

 

代码:

    function Person(name)  

    {  

       this.name=name;  

       this.showMe=function()  

            {  

               alert(this.name);  

            }  

    };  

 

    Person.prototype.from=function()  

    {  

      alert('I come from prototype.');  

    }  

 

    var one=new Person('js');  

 

    one.showMe();//js,这个结果没有什么好奇怪的  

    one.from();//I come from prototype.,这个结果有一点奇怪吧  

    alert(one.constructor);//function Person(name) {...}  

    alert(Person.prototype.constructor);//function Person(name) {...}  

再看看继承是如何实现的

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) {...};

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

 

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

注意:红色的方框就是把子类与父类链接起来的地方。这个就应该是传说中的prototype链了吧。下面有代码进行验证

js代码:

    function Person(name)  

    {  

       this.name=name;  

       this.showMe=function()  

            {  

               alert(this.name);  

            }  

    };  

 

    Person.prototype.from=function()  

    {  

      alert('I come from prototype.');  

    }  

    var father=new Person('js');//为了下面演示使用showMe方法,采用了js参数,实际多采用无参数  

    alert(father.constructor);//查看构造函数,结果是:function Person(name) {...};  

    function SubPer()  

    {  

    }  

    SubPer.prototype=father;//注意这里  

    SubPer.prototype.constructor=SubPer;  

 

    var son=new SubPer();  

    son.showMe();//js  

    son.from();//I come from prototype.  

    alert(father.constructor);//function SubPer(){...}  

    alert(son.constructor);//function SubPer(){...}  

    alert(SubPer.prototype.constructor);//function SubPer(){...}  

根据上图的prototype链,还有代码的结果,我想应该明白为什么使用prototype能够实现

JS中的继承了吧。



 

到此,上面的图应该很清楚了。再来一张手绘版的,有点不清楚哈

 

posted @ 2019-04-01 10:58  Regina_wisdom  阅读(235)  评论(0编辑  收藏  举报