大白话通俗易懂的讲解javascript原型与原型链(__proto__、prototype、constructor的区别)

  javascript原型和原型链是js中的重点也是难点,理论上来讲应该是属于面向对象编程的基础知识,那么咱们今天为何要来说这个呢?(由于我也忘了,最近看资料才揭开面纱……  哈哈哈)
javascript

  

  好了,直接进入正文。在js的编程世界中,万物皆对象;无论你是数组仍是函数仍是对象,都是属于对象类型;那么这么多对象,如何进行管理呢?js中把对象分为实例对象、函数对象、原型对象三大类;java

  实例对象:编程

    经过构造函数(所谓构造函数咱们能够简单理解为进行new操做的函数就是构造函数)所建立的对象都是实例对象;数组

 

 

var people = new Student();
console.log(people)

 

 

 

  上面代码中的people就是一个实例对象,Student 就是一个构造函数;浏览器

  

  函数对象:函数

    函数对象咱们能够简单的理解为函数,由于在js中函数自己就属于一个对象;而上述代码中的Student是一个构造函数,构造函数是一种特殊的函数,构造函数每每都是在实例对象建立后才进行调用,做用是对实例对象进行初始化的操做;构造函数和普通函数的区分仅仅只是功能区分的一个称呼,体如今JS代码中的区别就是new和不new的区别;有new关键字就是新建一个构造函数,没有new关键字就是新建一个普通函数;this

 

  原型对象:spa

    原型对象咱们能够简单的理解为原型对象是实例对象和函数对象的父对象,俗称:爸爸,而且经过实例对象和函数对象都能找到原型对象;prototype

  

  咱们先来看看实例对象和函数对象的关系:code

  

function people(name){
     this.name=name;
     console.log(this.name);
}

var Student=new people("咸鱼");

 

  上面代码中咱们能够看到people是构造函数,Student是实例对象,两者之间的关系体如今constructor属性中;

  

Student.constructor==people;//true

  咱们的实例对象对象中是能够经过constructor属性来访问到构造函数的,那么反过来能够实现吗?

  答案是:不行!!!  可是咱们能够变通一下经过instanceof方法来进行检查

  

Student instanceof people  //true

 

  经过这种方式,咱们能够间接的检查一个对象是不是构造函数的实例对象

 

 

  new关键字作了什么?

    上面讲了构造函数和普通函数在JS中没有什么太大的区别,因此咱们能够想到那么直接使用函数的话,this的指向确定是浏览器全局对象window,那么咱们new一下以后,this的指向将会改变为新的实例对象,而且还会将这个新的实例对象返回回来;

    因此咱们能够总结new作了什么:

       1.新建了一个空的函数对象;

       2.改变this的指向,将this的指向改成接收新函数对象的实例对象

          3.返回这个新的实例对象

 

  

   你们来看一个小demo:

    

function people(name){
     this.name=name;
     this.say=function(){
             console.log(this.name);
     }

}

var Student=new people("咸鱼");
var man=new people("张三")
Student.say();  //咸鱼
man.say();    //张三
console.log(Student.say === man.say);  //false

  从上面代码中咱们能够看到咱们有两个实例对象分别是Student、man;有两个构造函数people;咱们能够看到最后两个结果进行比较是不相等的,这就说明两个函数对象不是同一个,这就验证了上面的new关键字,每new一下,就会新建一个空白的函数对象;若是咱们要作的事情都同样,每一次都须要去从新new一个对象,若是是大量重复的作100次一样的事情呢?难道咱们要去new100次吗?这会给内存带来极大的浪费;

  那如今怎么解决呢?JS官方都已经替咱们想好了办法,咱们能够把这个方法放到原型对象上,每次咱们须要用的时候去调用就能够了;那么咱们怎么放到原型对象上呢?

  JS中每个构造函数都有一个prototype属性,这个属性指向的是另外一个对象,这个对象就是原型对象;经过这个属性咱们能够直接找到原型对象,固然原型对象也是一个对象,毕竟万物皆对象嘛;同时原型对象中也有一个constructor属性,这个属性也是指向构造函数;

  对了,JS中每个对象都有一个__proto__属性(注意是左右两边各两个下划线),咱们能够经过这个对象直接访问到原型对象;构造函数能够直接经过prototype直接访问到原型对象,实例对象能够经过__proto__直接访问到原型对象,这之间就有了联系;因此获得结论:实例对象.__proto__===函数对象.prototype,由于这二者都是访问到原型对象,共同的爸爸了;

   咱们能够将上面的代码改造一下:

function people(name){
     this.name=name;
     people.prototype.say=function(){
             console.log(this.name);
     }

}

var Student=new people("咸鱼");
var man=new people("张三")
Student.say();  //咸鱼
man.say();    //张三
console.log(Student.say === man.say);  //true

  这样咱们就能够直接将say方法直接挂到原型对象上面了;

  

  这里有个图例能够对照着看,更方便理解:

  

 

 

   

function Student(name){
        this.name=name;
}
var xianyu = new Student('xianyu');
console.log(xianyu.__proto__ === Student.prototype); // true
console.log(xianyu.constructor === Student); // true
console.log(xianyu.constructor === Student.prototype.constructor); // true

    得出的结果以下:

 

 

   因此咱们能够总结一下结论:

       1.实例对象经过__proto__和函数对象经过prototype都是可以访问到原型对象的;

       2.实例对象经过constructor属性能够访问到构造函数,由于实例对象的constructor属性直接指向构造函数;

               3.原型对象上面的constructor属性跟实例对象同样,都是指向其构造函数

 

  原型链:

    对象能够经过“.”操做获取到一个属性的值,首先会在对象自身开始查找,若是查不到会到原型对象(__proto__)中去查找,若是原型对象中尚未就会把当前获得的原型对象看成实例对象,继续经过(__proto__)去查找当前原型对象的原型对象中去找,也就是去爸爸的爸爸那里找,直到__proto__为null时中止;

    

    上面可能有点拗口,咱们换个生活中的例子来理解:就是你有个对象,你丈母娘问你要20W彩礼,你本身拿不出来你没有(对象自己没有属性),丈母娘让你问你爸爸(原型对象)要,若是你爸爸也没有,你爸爸就得问你爸爸的爸爸也就是你爷爷要(原型对象的原型对象,这里的第一个原型对象实质上成了一个实例对象,由于你爸爸在你爷爷那里永远是儿子),若是你爷爷也没有就继续往上要……如此反复,实在拿不出来(null),丈母娘大手一挥,拿不出来就不嫁了(直到为null时中止);

 

  原型链实现继承:

    咱们知道全部的对象都有个toString()方法,上述代码中实例对象xianyu其实也是一个对象,这就是JS中的万物皆对象的道理,咱们没有给xianyu加任何toString()方法,它是哪里来的?继承来的!由于xianyu.__proto__最终指向的是Function原型对象(Function函数对象一直往上查找原型对象最终是Function原型对象,Object函数对象一直往上查找原型对象最终是null),由于Function原型对象中有,因此xianyu做为它的实例会继承上面的方法,这就是JS继承的本质;

    

    也就是说你爹(原型对象)那有20W,没花在银行存着,你(实例对象)也能够理解为你有20W在银行存着,由于你爹的钱早晚会给你,你爹的钱间接就是你的钱,你要是还存着,你的儿子(实例对象的实例对象)也就间接有了20W;

 

 

    总结:

      1.实例对象能够经过__proto__访问原型对象,函数对象能够经过prototype访问原型对象;

      2.原型对象上面的方法必定会继承给下属的实例对象,反之若是要给原型对象上添加方法须要经过   函数对象.prototype.方法名  或  实例对象.__proto__.方法名  进行添加;

         3.原型对象实质上也是一个对象,原型对象上面也会有__proto__、constructor等属性,因此原型对象能够经过  原型对象.__proto__来访问原型对象的原型对象,也能够经过constructor来知道本身是属于哪一个构造函数上面的实例对象;

相关文章
相关标签/搜索