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
的使用
__proto__
不是ES的标准,单纯是为了方便让用户可以直接获取,推荐通过Object.getPrototypeOf()
来获取,并不推荐直接通过这方法来修改原型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