几张图让prototype与__proto__和原型对象不再是我们的烦恼!(图解prototype...)

295 阅读6分钟

初衷

对于js新手来说,《JavaScript高级程序设计》的学习是必不可少的,这里我总结了一下JavaScript难以理解的知识点,现在拿来在掘金分享一下吧

前言

  • 在面向对象js中,JavaScript的原型和原型链的理解绝对是一件很让人头疼的事情,prototype与__proto__也容易让人混淆;我们都知道JavaScript 是基于原型的,但是可能大部分js新手都不知道如何去理解,所以我自己也想尝试理解一下,看看能不能把JavaScript 是基于原型的,用最简单的方法说清楚。
  • 理解了JavaScript 是如何基于原型的,我们就清楚了原型和原型链、prototype与__proto__;
  • 以下纯属个人见解,如果有任何不对的地方,欢迎下方评论告诉我

你必须知道的知识点

想要理解原型和原型链、prototype与__proto__,请先接受以下知识;

  • prototype(原型)

    1. 任何对象的都有一个prototype(原型),prototype(原型)是保存对象实例方法的所在。但是这里的prototype与函数的prototype属性不是同一种东西
    2. 任何函数都有一个prototype属性,这个属性指向函数的原型对象。这里原型对象要与对象的原型区分开来
  • __proto__
    
    1. 任何实例对象都有一个私有属性__proto__([[Prototype]]),这是一个包含指针的私部属性,这个指针指向它的构造函数的原型对象,后面都会使用__proto__而不使用[[Prototype]]
  • constructor(构造函数)
    
    1. 原型对象都会自动获得一个constructor属性,这个属性包含一个指向 prototype 属性所在函数的指针。
    2. 实例化的对象也一定有自身的constructor(构造函数),这里constructor(构造函数)要与原型对象的constructor属性区分开来
    3. 构造器其实就是一个普通的函数
    4. Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
    5. 注:大多数情况下,__proto__可以理解为构造函数的原型对象,即:实例对象.___proto_ === 实例对象.constructor.prototype;但是通过Object.create()方法创建的新对象不适用此等式

搞懂prototype与__proto__

  • 由于上方我说了对象的prototype和函数的prototype属性不是同一种东西,所以我们分两种方式来理解prototype,现在让我们分别用两张图片来理解
  • 函数的prototype属性

(颜色深的小圆圈代表的是属性,后面将不再赘述,默认各位看官都知道)

// "构造函数创建对象"
function Person() {}
// var Person = function () {};
Person.prototype.name = "ziyin";
Person.prototype.myFunc = function () {
    alert(this.name)
}

var person1 = new Person();
person1.myFunc();//"ziyin"

console.log(person1.__proto__ === Person.prototype)//true
console.log(Person.prototype.constructor) //function Person(){}(即构造器function Person)这里是对象原型的constructor属性
console.log(person1.constructor) //function Person(){}(即构造器function Person) 这里是实例对象的constructor
  • 对象的prototype

// "对象字面量创建对象"
var Person = {};//等同于 var Person = new Object()

console.log(Person.__proto__ === Object.prototype)//true   
console.log(Person.__proto__ === Person.constructor.prototype)//true   
console.log(Person.constructor === Person.__proto__.constructor)//true  
"此处等于号前面的constructor是实例对象的constructor,后面的是对象原型的constructor属性"
console.log(Person.constructor)//function Object()

几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例

// "Object.create()创建对象"
var person1 = {name:"ziyin"};//等同于 var Person = new Object()
var person2 = Object.create(person1);

console.log(person2.constructor)// function Object()  
console.log(person1.constructor)// function Object()  
"console.log(person2.__proto__ === person1)// true
console.log(person2.__proto__ === person2.constructor.prototype)// false  __proto__失效"
console.log(person1.constructor === Object)// true  
console.log(person1.__proto__ === Object.prototype)// true __proto__ 未失效

理解原型链与原型

ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念

  • 上面这一段巴拉巴拉的是不是看的有点头晕?事实上这正是原型链最详细的解释,看吧!这对你理解原型链有很大的帮助~
  • 看完上面一段文字,现在让我们用更为简单一点的方法来理解原型链;
    1. 我们都知道__proto__是任何对象都有的属性,每个实例对象(object)的__proto__指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的构造函数的原型对象(_proto_),层层向上直到一个对象的原型对象为null而恰恰在JS的世界里万物皆对象,所以在JS中会有一条由__proto__连接起来的一个链条,每个对象可以通过访问__proto__递归访问这个链条而且访问的最终值会是null;
    2. 当JS引擎开始在对象中查找属性和方法时,会先查找自身是否存在该属性,存在则停止查找,不存在则沿着__proto__连接起来的原型链上查找。如果本身与原型链上存在相同属性,js引擎会在查找到该属性后停止在原型链上查找。
    3. 现在让我们用一张图片来看看原型链是个什么东西?

// 原型链
var Person = function () {} // function Person() {}
var person = new Person()

console.log(person.__proto__ === Person.prototype)//true  
console.log(person.__proto__)//Object {} 这个是一个函数  constructor指向"Person()函数"
console.log(person.__proto__.__proto__)//Object  这里是一个对象,constructor指向"Object()函数"
console.log(person.__proto__.__proto__.__proto__)//null

总结

  • 好啦!到这里相信各位看官都知道prototype与__proto__、原型对象是什么东西了,现在我再来对这篇文章总结一下要点。(qiao黑板划重点了)
    • 任何对象都有一个prototype(原型) ,任何函数都有一个prototype属性,它指向这个函数的原型对象。
    • 任何实例对象都有一个私有属性__proto__,__proto__指向它的构造函数的原型对象;大多数情况下,__proto__可以理解为构造函数的原型对象,即:实例对象.___proto_ ===实例对象.constructor.prototype;但是通过Object.create()方法创建的新对象不适用此等式
    • 原型对象都会自动获得一个constructor属性,它指向prototype 属性所在函数即实例化对象的constructor(构造函数)。
    • 原型链是一条由__proto__连接起来的一个链条,链条上的每个对象可以通过访问__proto__递归访问这个链条而且访问的最终值会是null;

以上文章纯属个人见解,文中如有错误,欢迎评论区指正

希望看完本篇文章能对你理解JavaScript是基于原型的有所帮助!当然最重要的还是理解了本文所说的prototype与__proto__、原型对象,如果本文帮助到了你,欢迎点赞关注!

文章参考了《JavaScript高级程序设计(第三版)》、《你不知道的JavaScript上卷》、掘金作者水乙、"developer.mozilla.org/zh-CN/docs/…"