Javascript: переназначение функции с другой функцией - PullRequest
6 голосов
/ 26 октября 2010

Допустим, у меня есть эти две функции:

function fnChanger(fn) {
    fn = function() { sys.print('Changed!'); }
}
function foo() {
    sys.print('Unchanged');
}

Теперь, если я позвоню foo(), я увижу Unchanged, как и ожидалось.Однако, если я сначала позвоню fnChanger, я все равно вижу Unchanged:

fnChanger(foo);
foo(); //Unchanged

Теперь я предполагаю, что это потому, что foo не передается fnChanger по ссылке, но я могуошибаться.

Почему fnChanger не меняет foo на печать Changed!?
Кроме того, как я могу получить fnChanger для изменения foo без слишком сложного синтаксиса?

PS: я использую node.js для тестирования всего этого, так что вот откуда sys.print.

Ответы [ 2 ]

5 голосов
/ 26 октября 2010

Присвоение аргументу fn просто делает этот идентификатор указателем на анонимную функцию, foo во внешней области не затрагивается.

Когда вы передаете объект в качестве аргумента, можно сказать, что «ссылки передаются по значению». Присвоение просто заменяет местоположение, к которому относится идентификатор fn.

Вот как работает стратегия оценки в JavaScript.

Непосредственно перед присваиванием в функциях fnChanger два идентификатора, глобальный аргумент foo и fn, указывают на один и тот же объект функции:

                ---------------------------------------------
    foo ----->  |function foo { sys.print('Un changed!'); } |
                ---------------------------------------------
                   ^
                   |
    fn -------------

После назначения fn просто укажет на новую функцию:

                ---------------------------------------------
    foo ----->  | function foo { sys.print('Unchanged!'); } |
                ---------------------------------------------

                ---------------------------------------
    fn ------>  | function { sys.print('Changed!'); } |
                ---------------------------------------

Как ты мог это изменить?

Хорошо, если предположить, что foo является функцией в глобальной области видимости, вы можете сделать что-то вроде этого:

function fnChanger(obj, name) {
    obj[name] = function() { sys.print('Changed!'); };
}

function foo() {
    sys.print('Unchanged');
}

fnChanger(this, 'foo');
foo(); // Changed!

Вышеописанное будет работать, потому что в функции fnChanger нам требуется базовый объект и имя свойства , функции, объявленные в глобальном контексте выполнения, связаны как свойства Глобальный объект , поэтому мы можем переназначить его значение таким образом.

Строка fnChanger(this, 'foo'); должна выполняться также в глобальной области видимости, ей будет передано значение this (которое относится к глобальному объекту в этой области) и имя свойства, позволяющее сделать присвоение GlobalObject.foo идентификатор.

Если бы этот код был внутри функции, мы никак не могли бы получить базовый объект , потому что в этом «контексте выполнения кода функции» объявления функций (также объявления переменных и формальные параметры функции) связаны как свойства недоступного объекта , называемого Переменным Объектом (цепочка этих Переменных Объектов, образует Цепь Области), и если бы это было так, единственным обходным решением было бы использование eval.

Дополнительная информация:

3 голосов
/ 26 октября 2010

Как указывало @CMS, вы не можете назначить его внутри функции из-за области действия. Однако вы можете переназначить его так:

var fnChanger = function() {
  return function() {
      alert('changed!');
  }
}

var foo = function() {
  alert('Unchanged');
}

foo = fnChanger();
foo();

пример

...