05.md 15 KB

Функции, области видимости

Trivia

Повторяющиеся действия.

Как можно было заметить, компьютеры сильны именно в однотипных задачах, и делают простые задачи очень быстро. Связанные друг с другом однотипные задачи обычно повторяются в циклах (однотипные операции над массивами данных, статистические задачи, отрисовка повторяющихся данных на экране и так далее). Так же есть задачи по требованию, которые могут пригодится в любом месте кода. Например: prompt, alert, Math.random и прочие встроенные функции, которые являются подпрограммами, содержащими в себе программный код, вызываемый для решения определенной задачи. Я думаю понятно, что данные возможности не являются возможностями аппаратуры, а воплощены на программном уровне.

DRY

Don't repeat yourself. Один из основопологающих принципов разработки. Суть в том, что в процессе программирования вы должны минимизировать повторяющиеся части кода, которые делают почти одинаковые задачи; так как копипаста в коде приводит к дублированию отладки, да и вообще некрасиво это :-)

KISS

Keep It Simple, Stupid. Решайте задачи самым простым способом.

Отладка кода вдвое сложнее, чем его написание. Так что если вы пишете код настолько умно, насколько можете, то вы по определению недостаточно сообразительны, чтобы его отлаживать. — Brian W. Kernighan.

DRY > KISS

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

Пример

var surname    = prompt("Введите фамилию","")
if (surname === null || surname === ""){
    surname    = "Иванов"
}

var name       = prompt("Введите имя","")      || "Иван"
var fathername = prompt("Введите отчество","") || "Иванович"

Это наш пример, который спрашивает у пользователя ФИО ИЛИ берет эти параметры по умолчанию. Как видите, алгоритм ввода ФИО однотипен, и его неплохо было бы выделить в функцию. Ко всему прочему, несмотря на эквивалентность алгоритма, surname вводится кодом, отличающимся от ввода name и fathername, что усложняет модификацию и отладку кода.

Задание

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

Ниже спойлер, имейте совесть :-D. Не омрачайте задание подглядыванием ответов.

СПОЙЛЕР СПОЙЛЕР СПОЙЛЕР СПОЙЛЕР

Функции

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

Свойства функции, которые сделали её такой полезной для написания программ:

  • Вызов. Функция может быть вызвана, код функции выполнится в другом месте, после выполнения функции выполнение кода продолжается с места вызова.
function d()
{
    debugger;
}
alert("before d");
d()
alert("after d");
d()

Для входа и выхода из функции используются F11 и Shift-F11 в Developer Tools при пошаговой отладке

  • Область видимости. Так как функция не может "знать", из какого контекста она вызывается, то нет возможности знать заранее, совпадают ли имена переменных в функции и вне её. Таким образом вероятны побочные эффекты - непредсказуемые изменения переменных во внешнем коде, которые могут вызвать неправильную работу кода в целом;
var surname = "Петров";
function readSurname()
{
    surname = prompt("Введите фамилию","")
    if (surname === null || surname === ""){
        surname = "Иванов"
    }
}


alert(surname);
readSurname();
alert(surname);

Для решения этой проблемы используется концепция области видимости - правильно объявленная переменная в функции (через var) существует только в функции и создаются каждый раз при вызове функции; внешние же переменные с таким же именем остаются нетронутыми

var surname = "Петров";
function readSurname()
{
    var surname = prompt("Введите фамилию","") // ТУТ
    if (surname === null || surname === ""){
        surname = "Иванов"
    }
}
alert(surname);
readSurname();
alert(surname);
  • Параметры Функция должна уметь получить те или иные данные для своего выполнения. Например встроенные функции confirm, prompt, alert. Задание: Каковы параметры и какой у них смысл в вышеуказанных встроенных функциях?
var surname = "Петров";
function readWithDefault(promptText, promptDefault, somethingDefault)
{
    var something = prompt(promptText,promptDefault)
    if (something === null || something === ""){
        something = somethingDefault;
    }
    alert("something: " + something);
}
alert(surname);
readWithDefault("Введите фамилию","","Иванов");
  • Возвращаемое значение. Обратите внимание на то, что функции можно использовать как переменные в выражениях, однако не всегда это имеет смысл. Более того, функции нельзя присвоит значение, однако можно прочесть её значение.
var surname = "Петров";
function readWithDefault(promptText, promptDefault, somethingDefault)
{
    var something = prompt(promptText,promptDefault)
    if (something === null || something === ""){
        something = somethingDefault;
    }
    return something;
}
alert(surname);
var name = readWithDefault("Введите имя","","Иван");
alert(name);

Определение и выполнение функций

Обратите внимание, что первый alert происходит ДО включения пошаговой отладки. Это говорит о том, что определение функции НЕ вызывает её. Код функции работает только после вызова, который происходит по d(). Для вызова надо указать в коде имя функции и скобки после имения (с параметрами или без оных)

function d()
{
    debugger;
}
alert("before d");
d()
alert("after d");

Определение начинается с ключевого слова function, после которого идет имя функции и параметры в скобках через запятую. Далее идет блок кода функции в фигурных скобках. В отличие от if, else и циклов, фигурные скобки обязательны.

Именование функций

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

Выполнение функций.

Когда в коде упоминается имя функции со скобками и, возможно, параметрами происходят следующие действия:

  • вычисляются выражения в скобках. В функцию попадают уже значения выражений.
  • создается новая область видимости, в которую попадают параметры и их значения. Вам не нужно определять переменные для параметров.
  • начинается выполнение кода в фигурных скобках определения функции. Все переменные, определенные через var попадают в локальную область видимости функции, не перекрывающую внешнюю область видимости.
  • Код выполняется до выполнения return или окончания кода функции (закрывающей фигурной скобки). return прерывает выполнение функции, более того, с помощью return происходит возврат значения функции, которое подставляется на место вызова функции. Таким образом функция ведет себя как переменная для чтения. Если функция ничего не возвращает, то, на самом деле, она возвращает undefined
function sqr(a){
    alert("Вы передали:" + a);
    return a*a;
    alert("Этот код не выполнится");
}  

var sqr1 = sqr(5)
var otherVar = 2;
alert("Сумма квадратов: " + (sqr1 + sqr(otherVar + otherVar)));

Параметры функции и возвращаемое значение

Параметры (аргументы)

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

В Javascript количество параметров при определении и при вызове может отличаться. В таком случае непереданные параметры равны undefined:

debugger;
function add(a,b)
{
    a = a || 0;
    b = b || 0;
    return a + b;
}

alert(add())
alert(add(1));
alert(add(2,3));

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

debugger;
function add(a,b)
{
    console.log(arguments)
    a = a || 0;
    b = b || 0;
    return a + b;
}

alert(add(4,5,6))
alert(add(4,5,6,7));

Задание

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

Возвращаемое значение

Для возврата значения используется return. У него три основных свойства:

  • Собственно возврат значения во внешний код. Выражение после return вычисляется в контексте функции: javascript debugger; function add(a,b) { return a + b; } alert(add(3,4))

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

  • Прекращение выполнения функции
  • return без параметра возвращает ничего, т. е. undefined: javascript debugger; function bigAndWeirdFunction() { var somethingBad = Math.random() > 0.5; if (somethingBad){ alert("Something bad happens"); return; } alert("All OK!"); } bigAndWeirdFunction(); bigAndWeirdFunction(); bigAndWeirdFunction();