原型以及原型链

109 阅读2分钟

前言

js分为函数对象普通对象,两者都有__proto__属性,只有函数对象(非箭头函数)有prototype属性

原型

创建一个构造函数(非箭头函数),这个构造函数会创建一个prototype属性指向原型对象。原型对象会获得一个constructor属性指向与之关联的构造函数。每次调用构造函数创建一个实例,这个实例的内部指针([[Prototype]])会指向这个构造函数的原型对象,脚本中没有访问[[Prototype]]这个特性的标准方式,但 Firefox、Safari 和 Chrome会在每个对象上暴露__proto__属性,通过这个属性可以访问对象的原型。

创建一个构造函数

构造函数之所以被叫做“构造函数”,是因为其目的是为了将一个空对象构造成需要的样子。
创建一个有关动物的构造函数,并对其原型对象添加睡觉、进食、叫声行为。

function animal({name="旺财",size="small"}={}){
	this.size=size
	this.name=name
}
animal.prototype.sleep='zzzzz'
animal.prototype.eat='muamua'
animal.prototype.bark=(val)=>{console.log(val);}

生成两个实例

生成一个猫的实例和一个狗的实例。

let dog=new animal()
let cat=new animal()
console.log(cat==dog);//false
console.log(animal.prototype.constructor==animal);//true
console.log(cat.__proto__==animal.prototype&&dog.__proto__==animal.prototype)//true
console.log(animal.prototype==animal);//false
console.log(cat==animal);//false
console.log(animal.prototype==cat);//false

由此可见:

  1. 两个实例是不同的。
  2. 构造函数(animal)的原型(animal.prototype)constructor指向构造函数(animal)
  3. 同一个构造函数生成的实例,其__proto__指向都是一样的,都是指向构造函数的原型。
  4. 构造函数,原型,实例是三个不同的对象。

原型层级

访问一个实例某个属性时,会先在这个实例上搜索,看有没有这个属性,如果没得,就去与这个实例相关联的原型上找,如果还没找到,则去这个原型的原型上找以此类推,直到找到null上为止。

原型链

构造函数A,创建了一个实例a,构造函数A通过prototype指向其原型,实例a通过__proto指向原型。原型也是一种实例,他也有自己的构造函数和原型。这样就在实例和原型间构造了一条原型链。继承就可以通过原型链来实现。还是用上面的实例:

function animal({name="旺财",size="small"}={}){
	this.size=size
	this.name=name
}
animal.prototype.sleep='zzzzz'
animal.prototype.eat='muamua'
animal.prototype.bark=(val)=>{console.log(val);}
let dog=new animal()
let cat=new animal()
function people(){}
people.prototype=cat
let person=new people()
console.log(person.sleep);
console.log(cat.sleep);

定义了个人,这个人的构造函数的原型指向动物猫的实例,那么人的实例__proto__就指向动物猫了,在人实例上查找sleep属性发现没有,就去动物猫上找,也就实现了继承。