[[Prototype]]
在 JavaScript 中,所有的对象都有一个隐藏的 [[Prototype]] 属性,它要么是另一个对象,要么就是 null
proto
- 通过
[[Prototype]]引用的对象被称为“原型”,用obj.__proto__访问它let animal = { eats: true }; let rabbit = { jumps: true }; rabbit.__proto__ = animal; // 设置 rabbit.[[Prototype]] = animal // rabbit 能使用 animal 的所有属性和方法 console.log(rabbit.eats) // true__proto__的值可以是对象,也可以是 null。而其他的类型都会被忽略__proto__与内部的[[Prototype]]不一样。__proto__是[[Prototype]]的 getter/setter。__proto__属性有点过时了,用函数Object.getPrototypeOf/Object.setPrototypeOf来取代__proto__去 get/set 原型
“this” 的值
无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,this 始终是点符号 . 前面的对象。
// animal 有一些方法
let animal = {
walk() {
if (!this.isSleeping) {
alert(`I walk`);
}
},
sleep() {
this.isSleeping = true;
}
};
let rabbit = {
name: "White Rabbit",
__proto__: animal
};
// 修改 rabbit.isSleeping
rabbit.sleep();
alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined(原型中没有此属性)
for…in 循环
for..in循环会迭代继承的属性,即会获得原型的属性Object.keys只返回自己的keyobj.hasOwnProperty(key):如果obj具有自己的(非继承的)名为key的属性,则返回true- 思考 任务
F.prototype
F.prototype 属性仅在 new F 被调用时使用,它为新对象的 [[Prototype]] 赋值
function F() {}
let f = new F
f.__proto__ == F.prototype // true
- 每个实例对象都会保持对旧的
prototype的引用 - 每个函数都有
prototype属性,默认的 "prototype" 是一个只有属性constructor的对象,属性constructor指向函数自身。F.prototype.constructor == F // true - 通过实例对象的
constructor来创建一个和它类似的对象let ff= new f.constructor() // ff 为 F 的实例对象 - 给
prototype增加属性F.prototype.name = "ddd" // {name: "ddd", constructor: ƒ}- 任务思考链接
原生的原型
Object.prototype
let obj = {}
obj.__proto__ === Object.prototype // true
let obj = {}创建了一个对象,其相当于new Object(),因此obj的[[Protopype]]会指向Object.prototype
其他内建原型
内建对象,像 Array、Date、Function 及其他,都在 prototype 上挂载了方法。所有的内建原型顶端都是 Object.prototype 。(更直观的图示)
- null:
Object.prototype.__proto__ === nulllet arr = [1, 2, 3]; // 它继承自 Array.prototype? alert( arr.__proto__ === Array.prototype ); // true // 接下来继承自 Object.prototype? alert( arr.__proto__.__proto__ === Object.prototype ); // true // 原型链的顶端为 null。 alert( arr.__proto__.__proto__.__proto__ ); // null
基本数据类型
- 字符串、数字和布尔值上会被包装成对象,然后调用
String.prototype、Number.prototype和Boolean.prototype中的方法。 null和undefined没有对象包装器
更改原生原型
String.prototype.show = function() {
alert(this);
}
"BOOM!".show(); // BOOM!
- 原型是全局的,所以很容易造成冲突
在现代编程中,只有一种情况下允许修改原生原型。那就是 polyfilling。Polyfilling 是一个术语,表示某个方法在 JavaScript 规范中已存在,但是特定的 JavaScript 引擎尚不支持该方法,那么我们可以通过手动实现它,并用以填充内建原型。
原型方法,没有 _proto_ 的对象
proto 被认为是过时且不推荐使用的,可以用新方法代替。现代的方法有:
Object.create(proto, [descriptors])—— 利用给定的 proto 作为[[Prototype]]和可选的属性描述来创建一个空对象。Object.getPrototypeOf(obj)—— 返回对象 obj 的[[Prototype]]。Object.setPrototypeOf(obj, proto)—— 将对象 obj 的[[Prototype]]设置为 proto。let animal = { eats: true }; // 创建一个以 animal 为原型的新对象 let rabbit = Object.create(animal, { jumps: { value: true } }); alert(rabbit.eats); // true alert(Object.getPrototypeOf(rabbit) === animal); // true Object.setPrototypeOf(rabbit, {}); // 将 rabbit 的原型修改为 {}- 比复制 for..in 循环中的属性更强大的对象克隆方式:
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); // 此调用可以对 obj 进行真正准确地拷贝,包括所有的属性
"Very plain" objects
obj.__proto__ = 11该表达式能否正确运行??(详情)__proto__是obj的原型即Object.prototype的访问器属性,因此每次赋值的时候只会调用原型中对应方法。- 如何使
__proto__成为对象的属性?let o = Object.create(null) // 原型为 null,没有继承的 __proto_ 访问器属性 o.__proto__ = 111 // right