一篇文章图文并茂地带你轻松学完 JavaScript 原型和原型链

JavaScript 原型和原型链

在阅读本文章之前,已经默认你了解了基础的 JavaScript 语法知识,基础的 ES6 语法知识 。
本篇文章旨在为 JavaScript继承 打下基础

原型

  1. JavaScript 里任何一个函数都有一个 prototype 属性,这个属性称之为原型
function Person() {
    this.name = "name";
}
console.log(Person.prototype)

Person.prototype 实际上是一个包含 constructor 属性的对象

constructor 实际上就是构造函数本身

// Person.prototype
{
    constructor: Person
}
  1. 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]

要解释这个原因,还有 "很长的路" 要走。

  1. 首先任何实例化出来的对象都拥有 __proto__ 属性,这个属性指向构造函数的 prototype
console.log(p.__proto__ === Person.prototype) // true

之前我们写过这样的代码

Person.prototype.eat = function() {
    console.log("eat");
}

obj1.eat(); // "eat";
obj2.eat(); // "eat";

我们惊讶的发现, obj1 上也可以拥有 eat 方法了。

  1. 如果一个对象上没有这个属性,他就会去他的 __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]

至此原型链基本是说完了,有兴趣的可以探究一下

  1. Object.prototype 是否有 __proto__ ?
  2. 构造函数是否也是被实例化出来的?
  3. 数组的原型链是什么样的

对于第三题,有兴趣的可以自己画一下图

console.log(Array.prototype.__proto__ === Object.prototype) // true
posted @ 2021-02-03 23:29  Huro~  阅读(41)  评论(0编辑  收藏  举报