zoukankan      html  css  js  c++  java
  • 详解JS原型与原型链

    一、原型

      任何对象都有一个原型对象,这个原型对象由对象的内置属性_proto_指向它的构造函数的prototype指向的对象

      即任何对象都是由一个构造函数创建的,但是,不是每一个对象都有prototype,只有方法才有prototype。

    function Person() {
    }
    var p = new Person();
    //方法才有prototype,普通对象无prototype
    console.log(Person.prototype); // Object{} 
    console.log(p.prototype); // undifined
    
    //任何对象都是有构造函数的,Person这种方法的构造函数是Function。
    //备注:constructor很容易被改变,一般不用它,此处只是打印下列对象的构造函数是什么。
    console.log(p.constructor); //function Person(){}  
    console.log(Person.constructor); //function Function(){} 
    console.log({}.constructor); // function Object(){}
    console.log(Object.constructor); // function Function() {}
    console.log([].constructor);  //function Array(){}

      那什么是构造函数呢?用function声明的都是函数,而如果直接调用的话,那么Person()就是一个普通函数,只有用函数new产生对象时,这个函数才是new出来对象的构造函数。

    二、采用new关键创建对象的过程

    1、声明方法

      当我们声明一个function关键字的方法时,会为这个方法添加一个prototype属性,指向默认的原型对象,并且此prototype的constructor属性也指向方法对象。此二个属性会在创建对象时被对象的属性引用。

    2、我们如果用Hello创建一个对象h,看这个对象有什么属性。

      备注:getPrototypeOf是获取_proto_。

      我们惊喜的发现,new出来的对象,它的constructor指向了方法对象,它的_proto_和prototype相等。  即new一个对象,它的_proto_属性指向了方法的prototype属性,并且constructor指向了prototype的constructor属性。

    3、所以new创建一个对象的过程

    function Hello(name) {
      this.name = name;
    }
    var h = new Hello("...");
    //伪代码:
    function newObj(name){
      var obj = {};
      obj.__proto__ = Hello.prototype; 
      obj.constructor = Hello.prototype.constructor;
      var result = Hello.call(obj, name);
      return typeof result==='object' && result!=null ? result : obj;  //当无返回对象或默认时返回obj。
    }
    var hh = newObj("...");
    console.log(hh);
    console.log(h);
    //虽然hh!=h,但是可以看到这个hh就和h的结构一样了

      过程:

    (1)先创建一个空对象,

    (2)设置空对象的_proto_指向方法的原型,

    (3)设置空对象的constructor为构造函数的名称

    (4)用新对象做this指向方法调用函数,

    (5)将初始化完毕的新对象返回

      具体可以看下之前总结的这篇:原生JS实现new方法、new一个对象发生的四部、new里面常用的优先级

    4、原型链

      第一个 __proto__  指向的是 Hello.prototype;第二个 __proto__ 指向的是 Object.prototype,

      从上面说明的过程中,我们发现只要是对象就是由构造函数来创建的,并且内部二个属性是从构造函数的prototype衍生的一个指向,而构造函数的prototype也是一个对象,那么它应该肯定也有一个构造函数,首先它是一个Object {} 对象,那么它的构造函数肯定是Object,所以就会有一个指针_proto_指向Object.prototype。最后Object.prototype因为没有_proto_,指向null,这样就构成了一个原型链。

    三、原型链

    1、什么是原型链?

      每个对象都有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找...... 这个操作被委托在整个原型链上,这个就是我们说的原型链了。

      原型链的核心就是依赖对象的_proto_的指向,当查找自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有_proto_指向了。因为_proto_实质找的是prototype,所以我们只要找这个链条上的构造函数的prototype。其中Object.prototype是没有_proto_属性的,它==null。

      原型链查找分析:

    (1)当访问一个对象的成员的时候,会先在自身找有没有,如果找到直接使用。

    (2)如果没有找到,则去原型链指向的对象的构造函数的prototype中找,找到直接使用,没找到就返回undifined或报错。

    function Person(name){
      this.name = name;
    }
    var p = new Person();
    //p ---> Person.prototype --->Object.prototype---->null

    2、原型指针

      我们知道了原型的概念,接下来我们就看下面这张图来具体分析一下原型的指针:

      中间最上面蓝色模块标注的构造函数Foo, 里面有两个属性: _proto_ 和 prototype, 这两个很容易使人混淆。

    (1)prototype属性,它是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象; 这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象);

    (2)__proto__ 是原型链查询中实际用到的,它总是指向 prototype,换句话说就是指向构造函数的原型对象,它是对象独有的。注意,为什么Foo构造函数也有这个属性呢,因为在js的宇宙里万物皆对象,包括函数;

      根据以上的概括我们能知道Foo构造函数_proto_指向的是他的构造函数的原型对象,它的构造函数是Function, 也就是说Foo的_proto_指向Function.prototype, 

      我们再看到左边绿色的a和b函数的_proto_指像的是Foo.prototype,因为他们是通过 new Foo实例化出来的,它们的构造函数就是Foo(),即a._proto_ = Foo.prototype;

      接着我们来看看最右边紫色的模块Function.prororype, 它的_proto_指针指向的是Object.prototype,Object._proto_又为null.。

      于是我们就可以得出:在原型链中的指向是,函数 → 构造函数  → Function.prototype → Object.protype → null ;

    (3)constructor:

      我们看到最中间灰色模块有一个constructor属性,这个又是做什么用的呢?

      每个函数都有一个原型对象,该原型对象有一个constructor属性,指向创建对象的函数本身。

      此外,我们还可以使用constructor属性,所有的实例对象都可以访问constructor属性,constructor属性是创建实例对象的函数的引用。我们可以使用constructor属性验证实例的原型类型(与操作符instanceof非常类似)。

      由于constructor属性仅仅是原始构造函数的引用,因此我们可以使用该属性创建新的对象,如:

      通过第一个对象实例化对象的constuctor方法创建第2个实例化对象,说明创建的新对象ninja2 是Ninja的实例,由于ninja和ninja2不是同一个对象可以得出它们是两个截然不同的实例;

    3、结论

    (1)__proto__ 是原型链查询中实际用到的,它总是指向 prototype;

    (2)prototype 是函数所独有的在定义构造函数时自动创建,它总是被 __proto__ 所指。

    (3)所有对象都有__proto__属性,函数这个特殊对象除了具有__proto__属性,还有特有的原型属性prototype。

    (4)prototype对象默认有两个属性,constructor属性和__proto__属性。

    (5)prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式。

    (6)constructor,这个属性包含了一个指针,指回原构造函数。

  • 相关阅读:
    一:字符串中匹配中文
    flask+gevent的异步框架
    linux常见的操作指令
    Markdown语法
    Git常用操作
    Vim
    IDEA使用汇总
    Maven基本使用汇总
    Zookeeper问题汇总
    编程最佳实践
  • 原文地址:https://www.cnblogs.com/goloving/p/13019622.html
Copyright © 2011-2022 走看看