Open jannahuang opened 2 years ago
假设有一个对象包含一些属性和方法,而我们在构建另一个新的对象时,想直接复用那些属性和方法而不是复制或重新实现它们,我们可以用原型继承来实现这个需求。
在 JavaScript 中,对象有一个特殊的隐藏属性 [[ Prototype]],它要么为 null,要么就是对另一个对象的引用。该对象被称为“原型”。 当从一个对象中读取一个它没有的属性时,JavaScript 会向它的原型读取该属性,这被称为“原型继承”。 根据 MDN 文档可知,可以用 Object.setPrototypeOf() 方法设置一个指定的对象的原型 (内部 [[ Prototype]] 属性)为另一个对象或 null。可以用 Object.getPrototypeOf() 方法获取原型。但是更改对象的 [[ Prototype]] 在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。建议使用 Object.create() 来创建带有你想要的 [[ Prototype]] 的新对象。
// 语法 Object.create(proto, propertiesObject),其中 propertiesObject 可选 let animal = { eats: true }; // 创建一个新对象 rabbit,将 animal 设置为 rabbit 的原型,rabbit 便继承了 animal 的属性。 let rabbit = Object.create(animal, { jumps: { value: true, writable: true, //非必填 enumerable: true, //非必填 configurable: true //非必填 } }) Object.getPrototypeOf(rabbit) === animal // true Object.setPrototypeOf(rabbit, {}); // 将 rabbit 的原型修改为 {}
注意:以前可以用 proto 属性设置原型,现在该属性已废弃,不建议使用。
如果想排出继承的属性,可以用内建方法 obj.hasOwnProperty(key),如果 obj 有自己的名为 key 的属性则返回 true。 该方法是 Object.prototype.hasOwnProperty 提供的,hasOwnProperty 的 enumerable 为 false,是不可枚举的。而在 for...in 只会列出可枚举的属性。
我们可以使用 new F() 这样的构造函数来创建一个新对象。 如果给 F.prototype 赋值一个对象,那么在 new F() 时,F.prototype 属性为新对象添加 [[ Prototype]](原型)。 举例:
let animal = { eats: true }; function Rabbit(name) { this.name = name; } Rabbit.prototype = animal; // 当创建一个 new Rabbit 时,把它的 [[Prototype]] 赋值为 animal let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
注意: 如果在创建之后,F.prototype 属性发生变化(F.prototype = < another object>),那么通过 new F 创建的新对象也将随之拥有新的对象作为 [[ Prototype]],但已经存在的对象将保持旧有的值。
每个函数都有 "prototype" 属性,默认的 "prototype" 是一个只有属性 constructor 的对象,属性 constructor 指向函数本身。
// 可以在控制台打印出来看,f 指向函数本身 {constructor: ƒ}
举例:
// 假设有函数 Rabbit function Rabbit() {} /* 则其默认的 prototype 为 Rabbit.prototype = { constructor: Rabbit } */ // 此时新建一个对象,则对象的原型 [[Prototype]] 为 { constructor: Rabbit } let rabbit = new Rabbit('white') // 此时 rabbit.constructor == Rabbit,所以可以用 rabbit.constructor 属性来创建一个新对象 let rabbit2 = new rabbit.constructor("black")
constructor 的好处就是,当不知道一个对象使用哪个构造函数时,可以用这个对象的 constructor 属性来创建类似的新对象。 可是如果将 F.prototype 默认值改掉(比如 F.prototype = {}),就不一定会有 "contructor" 属性了。 因此,为了保证有正确的 "contructor",我们直接添加/删除属性到默认的 prototype(比如 F.prototype.check = true),而不是将其整个替换。 或者,手动添加 "contructor" 属性,将其指向函数本身。
F.prototype = { check: true, constructor: F }
以上笔记参考《现代 JavaScript 教程》及 MDN 文档
原型继承
假设有一个对象包含一些属性和方法,而我们在构建另一个新的对象时,想直接复用那些属性和方法而不是复制或重新实现它们,我们可以用原型继承来实现这个需求。
[[ Prototype]]
在 JavaScript 中,对象有一个特殊的隐藏属性 [[ Prototype]],它要么为 null,要么就是对另一个对象的引用。该对象被称为“原型”。 当从一个对象中读取一个它没有的属性时,JavaScript 会向它的原型读取该属性,这被称为“原型继承”。 根据 MDN 文档可知,可以用 Object.setPrototypeOf() 方法设置一个指定的对象的原型 (内部 [[ Prototype]] 属性)为另一个对象或 null。可以用 Object.getPrototypeOf() 方法获取原型。但是更改对象的 [[ Prototype]] 在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。建议使用 Object.create() 来创建带有你想要的 [[ Prototype]] 的新对象。
注意:以前可以用 proto 属性设置原型,现在该属性已废弃,不建议使用。
for...in 循环也会迭代继承的属性
如果想排出继承的属性,可以用内建方法 obj.hasOwnProperty(key),如果 obj 有自己的名为 key 的属性则返回 true。 该方法是 Object.prototype.hasOwnProperty 提供的,hasOwnProperty 的 enumerable 为 false,是不可枚举的。而在 for...in 只会列出可枚举的属性。
F.prototype
我们可以使用 new F() 这样的构造函数来创建一个新对象。 如果给 F.prototype 赋值一个对象,那么在 new F() 时,F.prototype 属性为新对象添加 [[ Prototype]](原型)。 举例:
注意: 如果在创建之后,F.prototype 属性发生变化(F.prototype = < another object>),那么通过 new F 创建的新对象也将随之拥有新的对象作为 [[ Prototype]],但已经存在的对象将保持旧有的值。
默认 F.prototype
每个函数都有 "prototype" 属性,默认的 "prototype" 是一个只有属性 constructor 的对象,属性 constructor 指向函数本身。
举例:
constructor 的好处就是,当不知道一个对象使用哪个构造函数时,可以用这个对象的 constructor 属性来创建类似的新对象。 可是如果将 F.prototype 默认值改掉(比如 F.prototype = {}),就不一定会有 "contructor" 属性了。 因此,为了保证有正确的 "contructor",我们直接添加/删除属性到默认的 prototype(比如 F.prototype.check = true),而不是将其整个替换。 或者,手动添加 "contructor" 属性,将其指向函数本身。
以上笔记参考《现代 JavaScript 教程》及 MDN 文档