深刻剖析prototype、constructor、_proto_原理

JavaScript中比较难理解的点之【prototype、_ proto _、constructor】,一般不明白这三者关系的同窗都有个毛病:继承也不懂!
深入理解这个知识点不只能够对学习继承有帮助,并且对new关键字、this、性能方面都会有更好的认识。最关键是,几乎做为面试必考题目以前,没啥理由很差好看完~

友情提示:文章相对枯燥且绕,必定要耐心。这篇文章会尽可能按照我这段时间所产生过的疑惑,以懵逼当事人之一的角度去一一解开谜底。javascript


在这个复杂三角恋的关系中,我认为最让人容易混淆的有以下几个点:java

  • Function及Object在原型链中所扮演的角色
  • 这三者存在相互引用,且呈链式这样骚里骚气的关系
  • Function及Object这两个内置对象的特殊性
  • 函数即对象
  • construtor、prototype、__proto __具体存在哪一个对象当中

接下来咱们对劈腿三件套【prototype、_ proto _、constructor】 依次从不一样的角度去分析?面试

  1. 为什么有?
  2. 是什么?
  3. 有何用?
  4. 怎么用?

@TOC浏览器

真正剖析三角恋【prototype、_ proto _、contrutor】是本文最核心的地方,若是你对该知识点有了必定的了解以后,也建议先从这里读起函数


为什么有?

这你应该听过:JS一开始设计的初衷是一个给浏览器作简单交互用的,做者估计也万万没想到时至今日能发展到这么强大!后来的JS慢慢的更像一个Java这样的==面向对象==程序语言。而在Java中有类Class,对象Object,举个例:post

// Java

// 一种类: 人
public class Person {
    String name = '狗子';
}
// 男人
public class Man extend Person {
    int jj = 18;
}
// 一个具体的男人
Man tony = new Man();     // 托尼老师 - 一个拥有18cm男人的实例

而在Java中,有new、extend关键字来完成了继承的关系,性能

那么问题来了,JS如何实现?
这个问题就是【为什么有?】的答案,为了让js更好的写出面向对象的代码,实现继承。学习


是什么?

在 ==【prototype、_ proto _、constructor】== 中我认为先解释清楚【construtor、prototype】之间的关系尤其重要,因此,我决定先从【consrtutor】做为切入点展开。测试

若是你学过Java那么就能够找到js模拟类、继承有不少类似的地方

construtor-构造函数

若是你学过Java之类的语言,那么理解这点会很是轻松。由于construtor跟Java当中的同样,就是一个生成具体对象的函数。
代码:字体

function Person (name) {
    this.name = name;
}
let person = new Person('js bigname');

// 对比 Java
public class Person {
    String name = '';
    // java构造函数
    Person (name) {
        this.name = name;
    }
}
Person person = new Person('java bigname');

对吧几乎同样的意思跟写法。

construtor是什么?

这里就能够下定义了,用来建立对象的函数!Js构造函数自己并没有特殊,仅仅是约定首字母大写而已。真正给函数带来不一样的是 ==new==关键字。

那么对象能够是能够建立了,可是如何实现继承?往下看

prototype-原型对象

<font color=#green size=4>假如让你当js语言的设计者,你会如何去实现继承的功能呢?

带着这个问题思考会很是有用,而我首先会这么思考:

要实现继承,第一个要解决的问题:
==son如何去访问father的方法?son中拥有father!==

<font color=#0066CC size=4>模拟实现继承效果的伪代码:

function Father (name) {
        this.name = name;
    }

    function Son (name) {
        this.showName = function () {
            console.log(this.name || 'empty')
        };
    };

    let son = new Son('son1');
    son.showName();  // empty

此时Son与Father一点关系都没有,怎么办?

function Father (name) {
        this.name = name;
    }

    function Son (name) {
        this.father = new Father(name); // 改变的地方
        this.showName = function () {
            console.log(this.name || this.father.name || 'empty') // 改变的地方
        };
    };

    let son = new Son('son1');
    son.showName();  // son1

在son中增长父类对象father,若是在son中找不到该属性,则从father中去读取,而father的父类实际上是Object,万物皆Object嘛,逃不掉的。

function Object (firstName) {
        this.firstName = firstName;
    }

    function Father (name , firstName) {
        this.father = new Object(firstName);
        this.name = name;
    }

    function Son (name, firstName) {
        this.father = new Father(name , firstName);
        this.showName = function () {
            // son自己没有firstName则从father上找,fathter没有则从father的father上找(也就是object)
            // 这一条的关系链其实也就是日常所讲的原型链
            console.log(this.firstName || this.father.firstName || this.father.father.firstName || 'empty')
            console.log(this.name || this.father.name || 'empty')
        };
    };

    let son = new Son('son1', 'liu');
    son.showName();

将这一个设想结合到真实的prototype中,son实例中以及father实例中的father对象其实就是一个简易版的prototype!!!它指明了各个对象之间的关系。

这里先作一个基于上面代码总结出来的关系图:
在这里插入图片描述
JS中这种继承关系的实现,其实也基本跟上面一致。

prototype是什么?

prototype是在【建立对象过程当中】==自动==为对象添加的【内置属性(对象类型)】;你能够先这么理解,后面还会讲到prototype的真正位置。

_proto _-原型对象的引用

_proto _是什么?

proto是指向prototype对象的变量。(他指向的是他的构造函数的prototype)

看代码:

function Person () {
    this.name = 'tony';
}
let person = new Person();

调试模式下观察person对象:
在这里插入图片描述
咱们只定义了name属性,但却被自动添加了proto属性,而且指向的prototype是Object类型,这其实就是Js中对象默认自动继承Object的意思了。当执行==person.age==会先从person对象查找,没有则从proto对象中查找。


真正剖析三角恋【prototype、_ proto _、contrutor】

文章最值钱的内容,前面作了那么多铺垫就是为了引出这里而铺垫

function Person () {
    this.name = 'tony';
}
let person = new Person();

仍是刚才的案例,重点看一下person下的_ proto _内的属性
在这里插入图片描述

插入:先梳理一下函数即对象的概念?
Function做为内置对象之一,不要过多的纠结他,只要知道<font color=red size=4>Function是函数也是一个对象,而且继承自Object。构造函数Person是一个函数,继承自Function,但new出来的person是对象,继承自Object

继续------------------------》》

  • 第一点:construtor始终指向建立本身的函数(记住了)

如:person 下的 proto 的constructor指向Person函数

  • 第二点:_ _proto__始终指向prototype【==prototype存在于构造函数Person下==,这一点也很是很是重要】
  • 第三点:Person产生的对象能够有不少个,但所产生的protorype只有一个
function Person () {
    this.name = 'tony';
  }
  Person.prototype.age = 12;

  let person1 = new Person();
  let person2 = new Person();
  console.log(person1 === person2);   // false
  console.log(person1.__proto__ === person2.__proto__); // true
  • 第四点:prototype存在于函数对象中【Person()】
  • 第五点:__proto_ _存在于对象中【person】
  • 第六点:函数即对象,因此函数也有__proto_ _

记住上面的点:而后带着来理解==let person = new Person()==所发生的事情:
在这里插入图片描述

<font color=#aaa size=3>_纠正上面紫色字体,应该改成:Object构造函数的protype没有__proto __

这个过程当中有两点须要注意的:

  • Function函数的__proto __指向本身的prototype
  • Object函数的prototype没有__proto __,因此Object就是原型链的终点

<font color=purple size=3>总结

  • <font color=deepPurple size=3>Function函数的__proto __指向本身的prototype
  • <font color=deepPurple size=3>Object对象没有__proto __,因此Object就是原型链的终点
  • <font color=deepPurple size=3>__proto __指向prototype
  • <font color=deepPurple size=3>prototype才有construtor,construtor指向构造函数,而且构造函数才有prototype,这相互嵌套的关系理解起来可能会容易混淆
  • <font color=deepPurple size=3>prototype只有一个(Person的只有一个)
  • <font color=deepPurple size=3>除了Object构造函数没有prototype外,对象都有__proto __

// Object构造函数是指函数对象Object(){},而不是construtor

那么什么是原型链?
上图中荧光绿【1,2,3,4】就是一天原型链!他规定了对象之间的关系,以及变量访问的查找规则。

先作个小结:
JS千辛万苦作了这么多花里花哨的关系处理,无非就是为了达到继承的效果!也就是处理变量访问的规则,例如person.name,假如person自己没有这个属性咋办?就去person的__proto __里面找呗,同理往下推理。这一条查找的路线就是原型链!


有何用?

到这里应该能理解到,【prototype、_ proto _、constructor】是为了更好地面向对象,实现继承的解决方案


怎么用?

怎么利用这些属性实现继承?

先来第一种

construtor继承:

function Father () {
        this.name = 'father';
    }
    function Son () {
        Father.call(this); // 构造继承,将son传递给Father()函数做为上下文,因此this.name实际在son上建立name属性
        this.age = 12;
    }

    let son = new Son();

看下执行以后son的结构:
在这里插入图片描述
看图,在son对象中建立了name属性,而且prototype仍然仍然是Object类型,与Father没有关系。
这种方式的缺点:全部属性都建立在对象当中,试想一下,假如建立了100个son,就会在建立了100次name。若是你想要建立的属性是给全部对象不是独一份,而是共享的怎么办?
回想一下上面提到的,prototype是独一份的,因此只须要将共享的属性建立prototype中,而后将Son的prototype改成指向Father,因此看下一个继承方式。

原型链继承:

function Father () {
            this.name = 'father';
        }
        // Father在原型中定义公共方法
        Father.prototype.getName  = function () {
            console.log(this.name);
        };

        function Son () {
            this.age = 12;
        }

        //name属性在son中找不到,则会自动从Son的prototype(即father对象)中寻找
        Son.prototype = new Father(); // 原型链继承

        let son = new Son();

看下执行以后son的结构:
在这里插入图片描述
固然,原型链存在的问题就是,全部son实例对象的prototype指向的father对象都是同一份。因此一旦修改就会影响到全部的对象。

<font color=#0066CC size=3>通常的场景就是,既有须要共享的属性,也有独属的。如Person,每一个人都有年龄,姓名,但值各不相同,因此适合构造函数继承,而获取名称,获取年龄这样的方法每一个人都如出一辙,因此应该定义成公共方法,就应该使用适合链继承。

综上述:

组合继承才是上佳选择,欸,感受有点跑题了,咱们讲的主题可不是这个。后面再考虑作个关于==通常企业是如何使用js继承的==。

其实到这里我感受应该能够结题了,但又想了想,会不会有人看了上面Person与Object的【prototype、_ proto _、constructor】关系,搞多了个Son继承Person就不知道咋回事的了吧。

我想一想应该还真的有,因此我以为再以上面原型链继承以后【prototype、_ proto _、constructor】各对象之间的关系:
在这里插入图片描述
这里主要体现了son是如何经过原型链集成以后的关系。

那么到这里就该结题了。

<font color=#0066CC size=3>题外话:这篇包括从查阅资料,写demo测试,到这篇文章的编写前先后后花了将近5天时间。但其中是写文章的时候, 用本身的语言从新组织出来,测试案例,才感受真正有点心照不宣的感受,因此除了看文章以外必定要动手,思考一下,若是要给别人讲解这个知识点你会怎么讲

另外,很感谢很是不错的文章,让我对这个知识点有了不一样的认知:

用本身的方式(图)理解constructor、prototype、__proto__和原型链

帮你完全搞懂JS中的prototype、__proto__与constructor(图解)

相关文章
相关标签/搜索