我们可以使用构造函数创建新对象,类似于 new F() 这种方式。

如果 F.prototype 的值是个对象,那么 new 操作符就会使用这个对象作为新建对象的 [[Prototype]] 值。

注意:

原型继承是 JavaScript 这么语言的核心特性。

在以前,没有可以直接访问对象原型的方法。唯一可靠的方式是通过构造函数的 “prototype ”属性。现在还有许多脚本在这样做。

注意这里的 F.prototype 表示 F 上的一个叫“prototype”的属性。注意与之前说的名词“prototype(原型)”区分开。

下面举个例子:

  1. let animal = {
  2. eats: true
  3. };
  4. function Rabbit(name) {
  5. this.name = name;
  6. }
  7. Rabbit.prototype = animal;
  8. let rabbit = new Rabbit('小白兔'); // rabbit.__proto__ 等于 animal
  9. alert( rabbit.eats ); // true

Rabbit.prototype = animal 这句话表示“在用 new Rabbit 创建一个兔子的时候,让它的 [[Prototype]] 属性赋值为 animal”。

下面是结构说明:

F.prototype - 图1

图片里,“prototype”是个横向箭头,表明是个常规属性,[[Prototype]] 是垂直的,表示 rabbit 继承自 animal

注意:**F.prototype** 仅在 **new F** 时被使用

F.prototype 仅在用 new F 形式调用时被使用,被用来赋值给新对象的 [[Prototype]] 属性。之后新对象与 F.prototype 之间再无联络。可以把它想象成是“one-time gift(一次性礼物)”。

如果使用 **F** 创建完一个对象后,改变 F.prototype 属性值(F.prototype = <another object>),那之后再用 new F 方式创建的新对象的 [[Prototype]] 属性值就是另一个对象了,之前已创建对象的 [[Prototype]] 属性保持原样。

默认的 F.prototype、constructor 属性

即使我们没有显式指定,每个函数都会有一个“prototype”属性。

默认的“prototype”属性是一个对象,仅包含唯一属性 constructor,这个属性的属性值又指向函数本身。

举个例子:

  1. function Rabbit() {}
  2. /*
  3. 默认的 prototype
  4. Rabbit.prototype = { constructor: Rabbit };
  5. */

F.prototype - 图2

我们试下:

  1. function Rabbit() {}
  2. // 默认
  3. // Rabbit.prototype = { contructor: Rabbit };
  4. alert( Rabbit.prototype.constructor === Rabbit ); // true

正常来说,如果我们什么都不做,所有的兔子(即 Rabbit 实例)都能通过 [[Prototype]] 访问 constructor 属性:

  1. function Rabbit() {}
  2. // 默认
  3. // Rabbit.prototype = { contructor: Rabbit };
  4. let rabbit = new Rabbit(); // 继承自 {constructor: Rabbit}
  5. alert( rabbit.constructor === Rabbit ); // true(从原型中获得)

F.prototype - 图3

我们可以使用 constructor 属性来创建一个新对象,本质上使用的就是同一个构造函数。

看下面:

  1. function Rabbit(name) {
  2. this.name = name;
  3. alert(name);
  4. }
  5. let rabbit = new Rabbit('White Rabbit');
  6. let rabbit2 = new rabbit.constructor('Black Rabbit')

当我们已有一个对象时,在不知道它是用哪个构造函数创建的情况下(例如,它来自于第三方库),使用这种方式创建另一个同类对象。

但使用“constructor”一个问题是:JavaScript 并不能保证“constructor”值一定是正确的

是,它默认是在函数的“prototype”属性里,但仅此而已。之后会发生什么,则完全取决于我们。

特别地,如果我们把默认的原型对象整个替换了,那么也就不会有这个“constructor”属性了。

  1. function Rabbit() {}
  2. Rabbit.prototype = {
  3. jumps: true
  4. };
  5. let rabbit = new Rabbit();
  6. console.log(rabbit.constructor === Rabbit); // false

所以,为了确保正确的“constructor”,我们可以直接操作默认的“prototype”属性,为这个对象添加/删除属性,而不是重写它:

  1. function Rabbit() {}
  2. // 不要重写 Rabbit.prototype
  3. // 只是在其基础上添加
  4. Rabbit.prototype.jumps = true
  5. // 这样,默认的 Rabbit.prototype.constructor 就还在

或者,手动添加 constructor 属性也行:

  1. Rabbit.prototype = {
  2. jumps: true,
  3. constructor: Rabbit
  4. };
  5. // 现在,constructor 属性也是正确的,因为我们手动添加了

总结

本章简要叙述了,为使用构造函数创建的对象设置 [[Prototype]] 的方法,后面我们将看到更高级的、依赖于它的编程模式。

本篇没有太复杂的内容,我们简单做下总结:

  • F.prototype 仅在用 new F 形式调用时被使用,被用来赋值给新对象的 [[Prototype]] 属性。
  • F.prototype 的值可能是个对象,也可以为 null。赋值其它类型值无效。
  • prototype”属性仅用在构造函数上使,才有如此特殊的效果,并且还要搭配 new 运算符来使用。

常规对象的 prototype 属性没有任何特别之处:

  1. let user = {
  2. name: 'John',
  3. prototype: 'Bla-bla' // 没有任何特别之处
  4. };

所有函数默认都有 F.prototype = { constructor: F },我们可以通过对象的“constructor”属性获得这个对象在创建时使用的构造函数。

(完)


📄 文档信息

🕘 更新时间:2020/01/14
🔗 原文链接:http://javascript.info/function-prototype