2014-01-30 3 views
0

Вот упрощен отрывок из какого-то кода, который я написал для управления планшетом жестами на холст элементахтребуется помощь в понимании использования замыкания в этом коде

первой функцией, которая принимает элемент и словарь обратных вызовов и зарегистрировать события плюс добавление другие функции, такие как жесты «Hold»:

function registerStageGestures(stage, callbacks, recieverArg) { 
    stage.inhold = false; 
    stage.timer = null; 

    var touchduration = 1000; 
    var reciever = recieverArg || window; 

    stage.onLongTouch = function(e) { 
     if (stage.timer) clearTimeout(stage.timer); 
     stage.inhold = true; 
     if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e); 
    }; 

    stage.getContent().addEventListener('touchstart', function(e) { 
     e.preventDefault(); 
     calcTouchEventData(e); 
     stage.timer = setTimeout(function() { 
      stage.onLongTouch(e); 
     }, touchduration); 
     if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e); 
    }); 

    stage.getContent().addEventListener('touchmove', function(e) { 
     e.preventDefault(); 
     if (stage.timer) clearTimeout(stage.timer); 

     if (stage.inhold) { 
      if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e); 

     } else { 
      if (callbacks.touchmove) callbacks.touchmove.call(reciever, e); 
     } 
    }); 

    stage.getContent().addEventListener('touchend', function(e) { 
     e.preventDefault(); 
     if (stage.timer) clearTimeout(stage.timer); 
     if (stage.inhold) { 
      if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e); 
     } else { 
      if (callbacks.touchend) callbacks.touchend.call(reciever, e); 
     } 
     stage.inhold = false; 
    }); 
} 

позже я называю registerStageGestures на несколько элементов (представленных «View» объектов) в одной и той же странице. Что-то вроде:

function View() { 
    var self=this; 
    .. 

    function InitView() { 
     ... 

     registerStageGestures(kineticStage, { 
     touchstart: function(e) { 
      // do something 
     }, 
     touchmove: function(e) { 
      // do something 
     }, 
     touchendunction(e) { 
      // do something 
     }, 
     touchholdstart: function(e) { 
      // do something 
     }, 
     touchholdmove: function(e) { 
      // do something 
     }, 
     touchholdend: function(e) { 
      // do something 
     }, 
    }, self); 

Все работает отлично, но я озадачен две вещи в реализации registerStageGestures:

Во-первых, это необходимо сделать inhold, таймер и onLongTouch членов стадии? или затворы делают все хорошо, если они являются локальными варами в registerStageGestures?

Во-вторых, нужно ли вызывать обратные вызовы с помощью .call (приемник, синтаксис?) Я делаю это, чтобы убедиться, что код обратного вызова будет работать в контексте представления, но я не уверен, ? нужен

любой вход высоко ценится

Благодаря

ответ

1

Во-первых, необходимо ли использовать таймер и элементы onLongTouch стадии? или затворы делают все хорошо, если они местных варов в registerStageGestures?

Насколько registerStageGestures() обеспокоен, var inhold, var timer и function onLongTouch(e) {...}. было бы достаточно. Механизм, с помощью которого внутренняя функция имеет автоматический доступ к ее членам внешней функции, называется «закрытием». Вам нужно будет установить только stage.inhold, stage.timer и stage.onLongTouch, если какой-либо другой фрагмент кода нуждается в доступе к этим настройкам в качестве свойств stage.

Во-вторых, это необходимо вызвать обратные вызовы с «.call (приемник,» синтаксис? Я делаю это, чтобы убедиться, что код обратного вызова будет работать в контексте View, но я не уверен, если это необходимо?

Возможно, в зависимости от того, как эти обратные вызовы записываются. .call() и .apply() иногда используются при вызове функций, которые используют this внутри. в обоих случаях первый параметр, переданный определяет объект, который будет интерпретироваться как this. Таким образом, javascript предоставляет вам средства определения ge neral purpose methods без a priori предположение об объекте, к которому применяются эти методы при вызове. Точно так же вы можете вызвать метод объекта таким образом, чтобы он воздействовал на другой объект.

EDIT:

Для полноты, обратите внимание, что даже при отсутствии this в функции, .apply() может быть очень полезным, поскольку это позволяет использовать несколько параметров, которые будут определены в качестве элементов одного массива, например, ubiquitous jQuery.when.apply(null, arrayOfPromises)...

1

Есть несколько простых ответов, здесь

Во-первых, закрытие:.

Закрытие в основном говорит, что независимо от того, что определено внутри функции, имеет доступ к остальной части содержимого этой функции.
И все эти материалы гарантированно остаются в живых (из мусора), пока не осталось больше объектов, которые были созданы внутри.

Простой тест:

var testClosure = function() { 
    var name = "Bob", 
     recallName = function() { return name; }; 

    return { getName : recallName }; 
}; 


var test = testClosure(); 
console.log(test.getName()); // Bob 

Поэтому все, что было создано внутри можно получить с помощью любой функции, которая была также созданный внутри (или создается внутри функции, созданной в функции [... ], внутри).

var closure_2x = function() { 
    var name = "Bob", 
     innerScope = function() { 
      console.log(name); 
      return function() { 
       console.log("Still " + name); 
      } 
     }; 

    return innerScope; 
}; 


var inner_func = closure_2x(); 

var even_deeper = inner_func(); // "Bob" 
even_deeper(); // "Still Bob" 

Это относится не только к переменным/объектам/функциям, созданным внутри, но также к функциям аргументов, передаваемых внутри.

Аргументы не имеют доступа к внутренним работам (если не переданы методам/обратным вызовам), но внутренняя работа будет запоминать аргументы.

До тех пор, пока ваши функции создаются в той же области, что и ваши значения (или область с детьми), есть доступ.

.call сложнее.

Вы знаете, что он делает (заменяет this внутри функции с объектом вы передаете его) ...

... но почему и когда, в этом случае труднее.

var Person = function (name, age) { 
    this.age = age; 
    this.getAge = function() { 
     return this.age; 
    }; 
}; 

var bob = new Person("Bob", 32); 

Это выглядит довольно нормально.
Честно говоря, это может сильно походить на Java или C# с несколькими ухищрениями.

bob.getAge(); // 32 

Работает как Java или C#, тоже.

doSomething.then(bob.getAge); 

? Бух?

Теперь мы прошли метод Боба в функцию как функцию, само по себе.

var doug = { age : 28 }; 
doug.getAge = bob.getAge; 

Теперь мы дали doug ссылку на непосредственно использовать bob сек methid - не копию, а указатель на фактический метод.

doug.getAge(); // 28 

Ну, это странно.

Как насчет того, что произошло с момента его передачи в качестве обратного вызова?

var test = bob.getAge; 
test(); // undefined 

Причиной этого является, как вы сказали, о контексте ...

Но конкретная причина в том, что this внутри функции в JS не предварительно скомпилированных или сохранены. ..
this разработан на лету каждый раз, когда вызывается функция.

Если вы звоните

obj.method(); 

this === obj;

Если вы звоните

a.b.c.d(); 

this === a.b.c;

Если вы звоните

var test = bob.getAge; 
test(); 

...?

this соответствует window.
В «строгом режиме» этого не происходит (вы быстро получаете ошибки).

test.call(bob); //32 

Баланс восстановлен!

В основном ...

Есть еще несколько уловов.

var outerScope = function() { 
    console.log(this.age); 
    var inner = function() { 
     console.log("Still " + this.age); 
    }; 

    inner(); 
}; 

outerScope.call(bob); 

// "32" 
// "Still undefined" 

Это имеет смысл, когда вы думаете об этом ... Мы знаем, что если функция выясняет this в данный момент это называется - область не имеет ничего общего с ним ...

... и мы не добавили inner к объекту ...

this.inner = inner; 
this.inner(); 

бы работал просто отлично (но сейчас вы просто запутались с внешним объектом) ...

So inner видел this как window.

Решение будет либо использовать .call или .apply или использование функции-обзорного и/или закрытие

var person = this, 
    inner = function() { console.log(person.age); }; 

кроличья нора глубже, но мой телефон умирает .. .

+0

thx для этого, это помогло, я искал ответ, который включал мой код. – kofifus

+0

Конечно. Покажите человеку закрытие, и он поймет сферу действия ... Научите человека закрыться ... – Norguard

Смежные вопросы