web-dev-qa-db-ja.com

コンストラクター関数をプロトタイプから変更できないのはなぜですか?

そのような例があります。

_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();
_

_var jumps_がパブリックになるように、Rabbit()のコードを変更したい。私はこのようにします:

_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
_

コンストラクター関数のコードをこの方法で変更できないのはなぜですか?

49
Green

_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演算子を自分で実装することです。 (Crockfordは幸せになり、新しいことはありません;)

_// `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;
}
_

上記のコードでは、コンストラクタープロパティをfixする必要があることがわかります。これは、インスタンスのみがある場合にオブジェクトのインスタンス化に使用されることがあるためです。しかし、実際のコンストラクターには影響しません。 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
_
77
Juan Mendes

これは、コンストラクター関数からではなく、リテラルからオブジェクトを作成する素晴らしい回避策です。

まず、コンストラクターの単なるローカル変数ではなく、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オブジェクトを作成すると、コンストラクターで定義されたとおり、最初は「yes」になりますよね?

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

できることは、デフォルトのRabbitオブジェクトからRabbitを作成することです。具体的なウサギは、具体的なウサギオブジェクトの値を変更していない限り、オンザフライで変更しても、デフォルトの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
0
Hrvoje Golcic

以下をお試しください

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

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