UZER2006
@UZER2006

Неправильный контекст выполнения функции?

Передаю в конструктор объекта (пусть он будет вторым) две функции, являющиеся методами другого, ранее существующего, объекта (пусть он будет первым). Конструктор сохраняет полученные параметры как свойства новосоздаваемого объекта. Потом в процессе работы некая функция второго объекта дёргает те две функции, что были переданы как аргументы, через обращение к свойствам второго объекта. В результате функции выполняются в контексте второго объекта.

В коде выглядит где-то вот так:
var A2 = function(params){
	this.f1 = params.f1;
	this.f2 = params.f2;
}
A2.prototype.f3 = function(){
	//этот метод дёргается извне, не буду углубляться в детали, факт в том, что здесь контекст – экземпляр А2
	if (this.f1)
		this.f1();
}
a1 = {
	ff1:function(){
		//this ссылается на экземпляр A2, из которого была вызвана функция
	},
	ff2:function(){

	}
	init:function(){
		this.a2 = new A2({f1:this.ff1,f2:this.ff2});
	}
}
a1.init();
//дальше предположим, что какой-то внешний раздражитель дёргает a1.a2.f3() в контексте a2 (на самом деле, контекст – элемент DOM, но в нём я сохранил ссылку на a2 в конструкторе A2, и в listener'е на событие по этому DOM-элементу вытаскиваю a2 и дёргаю его метод)


Я же ожидаю получить внутри ff1 контекст a1.

Что я делаю не так? Это правильное поведение или что-то не так работает? Как правильно дёрнуть методы a1, чтобы они вызывались в его контексте? Как-то меня не впечатляет идея в конструктор ещё как-то передавать this и выполнять те функции через apply. Может, существуют правильнее и красивее решения?
  • Вопрос задан
  • 3282 просмотра
Решения вопроса 1
Это правильное поведение. Контекст — то, что перед последней точкой. Нужен другой — в любом случае придется где-то привязывать. Или как написано выше, или так:
this.a2 = new A2({f1:this.ff1.bind(this),f2:this.ff2.bind(this)});

Можно упростить, написав функцию bindAll:
function bindAll(obj,ctx){
  if(arguments.length<2)ctx=obj;
  for(var key in obj)if(typeof obj[key]=='function')obj[key]=obj[key].bind(ctx);
  return obj}

В итоге выйдет что-то в стиле
var A2 = function(params){
  this.f1=params.f1;
  this.f2=params.f2;
  this.name='a2'}
A2.prototype.f3=function(){
  if(this.f1)this.f1()}
a1={
  name:'a1',
  init:function(){
    this.a2=new A2(bindAll({
        f1:function(){console.log(this.name)},
        f2:function(){console.log(this.name)}
      },this));
    }
  }
a1.init();
a1.a2.f3(); //=> a1
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
simbajoe
@simbajoe
Попробуйте вместо:

this.a2 = new A2({f1:this.ff1,f2:this.ff2});

написать

var me = this; this.a2 = new A2({f1: function () { me.ff1(); }, f2: function () { this.ff2(); }});

В общем, надо так или иначе замкнуть нужный объект в функции.

Почитайте про контексты и this в JavaScript.
Ответ написан
gaelpa
@gaelpa
var A2 = function(params){
    this.f1 = params.f1;
    this.f2 = params.f2;
}
A2.prototype.f3 = function(){ /* ... */ }
a1 = new (function(){
   var context=this; // вся "магия" здесь
    this.ff1=function(){
        //this не использовать, используйте context из замыкания
    },
    this.ff2=function(){  }
   // выполнить "тело" init прямо здесь или определить его как this.init=function...
})();
Ответ написан
k12th
@k12th
console.log(`You're pulling my leg, right?`);
Либо передавайте контекст, либо уже привязанные к контексту функции. Другого варианта нет.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы