Как ссылаться на функцию из метода класса Javascript? - PullRequest
5 голосов
/ 03 января 2011

Я использую SWFAddress для глубоких ссылок на мой сайт ( ссылка на SWFAddress ). Мне нравится разбивать код на классы, поэтому у меня есть основная структура, подобная этой:

function SomeClass() {
    //this adds the this.handleChange() function to the
    //SWFAddress event listener
    this.initializeSWFA = function() {
        //SWFAddress variable is instantiated in SWFAddress javascript file
        //so I can use it here
        SWFAddress.addEventListener(SWFAddressEvent.CHANGE, this.handleChange);
    }

    //SWFAddress suppose to call this function
    this.handleChange= function(evt) {
    //some code here
    }

}

//instantiate the SomeClass
var someVar = new SomeClass();
someVar.initializeSWFA();

Эта строка здесь не работает:

SWFAddress.addEventListener(SWFAddressEvent.CHANGE, this.handleChange);

Я попытался изменить его на:

SWFAddress.addEventListener(SWFAddressEvent.CHANGE, this.handleChange());

или

var self = this;
SWFAddress.addEventListener(SWFAddressEvent.CHANGE, self.handleChange);

и они тоже не работают.

Так как же ссылаться на функцию javascript из класса в такой ситуации? Если бы функция handleChange была бы вне класса, я мог бы написать имя функции.

Заранее спасибо

EDIT

Во-первых, спасибо за все ответы. Я все еще пытаюсь понять, как все это работает в Javascript. Я не привык к объектно-ориентированной модели, как здесь, в Javascript.

Это решение на данный момент. Я до сих пор не могу понять, как сделать это красиво в Javascript, но это решение работает. Я попытался реализовать решение, предложенное ehudokai (спасибо), однако не смог заставить его работать.

function SomeClass() {
    //this adds the this.handleChange() function to the
    //SWFAddress event listener
    this.initializeSWFA = function() {
        //SWFAddress variable is instantiated in SWFAddress javascript file
        //so I can use it here
        SWFAddress.addEventListener(SWFAddressEvent.CHANGE, someFunc);
    }

    //SWFAddress suppose to call this function
    this.handleChange= function(evt) {
    //some code here
    }

}

//instantiate the SomeClass
var someVar = new SomeClass();

function someFunc(evt) {
    someVar.handleChange(evt);
}

someVar.initializeSWFA();

Мне не нравится это, потому что это включает определение одной дополнительной функции, поэтому занимает дополнительное место. Если кто-нибудь выяснит, как добавить метод в SWFAddress EventListener из объекта Javascript, пожалуйста, помогите мне.

Ответы [ 3 ]

7 голосов
/ 04 января 2011

Ваша структура класса полностью действительна. Однако, если ваша функция handleChange() использует ключевое слово this, ожидая someVar, то в этом и заключается ваша проблема.

Вот что происходит:

  1. SWFAddress.addEventListener(SWFAddressEvent.CHANGE, this.handleChange); правильно ссылается на функцию-обработчик в классе. SWFAddress кэширует эту функцию до некоторой переменной f до тех пор, пока событие не будет отправлено.
  2. Когда событие отправляется, SWFAddress вызывает f. Хотя ссылка на функцию сохраняется, ссылка на контекст, или this, - нет. Поэтому this по умолчанию window.

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

function SomeClass() {
    this.initializeSWFA = function() {
        // Save a reference to the object here
        var me = this;

        // Wrap handler in anonymous function
        SWFAddress.addEventListener(SWFAddressEvent.CHANGE, function (evt) {
            me.handleChange(evt);
        });
    }

    //SWFAddress suppose to call this function
    this.handleChange= function(evt) {
    //some code here
    }

}

Объяснение this, запрошенное ФП:

Ключевое слово this можно объяснить по-разному: сначала прочитайте эту статью о области действия , а затем эту статью об объектно-ориентированном javascript .

Я хотел бы также привести мои быстрые рассуждения, которые вы можете найти полезными. Помните, что в Javascript нет «классов», как в таких языках, как Java. В этих языках «метод» класса принадлежит только этому классу (или может быть унаследован). В javascript, однако, есть только объекты и свойства объекта, которые могут иметь место для функций. Эти функции являются свободными агентами - они не принадлежат тому или иному объекту, как строки или числа. Например:

var a = {
    myMethod: function () {...}
};

var b = {};
b.myMethod = a.myMethod;

В таком случае, какому объекту принадлежит myMethod? Ответа нет, это может быть a или b. Следовательно, a.myMethod - это просто ссылка на функцию, не связанную с «контекстом» или родительским объектом. Следовательно, this не имеет смысла, если только не вызывается , явно используя a.myMethod() или b.myMethod(), и, следовательно, по умолчанию window при вызове любым другим способом. По той же причине, по которой в javascript отсутствует ключевое слово parent или super.

1 голос
/ 03 января 2011

Я думаю, что лучше всего перестать думать о SomeClass как о классе.

Javascript не имеет классов. Он использует прототип наследования. Который я не буду углубляться в это здесь. По сути, это означает, что у вас просто есть предметы. И если вы хотите создать другой объект, такой как первый, вы используете для этого атрибут Object.prototype.

Так что, если вы хотите делать то, что вы пытаетесь. (при условии, что SWFAddress работает как большинство javascript), выполните следующее.

var someVar = {  // (using object notation)
    initializeSWFA : function(){
        SWFAddress.addEventListener(SWFAddressEvent.CHANG, this.handleChange);
    },
    handleChange : function(){
        // your code here
    }
}

на данный момент вы можете вызвать someVar напрямую

someVar.initializeSWFA();

Если вместо этого вы хотите создать объект, от которого вы можете наследовать другие объекты, попробуйте следующее.

var baseObject = function(){};
baseObject.prototype.handleChange = function(){
    // your code here
}
baseObject.prototype.initializeSWFA = function(){
    SWFAddress.addEventListener(SWFAddressEvent.CHANGE, this.handleChange);
}

var someVar = new baseObject();
someVar.initializeSWFA();

оператор new просто создает новый объект, который наследует .prototype указанного объекта.

Надеюсь, это поможет.

1 голос
/ 03 января 2011

Боюсь, я не могу точно определить вашу проблему, но я бы сказал, что это как-то связано с вашим использованием SWFAddress.То, как вы структурируете SomeClass и его методы, полностью допустимо, вопреки тому, что здесь говорится в некоторых других ответах.

Дальнейшее разъяснение вопроса Мики (теперь удаленного) из другого ответа относительно того, какthis работает:

Есть два случая, как this ведет себя;обычные функции и функции, вызываемые с помощью new -оператора.

Для регулярных вызовов функций this относится к объекту слева от точки (условно говоря): например, при написании someVar.initializeSWFA(), this будет ссылаться на someVar.При вызове функции без точки this будет ссылаться на глобальный объект.Функция может быть назначена от одного объекта к другому, и, следовательно, this будет смещено, например так:

var someVar = new SomeClass();
var anotherVar = {};
anotherVar.aNewNameForTheSameFunction = someVar.initializeSWFA;
anotherVar.aNewNameForTheSameFunction(); // inside of this call to initializeSWFA, "this" will be "anotherVar"

Тогда у нас также есть случай, когда вызывается оператором new.В этом случае new создаст для вас новый объект, а this будет ссылаться на этот объект.

Существуют также способы вызова функции с явно установленным this, используя call -функция:

var someVar = new SomeClass();
var aNumber = 5;
someVar.initializeSWFA.call(aNumber, arg1, arg2, ..., argN);

Это вызовет функцию initializeSWFA с aNumber как this, а также передаст ей параметры arg1, arg2 и т. Д. Обратите внимание, что someVarв этом случае вычеркнуто из уравнения.

...