白话解释原型和原型链

1,077 阅读2分钟

前言

“函数在JavaScript中是一等公民”。为什么这么说,因为函数身上承担着重任,他同时扮演着函数、对象和类的角色。我们都知道,在ES6之前js中是不存在类的,那么如何创建对象呢?我们可以通过在function前面添加new来创建实例。

函数与对象

在js中函数和对象默认都分别拥有一个属性。

  • 函数:每个函数有一个属性prototype(以示区分:把他叫做原型对象)
  • 对象:每个对象有一个属性_proto_(以示区分:把他叫做对象原型)

既然有着这么独特的属性,背后一定有着很重要的原因,只是我们暂且不说。我们先来看一个示例。

一个示例

我们创建一个空对象,然后直接调用toString()和toArray()方法,结果发现:toString()可以正常打印,而toArray()显示方法不存在。

let obj = {}
console.log(obj.toString()); // [object Object]
console.log(obj.toArray());  // obj.toArray is not a function

这是为什么呢?我们直接打印一下obj这个对象。 原来这个空对象上有个__proto__属性,这个属性上又有个toString()方法。

原型和原型链

我们先来看一张说到原型链就会出现的图 对象xiaoming是由函数Person创建出来的实例,那么xiaoming的_proto_ 和Person的prototype属性指向同一个对象。这个原型对象本身也是一个对象,因此它也有__proto__属性,同时它还有一个constructor属性,指向Person。这种不断向上查找__proto__属性的特点就是原型链。

我们通过代码直观地来感受一下。

  function Person() {
  }
  var xiaoming = new Person() 
  console.log(xiaoming.__proto__ === Person.prototype);  // -> true
  console.log(Person.prototype.constructor === Person);  // -> true

原型和原型链的作用

上面说到js没有类的概念,那它如何实现创建的实例拥有函数的属性和方法呢?这就是靠原型和原型链实现。

js内部有这样的机制:一个对象如果访问某个属性或者方法,就会先去自身查找,如果没找到就去__proto__属性上查找,如果还是没找到,就去继续向上查找_proto_._proto_,直到到达原型链的顶端,如果还是没找到就会显示没找到。

因此要想实现创建出的实例拥有同样的属性或者方法,只需要给原型对象设置属性和方法就可以了。

Person.prototype.sex = 'men'
Person.prototype.eat = function() {
	console.log("I can eat!")
}

习题

最后我们来看一个小习题来巩固一下以上所学的知识。这里需要了解一个方法Object.create,我简单介绍一下,如果var a = Object.create(B) ,那么a._proto_ === B。

  function Person() {
    this.name = "ren"
  }
  var xiaoming = new Person() 

  function Student() {
    this.age = '12'
  }
  Student.prototype = Object.create(Person.prototype)
  var stu = new Student()

  Person.prototype.sex = 'men'

  console.log(Student.prototype.__proto__ === Person.prototype);  
  console.log(stu.__proto__ === Student.prototype);  
  console.log(stu.__proto__.__proto__ === Person.prototype);  

  console.log(xiaoming.name);  
  console.log(stu.name); 
  console.log(stu.sex);