一篇文章图文并茂地带你轻松学完 JavaScript 原型和原型链
JavaScript 原型和原型链
在阅读本文章之前,已经默认你了解了基础的 JavaScript
语法知识,基础的 ES6
语法知识 。
本篇文章旨在为 JavaScript继承
打下基础
原型
- 在
JavaScript
里任何一个函数都有一个prototype
属性,这个属性称之为原型
function Person() {
this.name = "name";
}
console.log(Person.prototype)
而 Person.prototype
实际上是一个包含 constructor
属性的对象
而 constructor
实际上就是构造函数本身
// Person.prototype
{
constructor: Person
}
Person.prototype
指向的对象很特殊(原型链学完就明白为什么特殊了),可以被实例所共享,可以作为实例的属性直接调用
const obj1 = new Person();
const obj2 = new Person();
console.log(obj1.constructor === obj2.constructor) // true
console.log(obj1.constructor === Person) // true
这意味着,我们可以在 Person.prototype
上加一些共享属性或者方法,然后直接在实例中共享
Person.prototype.eat = function() {
console.log("eat");
}
// 现在
obj1.eat(); // "eat";
obj2.eat(); // "eat";
上面,我们简要的说明了原型,以及原型的作用,下面我们探索原型链
原型链
我们先看一个小例子
function Person() {
this.name = "name";
}
const p = new Person();
p.toString(); // [object Object]
我们并没有在 Person.prototype
上定义 toString
这个方法,按理来说当我们调用的时候应该报错 ,然而浏览器却 "不厚道" 的输出了 [object Object]
要解释这个原因,还有 "很长的路" 要走。
- 首先任何实例化出来的对象都拥有
__proto__
属性,这个属性指向构造函数的prototype
console.log(p.__proto__ === Person.prototype) // true
之前我们写过这样的代码
Person.prototype.eat = function() {
console.log("eat");
}
obj1.eat(); // "eat";
obj2.eat(); // "eat";
我们惊讶的发现, obj1
上也可以拥有 eat
方法了。
- 如果一个对象上没有这个属性,他就会去他的
__proto__
属性对应的对象上去找,如果还没有就继续这个对象的__proto__
上去找
那么 Person.prototype
是否有 __proto__
呢
console.log(Person.prototype.__proto__);
在这里输出的结果中有 constuctor: Object
之前提到 constuctor
其实就是构造函数本身,因此
console.log(Person.prototype.__proto__ === Object.prototype); // true
我们在 Object.prototype
中发现了这个方法,并进行调用
console.log(Object.prototype.hasOwnProperty("toString")) // true
Object.prototype.toString.call(p); // [object Object]
至此原型链基本是说完了,有兴趣的可以探究一下
Object.prototype
是否有__proto__
?- 构造函数是否也是被实例化出来的?
- 数组的原型链是什么样的
对于第三题,有兴趣的可以自己画一下图
console.log(Array.prototype.__proto__ === Object.prototype) // true