Функции

Область видимости. Всплытие.
Замыкание

Мангин Александр

Что было в прошлых сериях:

  • Типы данных
  • Функции
  • Объекты
  • ...

Сегодня:

  • Область видимости
  • Глобальный объект
  • Всплытие
  • Замыкание

Область видимости

Область видимости в обычном ЯП


int x = 10;
            

Область видимости в обычном ЯП


int x = 10;
print x; // ?!;
            

Область видимости в обычном ЯП


int x = 10;
print x; // 10;
            

Область видимости в обычном ЯП


print x; // ?!;
int x = 10;
            

Область видимости в обычном ЯП


print x; // NameError: name 'x'
         // is not defined;
int x = 10;
            

Область видимости в обычном ЯП


if (true) {
    int answer = 2 + 2;
}
            

Область видимости в обычном ЯП


if (true) {
    int answer = 2 + 2;
}
// ?!
            

Область видимости в обычном ЯП


if (true) {
    int answer = 2 + 2;
}
// NameError: name 'answer'
// is not defined;
            

Круговорот переменных в обычном ЯП

  • Переменная 'рождается' в момент объявления
  • А 'умирает' в конце блока

Область видимости в JS

Область видимости в JS


var x = 10;
console.log(x); // ?!
            

Область видимости в JS


var x = 10;
console.log(x); // 10
            

Область видимости в JS


console.log(x); // ?!
var x = 10;
            

Область видимости в JS


console.log(x); // undefined
var x = 10;
            

Область видимости в JS


if (true) {
    var x = 10;
}
            

Область видимости в JS


console.log(x); // ?!
if (true) {
    var x = 10;
}
console.log(x); // !?
            

Область видимости в JS


console.log(x); // undefined
if (true) {
    var x = 10;
}
console.log(x); // 10
            

Область видимости в JS


function getAnswer() {
    var x = 21;

    return x * 2;
}
            

Область видимости в JS


console.log(x); // ?!

function getAnswer() {
    var x = 21;

    return x * 2;
}

console.log(x); // ?!
            

Область видимости в JS


console.log(x);
// Uncaught ReferenceError:
// x is not defined
function getAnswer() {
    var x = 21;

    return x*2;
}
console.log(x);
// Uncaught ReferenceError:
// x is not defined
            

Круговорот переменных в JS

  • Вызываем функцию/программу(*)
  • Создаем lexical environment(global)
  • Объявляем все переменные в environment(*)
  • Выполняем функцию
  • Что-то делаем
  • Инициализируем переменные
  • Что-то делаем
  • ...

Область видимости в JS


function sum (a, b) {
    return a + b;
}

sum(2, 2); // 4
            

Область видимости в JS


function sum () {
    var a = arguments[0];
    var b = arguments[1];

    return a + b;
}

sum(2, 2); // 4
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // *
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // * a=1;c;e;
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // *
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // * a=1;b=1;c;e;
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // *
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // * a=1;c=1;e;
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // *
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // * a=1;c=1;e;d=1;
}());           // 8
var e = 1;      // 9
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // *
            

Область видимости в JS


var a = 1;      // 1
(function () {  // 2
    var b = 1;  // 3
}());           // 4
var c = 1;      // 5
(function () {  // 6
    var d = 1;  // 7
}());           // 8
var e = 1;      // * a=1;c=1;e=1;
            

lexical environment

  • Список переменных и их значений
  • Ссылка на родительский environment

lexical environment


var a = 1;               // 1
(function root() {       // 2
    var b = 2;           // 3
    (function child() {  // 4
        var c = 3;       // 5
    }());                // 6
}());                    // 7
            

Поиск значения в переменной(*)

  • Берем текущий environment
  • Ищем значение переменной в нем
  • Если нашли => возвращаем значение
  • Если не нашли => берем родительский environment
  • Идем на первый шаг

Область видимости в JS


var a = 1;               // 1
(function root() {       // 2
    var b = 2;           // 3
    (function child() {  // 4
        var c = 3;       // 5
    }());                // 6
}());                    // 7
            

Глобальный объект

Глобальный объект в nodejs


global

Глобальный объект в nodejs


require('index.js');
Math.abs(-1);

global.require('index.js');
global.Math(-1);

Глобальный объект в nodejs


var key = 42;
console.log(global.key); // ?!

Глобальный объект в nodejs


var key = 42;
console.log(global.key);
// undefined

Глобальный объект в nodejs


key = 42;

Глобальный объект в nodejs


key = 42;
console.log(global.key); // !?

Глобальный объект в nodejs


key = 42;
console.log(global.key); // 42

Глобальный объект в nodejs


key = 42;
// global.key = 42;

Глобальный объект в браузере


window

Глобальный объект в браузере


var x = 1;
y = 2;

console.log(window.x); // !?
console.log(window.y); // !?

Глобальный объект в браузере


var x = 1;
y = 2;

console.log(window.x); // 1
console.log(window.y); // 2

Глобальный объект в браузере


var x = 1;
y = 2;
// window.y = 2

Глобальный объект в браузере


function createPi() {
    PI = 3.14;
    var twoPI = PI * 2;
}
console.log(window.PI); //!?
console.log(window.twoPI); //!?

Глобальный объект в браузере


function createPi() {
    PI = 3.14;
    var twoPI = PI * 2;
}
console.log(window.PI);
console.log(window.twoPI);
// 3.14
// undefined

Глобальный объект в браузере


function createPi() {
    PI = 3.14;
    // window.PI = 3.14;
    var twoPI = PI * 2;
}
console.log(window.PI);
console.log(window.twoPI);
// 3.14
// undefined

Оператор delete


var student = {
    name: 'Alex',
    isStudy: true
};

delete student.isStudy;
console.log(student); // ?!

Оператор delete


{
    name: 'Alex'
}

Оператор delete


var a = ['a', 'b', 'c'];
var b = ['c', 'b', 'd'];

var map = {};
for(var i = 0; i < a.length; i++ ) {
    map[a[i]] = true;
}

for(var i = 0; i < b.length; i++ ) {
    delete map[b[i]];
}

Оператор delete


var x = 1;
y = 2;

delete x;
delete y;


console.log(x); // ?!
console.log(y); // ?!

Оператор delete


var x = 1;
y = 2;

delete x;
delete y;


console.log(x); // 1
console.log(y); // undefined

Оператор delete

  • Удаляет свойство из объекта;
  • Если передали переменную,то ничего не удаляем
  • Если передали свойство глобального объекта, то удаляем

Поиск значения в переменной(*)

  • Берем текущий environment
  • Если (environment === null) => ищем в глобальном объекте
  • Ищем значение переменной в нем
  • Если нашли => возвращаем значение
  • Если не нашли => берем родительский environment
  • Идем на первый шаг

Всплытие переменных(Hoisting)

Круговорот переменных в JS

  • Вызываем функцию/программу(*)
  • Создаем lexical environment(global)
  • Объявляем все переменные в environment(*)
  • Выполняем функцию
  • Что-то делаем
  • Инициализируем переменные
  • Что-то делаем
  • ...

Всплытие



function calculateAnswer () {
    var text = 'title';
    var result = text + ':' + 42;
}

var result = calculateAnswer();

Всплытие


var result;

function calculateAnswer () {
    var text;
    var result;

    text = 'title';
    result = text + ':' + 42;
}

result = calculateAnswer();

Всплытие


var input = 42;

if (input === 42) {
  var result = 'Ты прав!';
} else {
  var result = 'Ты заблуждаешься!';
}

Всплытие


var result, input;

input = 42;

if (input === 42) {
  result = 'Ты прав!';
} else {
  result = 'Ты заблуждаешься!';
}

Всплытие



var f = function () {
    console.log(1);
}
function f () {
    console.log(2);
}

Всплытие


function f () {
    console.log(1);
}

f = function () {
    console.log(2);
}

Всплытие


if (...) {
    function f() {
        console.log(1);
    }
} else {
    function f() {
        console.log(2);
    }
}

Всплытие


function f() {
    console.log(2);
}

if (...) {
   // empty
} else {
   // empty
}

Всплытие


if (...) {
    var f = function () {
        console.log(1);
    }
} else {
    var f = function () {
        console.log(2);
    }
}

Всплытие


var f;

if (...) {
    f = function () {
        console.log(1);
    }
} else {
    f = function () {
        console.log(2);
    }
}

Всплытие


var squareFuncs = [];

for(var i=0; i < 10; i++) {
    squareFuncs.push(function () {
        return i*i;
    });
}

for(var i=0; i < 10; i++) {
    squareFuncs[i]();
}

Всплытие


var squareFuncs = [];

for(var i=0; i < 10; i++) {
    var square = i*i;
    squareFuncs.push(function () {
        return square;
    });
}

for(var j=0; j < 10; j++) {
    squareFuncs[i](); // ?!
}

Всплытие


var squareFuncs = [];

for(var i=0; i < 10; i++) {
    var square = i*i;
    squareFuncs.push(function () {
        return square;
    });
}

for(var j=0; j < 10; j++) {
    squareFuncs[i](); // 100 * 10
}

Всплытие


var squareFunc, i, j, square;
squareFunc = [];
for(i=0; i < 10; i++) {
    square = i*i;
    squareFunc.push(function () {
        return square;
    });
}
for(j=0; j < 10; j++) {
    squareFunc[i]();
}

Всплытие


var squareFunc = [];

for(var i=0; i < 10; i++) {
  (function (i) {
    squareFunc.push(function () {
      return i*i;
    });
  }(i));
}

Всплытие


var squareFunc, i;

squareFunc = [];

for(var i=0; i < 10; i++) {
  (function (i) {
    squareFunc.push(function () {
      return i*i;
    });
  }(i));
}

Всплытие


var squareFunc, i, j;

squareFunc = [];
function createFunc(i) {
    return function () {
        return i*i;
    }
}
for(var j=0; j < 10; j++) {
    squareFunc.push(createFunc(j));
}

Всплытие


for(let i=0; i < 10; i++) {
    squareFunc.push(function () {
        return i*i;
    });
}

Замыкания

Круговорот переменных в JS

  • Вызываем функцию/программу(*)
  • Создаем lexical environment(global)
  • Объявляем все переменные в environment(*)
  • Выполняем функцию
  • Что-то делаем
  • Инициализируем переменные
  • Что-то делаем
  • ...

Замыкания


var a = 3;
var b = 2;

function sum () {
    return a + b;
}

sum(); // ?!

Замыкания


var a = 3;
var b = 2;

function sum () {
    return a + b;
}

sum(); // 5

Замыкания


var a = 3;
var b = 2;

function sum () {
    return a + b;
}

a = 10;
sum(); // ?!

Замыкания


var a = 3;
var b = 2;

function sum () {
    return a + b;
}

a = 10;
sum(); // 12

Замыкания


function partialSum (a) {
    return function (b) {
        return a + b;
    };
}

var sum2 = partialSum(2);
sum2(2); // ?!
sum2(40); // ?!

Замыкания


function partialSum (a) {
    return function (b) {
        return a + b;
    };
}

var sum2 = partialSum(2);
sum2(2);  // 4
sum2(40); // 42

Замыкания


var cacheSum;
function partialSum (a) {
    cacheSum = function (b) {
        return a + b;
    };
}

partialSum(2);
cacheSum(2);  // 4
cacheSum(40); // 42

Замыкания: Сохранение контекста


var angle = createAngle360(90);

angle.add(180);
angle.getAngle(); // 270;
angle.add(180);
angle.getAngle(); // 90

Замыкания: Сохранение контекста


function createAngle360(value) {
  return  {
    add: function (dt) {
      var angle = value + dt;
      value = angle % 360;
    },
    getAngle: function () {
      return value;
    }
  };
}

Замыкания: Инкапсуляция


var a = 2;
var b = 2;

var result = a + b;

Замыкания: Инкапсуляция


a; // ?!
(function () {
    var a = 1;
}());
a; // ?!
(function () {
    var a = 2;
}());
a; // ?!

Замыкания: Инкапсуляция


a; // error
(function () {
    var a = 1;
}());
a; // error
(function () {
    var a = 2;
}());
a; // error

Паттерн модуль


(function () {
    // ваш код
}());

Замыкания: Создание новых функций


var doubleSum = doubleResult(function (a, b) {
    return a + b;
});

doubleSum(2, 5); // 14
doubleSum('a', 'b'); // 'abab'

Замыкания: Создание новых функций


function doubleResult(f) {
    return function () {
        var result = f.apply(null, arguments);
        return result + result
    };
}

Замыкания: когда нужно

  • Инкапсуляция
  • Сохранение контекста
  • Создание новых функций

Замыкания: Темная сторона


var a = 2;
var b = 2;

// 100500 строк кода

function sum() {
   return a + b;
}
            

Итого:

  • Область видимости
  • Глобальный объект
  • Всплытие(Hoisting)
  • Замыкание

Домашнее задание