JavaScript - 意外/奇怪的构造函数

JavaScript - unexpected/strange constructor

本文关键字:构造函数 意外 JavaScript      更新时间:2023-10-01
var ninja = {
    name: 'Ninja',
    say: function () {
        return 'I am a ' + this.name;
    }
};
function F(){
}
F.prototype = ninja;
var ninja2 = new F();
ninja2.name;
Output >>> "Ninja"
ninja2.constructor === F
Output >>> false
ninja2.constructor
Output >>> function Object()
ninja2.__proto__ === ninja
Output >>> true

在此示例中,为什么不F ninja2
的构造函数(正如人们所期望的那样(?!

我期待这一点,因为...下一个示例打印Hero

function Hero(){ } 
var h1 = new Hero();
h1.constructor
Output >>> function Hero()

概念上有什么区别?

我认为这与我在第一个示例中明确设置F.prototype = ninja;的事实有关。但它与此有什么关系?我对此感到困惑。F不是ninja2的构造函数吗?毕竟new F()被用来创造ninja2

constructor 属性没有魔力,它只是创建函数对象时自动添加的属性:

13.2 创建函数对象

  • 创建一个新的本机 ECMAScript 对象,并让 F 成为该对象。
  • proto 是创建一个新对象的结果,该对象将由表达式构造 new Object()其中Object是具有该名称的标准内置构造函数。
  • 调用 [[DefineOwnProperty]] 带有参数 "constructor " 的 proto 内部方法, 属性描述符 {[[Value]]: F, { [[可写]]: , [[可枚举]]: , [[可配置]]: },和
  • 用参数"prototype"调用 F 的 [[DefineOwnProperty]] 内部方法, 属性描述符 {[[Value]]: proto, { [[可写]]: true, [[Enumerable]]: false, [[Configurable]]: 假},和

例:

function F(){}
F.prototype.constructor; // F

因此,当您用另一个对象覆盖F.prototype时,您将丢失 constructor 属性。

然后,当您使用 ninja2.constructor 时,您会得到Object,因为:

  1. ninja2没有自己的constructor财产。
  2. ninja2继承自ninja,而没有自己的constructor属性。
  3. ninja继承自 Object.prototype ,后者将自己的 constructor 属性设置为 Object

要解决此问题,您可以

  • F.prototype中恢复constructor

    function F(){}
    F.prototype = ninja;
    F.prototype.constructor = F;
    
  • ninja中包含constructor

    var ninja = {
        constructor: F, /* ... */
    };
    
  • 将所需的属性添加到F.prototype,而不是将其替换为另一个对象:

    Object.assign(F.prototype, ninja); /* Requires ECMAScript 6 */
    

你声明"F"如下:

function F(){}

这将创建一个函数对象"F",其原型如下所示:

F.prototype = { constructor: F };

默认情况下,您创建的"ninja"对象具有 Object(( 构造函数。当您将 F.prototype 替换为 "ninja" 对象时,以下代码行现在使用 "ninja" 中的 Object(( 构造函数,因为您覆盖了 F 的原型:

var ninja2 = new F();