Конспект JS-course

Конспект. Функции и области видимости

Источник: http://forum.jscourse.com/t/05-konspekt-funkczii/584

Автор конспекта: @Nikolay

Правки: @dmitry

Функции (описание)

Функции - это некоторые действия. Функция позволяет структурировать программу, разбивать ее на маленьке части (подпрограммы)и таким образом организовывать код.

При создании функции необходимо необходимо ее реализовывать так, чтобы функция имела как можно меньше побочных эффектов.

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

Абстракция - этим термином описывают технику, когда за одним действием скрывается несколько других действий.

Вызов функции

При создании функции, к примеру f, мы можем вызвать ее с помощью console.log(f()):

function f() {
  return 999;
}
console.log(f()); // 999

Также мы можем записать эту функцию в переменную:

var a = f;

Присвоение, сравнивание функций происходит "по ссылке". То есть переменная а и переменная f , ссылаются на одну и ту же функцию.

Если мы обратимся к переменной а или f,то они вычисляются в ту функцию, в которую ссылаются а

console.log(a === f); // true

Чтобы вызвать функцию следует использовать круглые скобки:

a();

Функции - данные

Функция - это значения, и поэтому мы можем передавать их в качестве аргументов в другие функции. Такая техника используется часто в асинхронном программировании и при работе с API.

Создадим функцию которая вызывает аргумент 10 раз:

function run10Times(func) { // принимает аргументом обьект функции (func)
     for (var i = 0; i < 10; i += 1) { // вызывает обьект этой функции 10 раз
          func();
     }
}

Обьявляем функцию run10Times и передаем в качестве аргумента функцию f (а если точнее - ссылку на функцию).

function f(timeCalled) {
    console.log(999, timeCalled);
}

run10Times(f); // <<

Когда вызывается функция с аргументами, и подставляем другую функцию/обьект (f) в качестве аргумента, то первым делом запускается тело этой (передаваемой) функции (f), и те аргументы которые были подставлены при вызове записываются в переменные которые были обьявлены при обьявлении функции.

То есть, внутри run10Times есть переменнаяfunc, которая ссылается на ту же функцию, что и f.

Обьявление функции

Function declaration

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

функия может быть вызвана и работать с любым количеством параметров:

f('mama'); // [ "mama" ]
f('mama', 'mila'); // ["mama", "mila"]

Об аргументах функции удобно думать следующим образом: "Когда функция f будет вызвана, то в переменную first внутри функции будет записано то значение, которое передано в качестве первого аргумента при вызове".

Примитивные типы данных (строки, числа, булевые) передается аргументами в функцию по значению. Объекты, массивы, функции передаются по ссылке(!). Если функцию обьявлена с 3-я параметрами, а при вызове одно из значений не было передано, то в значение непереданных аргументом будет записано значение undefined.

function f(first, second, third) {
          console.log(first, second, third);
          console.log('arguments', arguments);
}
f('first'); // "first" undefined undefined
            //arguments ["first"]

Аргументы и функции

Обьект arguments создается в момент вызова функции.

arguments является "масивоподобным" обьектом (имеет числовой индекс и обладает свойством length (и только).

Если нужно работать с arguments, как с массивом (вызвать метод массива) объект arguments нужно превратить в массив. При этом по arguments все так же можно итерировать циклом for. Для преобразования массивоподобного объекта в массив можно использовать абстракцию.

// функция которая преобразует обьект arguments в массив
function objectToArray(arrayLikeObject) {
    var res = [];
    for (var i = 0; i < arrayLikeObject.length; i += 1) {
            res.push(arrayLikeObject[i]);
    }
    return res;
}

//рабочая функция
function f(first, second, third) {
    var args = objectToArray(arguments); // обращаемся к функции arrayLikeObject для преобразования обьекта arguments в массив
    console.log('undeinfed index', args.indexOf(undefined));
}
// Результат
f('mama', 'mila', undefined); // "undeinfed index" 2

Анонимные функции

Напишем функцию forEach которая будет принимать аргументом массив и функию action (пример с книги Eloquent JS). Функия forEach вызывает для каждоного элемента массива функию action , и передает туда i-тий аргумент массива. (дальше рассмотрим как реализовать аналогичную программу с использованием анонимной функции)

function forEach(array, action) {
  for (var i = 0; i < array.length; i++) {
      action(array[i]);
  }
}

function log(val) {
    console.log(val);
}
forEach(["Тили", "Мили", "Трямдия"], log)

Output:
"Тили"
"Мили"
"Трямдия"

Следует обратить внимание, что в параметр action записывается значение второго аргумента, с которым вызывается forEach (а именно log), а арумент log в свою очередь ссылается на функцию log. Значит - в параметр action записывается ссылка на функцию log .

Теперь вызовем функцию log используя анонимную функцию (function first-class objects)

forEach(["Тили", "Мили", "Трямдия"], function (val) {
     console.log(val, fff);
 });

То есть мы обьявляем и передаем функцию на месте. Следует запомнить, что литеральная запись массива никак не отличается от литеральной записи функции.

Также фукнция может хранится в обьекте:

var ppp = {
    log: function (val) {
        console.log('THIS FUNC');
        console.log(val);
    }
};

И мы ссылаемся на ее следующим образом:

forEach(["Тили", "Мили", "Трямдия"], ppp.log);