原型链! __proto__ 与 prototype

Every JavaScript object has a second JavaScript object (or null ,but this is rare) associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype.
每个 JavaScript 对象都会对应一个原型对象(除了 null),并从原型对象继承属性和方法。
JavaScript 权威指南

一句话解释:对象 __proto__ 属性是JS内置的,它所对应值是原型对象的 prototype ;而 prototype 就是你自己写的原型属性、方法 加上 JS原型内置的属性、方法

var one = {x: 1};
var two = new Object();

one.__proto__ === Object.prototype // true
Object.getPrototypeOf(one) === Object.prototype // true

two.__proto__ === Object.prototype // true
Object.getPrototypeOf(two) === Object.prototype // true

one.toString === one.__proto__.toString // true
two.toString === two.__proto__.toString // true

two.toString === one.__proto__.toString // true

那现在的疑问就是 __proto__prototype 的使用

  1. __proto__ 不是ES的标准,单纯是为了方便让用户可以直接获取,推荐通过 Object.getPrototypeOf() 来获取,并不推荐直接通过这方法来修改原型
  2. prototype 是创建函数时,JS为这个函数自动添加的属性,通过手动配置 prototype 属性,或者指向现存的对象来继承该对象。

🎉 小结

prototype 指向一块内存,这个内存里面有共用属性,__proto__ 指向同一块内存

prototype__proto__ 的不同点在于: prototype构造函数的属性,而 __proto__对象的属性


JavaScript 常被描述为一种 基于原型的语言 (prototype-based language) ——每个对象拥有一个 原型对象 ,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为 原型链 (prototype chain) ,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

准确地说,这些属性和方法定义在 Object 的构造器函数(constructor functions)之上的 prototype 属性上,而非对象实例本身。

在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制 —— 而是在对象实例和它的构造器之间建立一个链接(它是 __proto__ 属性,是从构造函数的 prototype 属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

注意: 理解对象的原型(__proto__属性)与 构造函数的 prototype 属性之间的区别是很重要的。前者是每个实例上都有的属性,后者是构造函数属性

在JavaScript中,函数可以有属性。 每个函数都有一个特殊的属性叫作 prototype (原型),通过给 prototype 添加属性和方法,来为以这个 函数 为 基础模型实例对象 继承下来函数上所有的属性。

// 从函数里创建一个对象
function f() {
  this.a = 1;
  this.b = 2;
}
// 在f函数的原型上定义属性
f.prototype.b = 3;
f.prototype.c = 4;

// 通过new操作符来创建基于f()的对象实例
let o = new f();
// 输出一下 对象o
console.log(o)
/* log
 ƒ { a:1, b:2 }
    a: 1
    b: 2
    __proto__:
      b: 3
      c: 4
      constructor: ƒ f()
        arguments: null
        caller: null
        length: 0
        name: "f"
        prototype: {b: 3, c: 4, constructor: ƒ}
        __proto__: ƒ ()
        [[FunctionLocation]]: VM510:2
        [[Scopes]]: Scopes[2]
      __proto__: Object
*/

// 更多熟悉调用demo

console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为 1

console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"

console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看它的原型上有没有
// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4

console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined
// o.__proto__.__proto__.__proto__ => null
// Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(o))) => null

如果想知道 new 操作符做了那些事,可以查看这篇笔记 👉 JS中的 new 关键字做了什么?

尾声

- 使用不同的方法来创建对象和生成原型链?
这个我就不多说了,直接看MDN上的示例就好 使用不同的方法来创建对象和生成原型链 - 继承与原型链 | MDN
知乎上也有,直接搜就好了。

- 简单的说,原型链 其实算是面向对象思想中的继承部分,以方便根据需求拆分功能,完成特定的工作职责

- ES6 提出了 类(Class) 这个概念
让对象原型的写法更加清晰,更像面向对象编程的语法,并且同样有着 prototype 这个属性。

资源

对象原型 - 学习 Web 开发 | MDN
继承与原型链 - JavaScript | MDN
Object.prototype.__proto__ - JavaScript | MDN
Object.getPrototypeOf() - JavaScript | MDN
Javascript Object Hierarchy

JS 中 __proto__ 和 prototype 存在的意义是什么? - 知乎
傻傻分不清的__proto__与prototype - SegmentFault 思否
从__proto__和prototype来深入理解JS对象和原型链 · Issue #9 · creeperyang/blog