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) => { ...
Тем не менее, вы все еще должны знать об этом.