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

Javascript переопределяет и переопределяет существующий корпус функции

Мне интересно, можем ли мы поменять тело функции после его построения?

     var O = function(someValue){
           this.hello = function(){
                return "hello, " + someValue;
           }
     }

     O.prototype.hello = function(){
           return "hhhhhhh";
     }

     var i = new O("chris");
     i.hello();   // -> this still returns the old definition "hello, chris"

Оператор javascript O.prototype.hello = function(){....} не переопределяет и не переопределяет поведение функции hello. Почему это? Я знаю, что у него будет ошибка типа, если вы попытаетесь повторно использовать параметр someValue.

      // this will fail since it can't find the parameter 'someValue'
      O.prototype.hello = function(){
             return "aloha, " + someValue;
      } 

Мне интересно, почему он позволяет добавлять функцию во время выполнения, например

      O.prototype.newFunction = function(){
           return "this is a new function";
      }

      i.newFunction();   //  print 'this is a new function' with no problem.

но не позволяет вам изменять определение после его определения. Я сделал что-то не так? как мы переопределяем и переопределяем функцию внутри класса? и есть ли способ повторно использовать параметр, который мы передали ранее, чтобы создать объект? в этом случае мы можем повторно использовать someValue, если хотим расширить к нему больше функций.

4b9b3361

Ответ 1

Когда вы используете new, значение this внутри конструктора указывает на вновь созданный объект (для получения дополнительной информации о том, как работает new, посмотрите этот ответ и этот ответ). Таким образом, ваш новый экземпляр i имеет функцию hello. Когда вы пытаетесь получить доступ к свойству объекта, он приближается к цепочке прототипов, пока не найдет ее. Поскольку hello существует на экземпляре объекта, нет необходимости перемещаться по цепочке прототипов, чтобы получить доступ к версии hello, которая возвращает hhhhhhhh. В некотором смысле вы переопределили реализацию по умолчанию в своем экземпляре.

Это поведение можно увидеть, если вы не назначили hello to this внутри вашего конструктора:

var O = function(someValue) {

 }

 O.prototype.hello = function(){
       return "hhhhhhh";
 }

 var i = new O("chris");
 console.log(i.hello()); //this prints out hhhhhhh

То, что вы делаете, отчасти обратное. Прототип в основном предоставляет форму "по умолчанию", которую вы можете переопределить для каждого экземпляра. Форма по умолчанию используется только в том случае, если свойство, которое вы ищете, не может быть найдено на объекте. Это значит, что JavaScript запустит цепочку прототипов, чтобы узнать, может ли она найти свойство, которое соответствует тому, что вы ищете. Он находит это, он будет использовать это. В противном случае он вернет undefined.

Что вы в основном имеете в первом случае:

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (returns "hello, chris")

Итак, когда вы делаете i.hello, JavaScript видит, что в i есть свойство hello и использует это. Теперь, если вы явно не определили свойство hello, вы в основном имеете следующее:

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (is "undefined", so JavaScript will walk up the chain until 
                   it sees O.prototype.hello, which does have a defined value 
                   it can use.)

Это означает, что вы можете обеспечить реализацию по умолчанию в прототипе, а затем переопределить его (в некотором смысле, это похоже на подкласс). То, что вы также можете сделать, это изменить поведение на основе каждого экземпляра, напрямую изменяя экземпляр. Версия hello, которая у вас есть на прототипе, выглядит как отказоустойчивая и откатная.

РЕДАКТИРОВАТЬ: Ответы на ваши вопросы:

Переопределение по каждому экземпляру означает, что вы присоединяете свойство или функцию к определенному экземпляру. Например, вы можете сделать:

i.goodbye = function() {
    return "Goodbye, cruel world!";
};

Это означает, что это поведение специфично для этого конкретного экземпляра (т.е. только для i, а не для любых других экземпляров, которые вы, возможно, создали).

Если вы выберете this, то у вас есть:

hello = function() {
    return "hello, " + someValue;
}

Что эквивалентно выполнению:

window.hello = function() {
    return "hello, " + someValue;
}

Итак, в этом случае hello является глобальной ссылкой на эту функцию. Это означает, что hello не привязан ни к одному из ваших объектов.

hello может быть undefined, если у вас нет this.hello = function() { .... }; внутри вашего конструктора. Я также говорил об общем процессе, который использует JavaScript, чтобы попытаться разрешить свойства объектов. Как я упоминал ранее, он включает в себя продвижение цепи прототипов.

Ответ 2

Когда вы создаете экземпляр объекта O, используя new O("somename");, вы назначаете метод экземпляра только что созданному объекту. Когда вы затем назначаете другой метод с тем же именем O prototype, метод уже затенен методом экземпляра. Итак:

Object.prototype.hello // undefined
       |
       O.prototype.hello // alternate function
         |
         i.hello // original function provided in constructor

JavaScript начинается в нижней части цепочки, а останавливается, когда находит совпадение имени. Таким образом, он останавливается на i.hello и никогда не видит O.prototype.hello.

JavaScript (как на ECMAScript 5) действительно не (насколько мне известно) дает вам хороший способ делать частные переменные, подобные тем, к которым можно получить доступ посредством методов экземпляра, добавленных после определения (либо добавленных в экземпляр, либо на prototype). Закрытие дает вам большую часть пути, но если вы хотите иметь возможность добавлять методы за пределами закрытия, которые имеют доступ к переменным закрытия, вам нужно выставить методы get и/или set, которые дают этим новым методам доступ к замыкающие переменные:

// Possibility #1 - marked as private member
var O = function(someValue) {
    this._someValue = someValue;
};
O.prototype.hello = function() { return "hhhh"; };

var i = new O("somename");
i.hello = function() { return "aloha," + this._someValue; };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #2 - factory function + closure with get and set methods
var OMaker = function(someValue) {
    var realO = function() {};
    realO.prototype.getSomeValue = function() { return someValue; };
    realO.prototype.setSomeValue = function(newVal) { someValue = newVal; };
    realO.prototype.hello = function() { return "hhhh"; };
    return realO;
};
var O = OMaker("somename"),
            i = new O();
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #3 - eschew prototype inheritance and create new objects
var O = function(someValue) {
    return {
        getValue: function() { return someValue; },
        setValue: function(newValue) { someValue = newValue; },
        hello: function() { return "hhhh"; }
    };
};
var i = O(); // Note the lack of the "new" keyword
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

Вы действительно захотите прочитать bobince отличный ответ на ООП в JavaScript для получения дополнительной информации по этому вопросу.

Ответ 3

Нет. Вы не можете, но вот хороший пример способов ограничения этого ограничения путем структурирования вашего наследования по-другому.

Методы переопределения JavaScript

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

Ответ 4

Во-первых, вам нужно понять прототипное наследование.

Когда вы создаете объект с помощью O в качестве конструктора, это происходит:

  • 1st, создается новый объект.
  • 2nd, свойство hello этого объекта устанавливается в функцию (через конструктор, который вы определили).
  • 3rd, создается секретная ссылка с объекта, указывающая на объект O.prototype.

При ссылке на свойства объектов O эти свойства сначала рассматриваются в самом объекте. Только если объект не имеет самого свойства, он ищет его прототип.

Во-вторых, вам нужно понять закрытие.

someValue - это переменная (не свойство), определенная в функции O. Доступ к нему можно получить только из других вещей, которые также определены в одной и той же функции (или любых функций, определенных внутри функции O). Итак, мы говорим: "someValue было закрыто". К нему нельзя получить доступ к функции, которую вы определяете вне O.


Чтобы достичь того, чего вы хотите, вам нужно либо установить someValue в свойство (что делает его менее похожим на вещь private и больше похоже на вещь public). Или вам необходимо определить все функции, которые нуждаются в доступе к someValue внутри исходного определения O.

Чтобы изменить то, что указывает i.hello после создания i, вам необходимо напрямую установить свойство объекта.

i.hello = function () { /* stuff */ };

Ответ 5

Если я правильно помню, функции, являющиеся непосредственными элементами объектов, имеют приоритет над подобными именами этого прототипа объекта. Следовательно, O.prototype.hello узурпируется O.hello, даже если первая определяется позже в коде.

Причина someValue недоступна для вашего O.prototype.hello, потому что область действия someValue ограничена функцией конструктора и любыми функциями, определенными или выполняемыми внутри нее. Поскольку O.prototype.hello определяется вне области действия конструктора O, он не знает о someValue

Ответ 6

Это связано с тем, что при доступе к объекту объекта JavaScript сначала проверяет свойства объектов, прежде чем входить в его прототип.

Это аналогично производному классу Java, переопределяющему функциональность базового класса.

Для лучшего понимания проверьте пример кода в Наследование свойств

Также обратите внимание, что someValue в вашем случае является локальным для функции-конструктора. Если вам это нужно в других функциях, вы должны назначить его this.someValue внутри конструктора.

Вы можете переопределить функцию hello для определенного объекта здесь следующим образом. Но не для всего класса.

i.hello = function(){ console.log('even here someValue is not accessible');};

  var O = function(someValue){
       this.someValue = someValue;
       this.hello = function(){
            return "hello, " + someValue;
       }
 }

 var i = new O("chris");
 console.log(i.hello()); // prints hello, chris
 i.hello = function() { 
   return 'Hi there '+ this.someValue;
 }
 console.log(i.hello()); // prints Hi there chris

 var test = new O('Sujay')
 console.log(test.hello()) // this still prints hello, Sujay

Обратите внимание, что здесь мы не изменили конструктор, и, следовательно, это не будет работать с другими экземплярами, такими как test в приведенном выше примере.

Лучший способ сделать это - определить функции только в прототипе, а не в конструкторе, например, следующий фрагмент.

 var O = function(someValue){
      this.someValue = someValue;
 };
 O.prototype.hello = function(){
            return "hello, " + this.someValue;
 };

 var i = new O("chris");
 console.log(i.hello()); // prints hello, chris
 O.prototype.hello = function() { 
   return 'Hi there '+ this.someValue;
 }
 console.log(i.hello()); // prints Hi there chris

 var test = new O('Sujay')
 console.log(test.hello()) // prints Hi there Sujay

Ответ 7

Когда вы получаете доступ к свойству, система сначала ищет его в экземпляре. Если он не найден, он ищет его в прототипе. Вот почему this.hello используется, а не O.prototype.hello.

Если вы хотите переопределить реализацию hello, вам нужно будет использовать наследование JavaScript. Вот пример:

var A = function(){
    console.log("A is getting constructed");
};

A.prototype.constructor = A;
A.prototype.someValue = 1;
A.prototype.hello = function() {
    console.log("A.hello(): " + this.someValue);
};

var B = function(){
    //Constructor of A is automatically called before B's

    console.log("B is getting constructed");
};
B.prototype = new A; //Inherit from A
B.prototype.constructor = B;
B.prototype.hello = function() {
    console.log("B.hello() called");
    console.log("Calling base class method");
    A.prototype.hello.call(this);
};

var a = new A();
a.hello();

var b = new B();
b.hello();