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

Examples

js

By Oleg Rovenskyi

js

events, closure, context, this, call, apply, setTimeout/setInterval

  • 523