JS
events, closure, context, this, call, apply, setTimeout/setInterval
Agenda
- Events
- Cascade
- Closures
- Context
- this
- setTimeout
- setInterval
Events
Event in HTML
Event as a value of property
Event and special methods
Events
Event in HTML
Event as a value of property
Event and special methods
<button onClick="someHandler()">
Click
</button>
Events
Event in HTML
Event as a value of property
Event and special methods
button.onclick = someMethod;
button.onclick = function() {
console.log( 'on click' );
};
Events
Event in HTML
Event as a value of property
Event and special methods
var button = document.getElementById("demo");
button.addEventListener("click", myFunction);
function myFunction() {
document.getElementById("demo").innerHTML = "YOU CLICKED ME!";
}
button.addEventListener("click", myFunction); // W3C
button.attachEvent("on" + "click", myFunction); // IE 8
Events
Event in HTML
Event as a value of property
Event and special methods
// Remove the event handler from <div>
var button = document.getElementById("demo");
button.removeEventListener("click", myFunction); // W3C
button.detachEvent("on" + type, myFunction); // IE 8
Bubbling and capturing
Bubbling
Capturing
Bubbling and capturing
Bubbling and capturing
lexical scoping
Все переменные внутри функции – это свойства специального внутреннего объекта LexicalEnvironment, который создаётся при её запуске.
function init() {
var name = "Mozilla"; // name - локальная переменная, созданная в init
function displayName() { // displayName() - внутренняя функция, замыкание
alert(name); // displayName() использует переменную, объявленную в родительской функции
}
displayName();
}
init();
lexical scoping
- Каждая функция при создании получает ссылку [[Scope]] на объект с переменными, в контексте которого была создана.
- При запуске функции создаётся новый объект с переменными LexicalEnvironment. Он получает ссылку на внешний объект переменных из [[Scope]].
- При поиске переменных он осуществляется сначала в текущем объекте переменных, а потом – по этой ссылке.
Cascade
var userName = 'Vik';
function sayHi(userName ) {
console.log(userName);
}
sayHi('Arni');
// ???
Closures
Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция, определённая в замыкании, «запоминает» окружение, в котором она была создана.
Closure
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
};
var myFunc = makeFunc();
myFunc();
Замыкание – это функция вместе со всеми внешними переменными, которые ей доступны.
Closure
function makeShout() {
var phrase = "Hi!";
var shout = function() {
console.log(phrase);
};
phrase = "Done!";
return shout;
}
makeShout()();
// ???
Closure
- Все переменные и параметры функций являются свойствами объекта переменных LexicalEnvironment. Каждый запуск функции создает новый такой объект. На верхнем уровне им является «глобальный объект», в браузере – window.
- При создании функция получает системное свойство [[Scope]], которое ссылается на LexicalEnvironment, в котором она была создана.
- При вызове функции, куда бы её ни передали в коде – она будет искать переменные сначала у себя, а затем во внешних LexicalEnvironment с места своего «рождения».
Closure
var firstFunc = function () {
var index = 5;
return function() {
return index;
};
};
var secondFunc = function() {
var index = 15;
console.log( firstFunc()() );
};
secondFunc();
// ???
Why we use Closure?
function t() {
for (var i = 0; i < 3; i++) {
document.getElementById('button' + i).onclick = function() {
console.log(i);
};
}
}
t();
// click1 = 3
// click2 = 3
// click3 = 3
Why we use Closure?
Проблема в том, что функции, присвоенные как обработчики события, являются замыканиями. Они состоят из описания функции и контекста исполнения (окружения), унаследованного от функции t. Было создано три замыкания, но все они были созданы с одним и тем же контекстом исполнения (окружением). К моменту возникновения события onclick цикл уже давно отработал, а значит переменная item (одна и та же для всех трех замыканий) указывает на последний элемент массива, который как раз 3.
Why we use Closure?
function t() {
for (var i = 0; i < 3; i++) {
(function(){
var index = i;
document.getElementById('button' + index).onclick = function() {
console.log(index + 1);
};
})();
}
}
t();
// click1 = 1
// click2 = 2
// click3 = 3
Вот это работает как следует. Вместо того, чтобы делить на всех одно окружение, самовызывающаяся функция создает каждому из замыканий свое собственное, в котором переменная index указывает на правильный элемент массива.
Why we use Closure?
function t() {
for (var i = 0; i < 3; i++) {
document.getElementById('button' + i).onclick = function(index) {
return function() {
console.log(index + 1);
};
}(i);
}
}
t();
Why we use Closure?
function makeHelpCallback(index) {
return function() {
console.log(index + 1);
};
}
function t() {
for (var i = 0; i < 3; i++) {
document.getElementById('button' + i).onclick = makeHelpCallback(i);
}
}
t();
Вот это работает как следует. Вместо того, чтобы делить на всех одно окружение, функция makeHelpCallback создает каждому из замыканий свое собственное, в котором переменная index указывает на правильный элемент массива.
Chaining
$("#my-account")
.height(20)
.width(200)
.show()
.parent()
.addClass("illusion")
.data("size", 200*20)….
$("#my-account").height(20);
$("#my-account").width(200);
$("#my-account").show();
$("#my-account").parent();
$("#my-account:parent").addClass("illusion");
$("#my-account:parent").data("size", 200*20)….
Chaining?
• Лаконично
• Быстро
• Легко воспринимать
this and object
function test() {
console.log(this);
}
test();
var a = {
name: 'Вася',
getA: function() {
return this;
}
}
a.name; // ???
a.getA(); // ???
this and object
var a = {
name: 'Вася',
getName: function () {
return this.name;
},
getContext: function() {
return this;
}
}
a.name; // ???
a.getName(); // ???
a.getContext(); // ???
В this будет храниться ссылка на текущий объект a.
this
Значение this называется контекстом вызова и будет определено в момент вызова функции.
this and object
var a = new Object();
a.foo = 13;
a.getContext = function() {
return this;
}
a.getFoo = function() {
return this.foo;
}
a.getFoo() // ???
a.getContext() // ???
function this
var t1;
function test() {
this.a = 13;
return this;
}
t1 = test();
console.log(t1);
console.log(t1.a);
function this and constructor
var t1;
var t2;
function test() {
this.a = 13;
return this;
}
t1 = test();
console.log(t1);
console.log(t1.a);
t2 = new test();
console.log(t2);
console.log(t2.a);
function this and constructor
function test() {
this.a = 13;
this.getA = function() {
return this.a;
}
return this;
}
var t2 = new test();
console.log(t2.a);
console.log(t2.getA());
Context and this
function getFullTime() {
this.time = new Date().getTime();
console.log(this);
}
getFullTime(); // window
new getFullTime(); // свой this
this нам нужен для эмулирования областей видимости, например если у нас есть класс который отвечает за поведение кнопки, например кнопка с контекстным меню мы можем создать window.ContextMenuButton.link, но у нас возникает проблема когда таких кнопок на странице много и нам без ООП не обойтись. У каждого класса есть контекст вызова, своё состояние и так далее.
Context call
• При помощи call можно легко взять метод одного объекта, в том числе встроенного, и вызвать в контексте другого.
• Это называется «одалживание метода» (на англ. method borrowing).
function f() {
console.log(this.toString()); // 123
}
f.call(123);
this
this – указатель на контекст запуска функции
Context call vs apply
• func.call(context, arg1, arg2, ...)
• func.apply(context, [arg1, arg2, …])
setTimeout/setInterval
Dellayed call of function
setTimeout(handler, dellay_in_ms); // :)
setTimeout('alert("code")', dellay_in_ms); // :(
setTimeout(function () {/*code*/}, dellay_in_ms); // :(
Interval works in same way but every period of time
SetTimeout context
// Be caerfully.
setTimout(function () {
console.log(this); // window
}, 0);
SetTimeout context
// Solution
(function () {
var _this = this;
function a() {
console.log(this)
}
setTimeout(_this.a, 0);
})()
// Otherwise use bind or proxy
(function () {
function a() {
console.log(this)
}
setTimeout(a.bind(this), 0);
})()
setTimeout event quie
// Ваш код
setTimeout(a, 0);
…
// someLongCode
…
setTimeout(b, 0);
// Порядок выполнения
// someLongCode
reflow() //browser native code
repaint()//browser native code
a()
reflow() //browser native code
repaint() //browser native code
b()
setTimeout - cancel
// SetTimeout/inteval return uniq id
var id = setTimeout(ahtung, 10000);
function somethingHappens() {
clearTimeout(id);
}
Links
- http://javascript.ru/tutorial/events/crossbrowser
- https://learn.javascript.ru/introduction-browser-events
- https://www.w3schools.com/jsref/met_element_removeeventlistener.asp
- https://developer.mozilla.org/ru/docs/Web/API/EventTarget/addEventListener
- https://learn.javascript.ru/event-bubbling
- https://developer.mozilla.org/ru/docs/Web/JavaScript/Closures
- https://learn.javascript.ru/closures
- https://habrahabr.ru/post/149516/
- https://learn.javascript.ru/object-methods
- https://habrahabr.ru/company/tradingview/blog/178261/
Examples
js
By Oleg Rovenskyi
js
events, closure, context, this, call, apply, setTimeout/setInterval
- 523