Подтвердить что ты не робот

Почему невозможно изменить конструкторскую функцию из прототипа?

У меня есть такой пример.

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

Почему невозможно изменить код в функции конструктора таким образом?

4b9b3361

Ответ 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
});

Ответ 2

Попробуйте следующее:

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

var rabbit2 = new Rabbit();             // create new object with new constructor
alert(rabbit2.jumps); 

Вместо того, чтобы устанавливать конструктор, вы хотите установить прототип Object для новой функции, таким образом, когда объект создается, он запускает прототип, устанавливая this.jumps на no, переопределяя набор значений по предыдущему конструктору.

Здесь мой jsfiddle: http://jsfiddle.net/ZtNSV/9/

Ответ 3

Попробуйте выполнить

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

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

Ответ 4

Это замечательное обходное решение, создающее объект из литерала, а не из конструкторской функции.

Во-первых, если вы хотите, чтобы член 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 по умолчанию. Бетонные кролики всегда будут иметь значение по умолчанию от объекта 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