【问题标题】:Why is it impossible to change constructor function from prototype?为什么无法从原型更改构造函数?
【发布时间】:2012-03-05 05:55:09
【问题描述】:

我有这样的例子。

function Rabbit() {
    var jumps = "yes";
};
var rabbit = new Rabbit();
alert(rabbit.jumps);                    // undefined
alert(Rabbit.prototype.constructor);    // outputs exactly the code of the function Rabbit();

我想更改Rabbit() 中的代码,以便var jumps 公开。我是这样做的:

Rabbit.prototype.constructor = function Rabbit() {
    this.jumps = "no";
};
alert(Rabbit.prototype.constructor);    // again outputs the code of function Rabbit() and with new this.jumps = "no";
var rabbit2 = new Rabbit();             // create new object with new constructor
alert(rabbit2.jumps);                   // but still outputs undefined

为什么不能以这种方式更改构造函数中的代码?

【问题讨论】:

  • 您的第二个代码显示 Rabbit 不会跳转,其计算结果为 false,因此出现错误 - 尝试 this.jumps = "yes"
  • @wheresrhys 所有非空字符串(即长度大于零的字符串)在 JavaScript 中计算为 true;甚至"false",更是如此,然后,"no" :)

标签: javascript oop prototype


【解决方案1】:

您不能通过重新分配给prototype.constructor来更改构造函数

发生的事情是Rabbit.prototype.constructor 是指向原始构造函数(function Rabbit(){...})的指针,因此“类”的用户可以从实例中检测构造函数。因此,当您尝试这样做时:

Rabbit.prototype.constructor = function Rabbit() {
    this.jumps = "no";
};

您只会影响依赖prototype.constructor 从实例动态实例化对象的代码。

调用new X时,JS引擎没有引用X.prototype.constructor,它使用X作为构造函数,X.prototype作为新创建对象的原型,忽略X.prototype.constructor

解释这一点的一个好方法是自己实现new 运算符。 (克罗克福德会很高兴,不再是新的;)

// `new` emulator
// 
// Doesn't reference `.constructor` to show that prototype.constructor is not used
// when istantiating objects a la `new`
function make(ctorFun, argsArray) {
  // New instance attached to the prototype but the constructor
  // hasn't been called on it.
  const newInstance = Object.create(ctorFun.prototype);
  ctorFun.apply(newInstance, argsArray);
  return newInstance;
}

// If you create a utility function to create from instance, then it uses the
// inherited `constructor` property and your change would affect that.
function makeFromInstance(instance, argsArray) {
  return make(instance.constructor, argsArray);
}

function X(jumps) {
  this.jumps = jumps;
}

// Flip the constructor, see what it affects
X.prototype.constructor = function(jumps) {
  this.jumps = !jumps;
}

const xFromConstructorIsGood = make(X, [true]);
const xFromInstanceIsBad = makeFromInstance(xFromConstructorIsGood, [true]);

console.log({
  xFromConstructorIsGood,
  xFromInstanceIsBad
});

JS 中的继承

有助于 JS 继承的库实现了继承,并且确实依赖于 prototype.constructor,其精神如下:

function extend(base, sub) {

  function surrogateCtor() {}
  // Copy the prototype from the base to setup inheritance
  surrogateCtor.prototype = base.prototype;
  sub.prototype = new surrogateCtor();
  // The constructor property is set to the base constructor
  // with the above trick, let's fix it
  sub.prototype.constructor = sub;
}

您可以看到,在上面的代码中,我们必须修复构造函数属性,因为它有时用于在您只有一个实例时创建实例化对象。但它不会影响实际的构造函数。看我关于JS继承的帖子http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

如何重新定义构造函数 如果你真的想重新定义一个构造函数,就这样做

// If Rabbit had any custom properties on it 
// (or static properties as some call it), they would not be copied, you'd have to do that manually using getOwnPropertyNames

// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
var oldProto = Rabbit.prototype;
Rabbit = function() {...};
Rabbit.prototype = oldProto;

请注意,这不会影响已复制该引用的代码,例如:

const myRefRabbit = Rabbit

【讨论】:

  • 是的,prototype.constructor 不会影响任何事情,真的。
  • 除了这不会复制静态函数吗?检查类时,静态函数显示在Rabbit 下而不是Rabbit.prototype 下。
  • @katspaugh 抱歉回复晚了。它确实会影响依赖prototype.constructor 从实例中检测构造函数的代码,如“JS 中的继承”部分中所述。不过我知道你的意思,它不会影响核心语言中的任何内容。
  • @tonix 不,你不能。您无法更改其他人对已检索到的Rabbit 的引用。
  • @tonix 很可能,您应该先尝试一下,然后再问一个单独的问题,因为您并不真正关心更改构造函数。
【解决方案2】:

试试下面的

function Rabbit() {
  this.jumps = "no";
};

var rabbit = new Rabbit();
alert(rabbit.jumps);  // Prints "no"

【讨论】:

  • 如果你解释为什么在原型中设置jumps 不起作用,我会给这个答案+1
  • 是的,谢谢,我知道我可以改变原来的功能。但我想知道是否有可能从原型改变它。
【解决方案3】:

这是从文字而不是构造函数创建对象的绝妙解决方法。

首先,如果您希望jumps 成员包含在对象中,而不仅仅是构造函数中的局部变量,那么您需要this 关键字。

function Rabbit() {
    this.jumps = "yes";
};

var rabbit = new Rabbit();
alert(rabbit.jumps);                    // not undefined anymore

现在您可以轻松地以您想要的方式公开访问jumps

rabbit.jumps = 'no';
alert(rabbit.jumps);                    // outputs 'no' 

但是,如果您创建另一个 Rabbit 对象,它最初将在构造函数中定义为“是”,对吗?

var rabbit2 = new Rabbit();
alert(rabbit.jumps);                     // outputs 'no' from before
alert(rabbit2.jumps);                    // outputs 'yes'

你可以做的是从一些默认的兔子对象创建一个兔子。混凝土兔子将始终具有默认 Rabbit 对象的默认值,即使您即时更改它,除非您更改了混凝土兔子对象(实现)中的值。这与@Juan Mendes 的解决方案不同,这可能是最好的,但它可以打开另一个观点。

Rabbit = {jumps : 'yes'};    // default object

rabbit = Object.create(Rabbit);
Rabbit.jumps = 'no';
rabbit2 = Object.create(Rabbit);

console.log(rabbit.jumps);   // outputs "no" - from default object
console.log(rabbit2.jumps);  // outputs "no" - from default object

// but...
rabbit.jumps = 'yes';
Rabbit.jumps = 'unknown';

console.log(rabbit.jumps);   // outputs "yes" - from concrete object
console.log(rabbit2.jumps);  // outputs "unknown" - from default object

【讨论】:

    猜你喜欢
    • 2015-03-16
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    • 2016-01-07
    相关资源
    最近更新 更多