Javascript изменить общедоступные свойства из частных функций

Я кодировал класс в JavaScript, и я пытаюсь изменить одно из моих общедоступных свойств в частной функции.

Вот пример моего кода:

MyClass = function(callback){

    this.tabs = [];
    var tabs_callback = function(){};

    this.setTabsCallback = function(callback) {
        tabs_callback = callback;
    }

    var _doStuff = function(callback) {

        // doing stuff

        this.tabs = [1, 2, 3];

        console.log("Inside doStuff");
        console.log(this.tabs);

        tabs_callback();
    }

    this.doStuff = function() {
        _doStuff();
    }
}


var myObject = new MyClass();

myObject.setTabsCallback(function () {

    console.log("Inside callback");
    console.log(myObject.tabs);
});

myObject.doStuff();

И вот что я получаю в консоли:

Inside doStuff
[ 1, 2, 3 ]
Inside callback
[]

Почему я не могу увидеть мою модификацию из функции обратного вызова?

4 ответа

Решение

Так как _doStuff определяется как функция, она имеет свою собственную this контекст, поэтому вы не можете получить доступ к this.tabs что вы определили вне частной функции.

Есть два простых способа решить эту проблему.

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

...
var that = this;
var _doStuff = function(callback) {
    // inside _doStuff, use "that" instead of "this"
...

Или, во-вторых, вы могли бы вместо этого использовать функцию стрелки. Функции стрелок являются относительно новым дополнением к Javascript. Они не создают отдельный контекст со своим собственным this ссылка, поэтому, когда вы используете this объект будет по-прежнему ссылаться на тот же объект, что и вне функции, поэтому ваша функция будет работать правильно:

....
var _doStuff = (callback) => {
    // now _doStuff doesn't create a new "this" object, so you can still access the one from outside
...

Когда _doStuf вызывается через функцию как

this.doStuff = function() {
                 _doStuff();
               }

_doStuff привязан к глобальному объекту. Следовательно, вы должны либо связать это определение с this или использовать .call(this) при вызове _doStuff функция. Так что изменение вашего кода следующим образом поможет;

MyClass = function(callback){

    this.tabs = [];
    var tabs_callback = function(){};

    this.setTabsCallback = function(callback) {
        tabs_callback = callback;
    }

    var _doStuff = function(callback) {

        // doing stuff

        this.tabs = [1, 2, 3];

        console.log("Inside doStuff");
        console.log(this.tabs);

        tabs_callback();
    } // <<< you can either use .bind(this) here 

    this.doStuff = function() {
        _doStuff.call(this);  // <<< or use .call(this) here to bind _doStuff to the this
    }
}


var myObject = new MyClass();

myObject.setTabsCallback(function () {

    console.log("Inside callback");
    console.log(myObject.tabs);
});

myObject.doStuff();

По умолчанию JS не использует лексическую привязку контекста, а когда вы вызываете обычные функции, this по умолчанию window (или же null если вы находитесь в строгом режиме).

Есть много способов избежать этого: вы можете сохранить this к переменной, как указано выше. Это будет работать в 100% случаев.

Также вы можете явно связать _doStuff с контекстом, используя Function#bind метод: сделать что-то вроде _doStuff = _doStuff.bind(this) после его инициализации или вы можете назвать его как _doStuff.bind(this)(), IE до IE9 (и, может быть, некоторые другие браузеры, я не совсем уверен) не поддерживают bind,

В конце вы можете использовать функцию стрелки, как это также упоминалось выше: они используют лексическое связывание и this будет таким же, как вы ожидаете. Функции со стрелками доступны с ES6, но, я полагаю, если вы создаете конструкторы, подобные этим, это не ES6.

Смотрите это ключевое слово. Использование функций со стрелками - хороший способ предотвратить многие из этих проблем:

var _doStuff = (callback) => { ...

Тем не менее, вы все еще должны знать об этом.

Другие вопросы по тегам