JavaScript中原型和原型链(图解)

JavaScript中原型和原型链

用构造函数创建一个对象

function Person(){
      this.age = '18';
    }
    let person1 = new Person();
    Person.prototype.name = 'zsl';
    console.log(person1.name); //zsl
    console.log(person1.age);  //18

在上面这个例子中我先创建一个Person构造函数,其中Person首字母大写的是跟普通函数区分开来,表示这是一个构造函数而不是普通的函数。

prototype

在上面第一个例子中我们已经简单的使用过了prototype,
那么究竟什么是prototype(原型)呢?
其中《JavaScript高级程序设计》一书中是这么说的,我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途就是包含可以由特定类型的所有实例共享的属性和方法。我的理解就是prototype就是通过调用构造函数而创建的实例的原型。想要阅读《JavaScript高级程序设计》请到JavaScript高级程序设计(第三版)pdf版 下载
那么我们为什么要使用prototype(原型)呢?
使用原型的对象的好处就是可以让所有的实例对象都能共享它所包含的属性和方法。换句话说就是,不必在构造函数中定义对象实例的信息,而是直接将这些信息添加到原型对象上,看下面这个例子。

function Person(){
    }
    let person1 = new Person();
    let person2 = new Person();
    Person.prototype.name = 'zsl';
    console.log(person1.name); //zsl
    console.log(person2.name); //zsl

在这个例子中就是person1和person2的name值都是zsl,就是共享了实例原型的属性。
那么现在我们已经知道了构造函数和实例原型的关系:
JavaScript中原型和原型链(图解)_第1张图片
我们已经把构造函数和原型的关系介绍清楚了,下面在介绍一下实例与实例原型之间的关系(person与Person.prototype),那么介绍一个_proto_属性,这个属性每个JavaScript对象都支持。

—proto—

已经说过这个属性每个JavaScript对象都支持,那么我们要弄懂这个属性是存在与实例与构造函数的原型对象直接,也就是实例与实例对象之间,而不是存在与实例与构造函数之间。通过这个属性很清楚的知道实例与实例对象之间的关联,看下面验证:

function Person(){

    }
    let person = new Person();
    console.log(person.__proto__ === Person.prototype) //true

对了要注意__proto__并不是一个标准的从实例访问原型的方法,但是Firefox和Safari和Chrome都支持它。当我们用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型即实例的原型,在ECMA-262第五版中管这个指针叫做[[Prototype]],虽然在现在所有实现中并没有标准的方法访问到[[Prototype]],但是可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。就是说,如果[[Prototype]]指向调用的isPrototypeOf()方法的对象Person.prototype,那么这个方法就会返回true

function Person(){

    }
    let person = new Person();
    console.log(Person.prototype.isPrototypeOf(person)) //true

就是说实例person内部有一个指向Person.prototype的指针,因此返回true
在es6中增加一个新方法,叫做Object.getPrototypeOf(),请看验证:

function Person(){

  }
  let person = new Person();
  console.log(Object.getPrototypeOf(person) === Person.prototype)//true

用关系图表示如下:
JavaScript中原型和原型链(图解)_第2张图片
到此已经知道构造函数和实例都可以指向prototype(原型),那么prototype(原型)是否也能指向构造函数或者实例呢?请看下面继续介绍一个constructor属性。

Constructor

prototype(原型)指向实例的没有,但是prototype(原型)指向构造函数的有,所有的JavaScript对象的原型对象有一个constructor属性,这个属性指向当前构造函数。请看验证:

function Person(){

      }
      console.log(Person.prototype.constructor === Person) //true

用关系图表示如下:

JavaScript中原型和原型链(图解)_第3张图片

到此就已经介绍了构造函数,构造函数的原型对象(实例原型),实例它们之间的关系,下面接着说实例与原型。

实例与构造函数的原型对象(实例原型)

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止,举个例子:

function Person() {

        }
        Person.prototype.name = 'zsl';
        var person = new Person();
        person.name = 'xiaoming';
        console.log(person.name) // xiaoming
        delete person.name;
        console.log(person.name) // zsl

那么什么是原型的原型呢?

原型的原型

在JavaScript高级程序设计一书中指出:所有的引用类型默认都继承了Object,而这个继承也是通过原型链实现的,意思就是默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。拿我们上面的例子通俗一点讲就是,构造函数(Person)创建一个实例(person),实例的原型对象即是构造函数的原型对象(Person.prototype),那么实例的原型(Person.prototype)就是Object对象的一个实例,所以实例的原型(Person.prototype)的原型就是Object.prototype
用关系图表示如下:
JavaScript中原型和原型链(图解)_第4张图片
那么介绍完上面的后,接着说Object.prototype的原型,可以直接用代码进行测试:

 console.log(Object.prototype.__proto__) //null

就是说Object.prototype没有原型。
用关系图表示:

JavaScript中原型和原型链(图解)_第5张图片

继承和原型链

关于继承,其实JavaScript中并没有真正的继承,我们在JavaScript中所说的继承是其实是复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。这句话来自《你不知道的JavaScript》一书。
在JavaScript中原型链就是由相互关联的原型之间组成的链状结构就是原型链,从上面关系图中可以清晰直观的看到。

你可能感兴趣的:(JavaScript,Web)