Skip to content

function object

garevna edited this page Sep 19, 2018 · 25 revisions

🎓 Объект function

⤵️
▶️ Объект arguments 📋 ECMAScript® 2016 Language Specification:
☝️ Функция является вызываемым объектом
☝️ Функция, связанная с объектом через свойство, называется методом
▶️ Контекст выполнения
     ▶️ Lexical Environment
     ▶️ hoisting
     ▶️ Область видимости
     ▶️ this
▶️ Контекст вызова

Как машина скорой помощи, функция может перемещаться от одного объекта к другому ( откуда она вызвана )

У 🚑 есть "контекст исполнения":

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

Все это функция 🚑 возит с собой

В момент вызова у функции 🚑 появляется контекст вызова:

🏠

конкретные условия 
( частный дом, квартира в многоэтажке, 
наличие или отсутствие лифта, водопровода и т.д. )

:neckbeard:

конкретный больной с конкретными симптомами, 
возрастом, историей болезни, характером и т.д.

🎓 КОНТЕКСТ ВЫЗОВА

⤴️ ⤵️

Контекст вызова - это объект

Обычно имя этого объекта стоит перед именем функции, и отделено от него точкой

Как правило, если имя объекта перед именем функции не указано, то контекстом вызова функции является глобальный объект window

Исключение составляют функции, контекст вызова которых установлен с помощью метода bind ()

⚠️ Отсюда следует, что все функции JS являются методами

Если объект ( "хозяин" метода ) не указан, подразумевается глобальный объект

☕ 1

Объявим три функции:

function first () {
        console.log ( "Работает функция first" )
}
function second () {
        console.log ( "Работает функция second" )
}
function third () {
        console.log ( "Работает функция third" )
}

Все три функции объявлены в глобальном контексте, то есть они являются методами глобального объекта window

Как мы уже знаем, можно обращаться к свойствам объекта как к элементам ассициативного массива

Тогда кострукция:

window [ "first" ]

вернет нам функцию first, которая является свойством ( методом ) глобального объекта window

Для вызова этой функции не хватает только круглых скобок:

window [ "first" ] ()

Используя этот факт, мы можем вызывать функцию, имя которой нам передано в переменной типа "string":

for ( var func of [ "first", "second", "third" ] ) 
    window [ func ] ()

🎓 ИНКАПСУЛЯЦИЯ

Таким образом, функция - это "капсула" со своим содержимым
Содержимое "капсулы" недоступно из контекста вызова
( посторонние не могут использовать материалы, инструменты 
и аппараты 🚑 )

🎓 this

Однако функция должна работать с переменными контекста вызова
Т.е. ей нужен доступ к контексту вызова
Для этого и существует ключевое слово  this
это ссылка на объект, в контексте которого вызвана функция

☕ Например, для функции 🚑

 this.адрес
 this.этаж
 this.квартира
 this.больной.имя
 this.больной.возраст
 this.больной.симптомы
 ...

Если бы не this, вряд ли функция могла бы помочь "больному" 😉

✏️ Сигнатура функции

⤴️ ⤵️

Сигнатура функции - это ее имя + список формальных параметров

Формальные параметры функции - это имена переменных, перечисленные в круглых скобках при объявлении функции

Аргументами функции являются фактические значения, передаваемые функции при вызове

Если:

👆 функции будет передано аргументов больше числа формальных параметров, то лишние аргументы будут отброшены;

👆 функции будет передано аргументов меньше числа формальных параметров, то недостающие аргументы получат значение undefined

🎓 Объект arguments

⤴️ ⤵️

Функции JavaScript имеют встроенный объект arguments

У него есть свойство length, как у массива

Его элементы доступны по индексу, как и элементы массива

⚠️ Однако это не массив

Поэтому к нему нельзя применить методы работы с массивами ( push, pop и т.д. )

Его можно преобразовать в обычный массив с помощью метода Array.from

var args = Array.from ( arguments )

В объекте arguments находятся все аргументы, переданные функции при ее вызове

Они будут доступны по индексу строго в том порядке, в каком они были переданы функции при вызове

☕ Например:

function testArguments () {
    for ( var i = 0;  i < arguments.length;  i++ ) {
        console.log ( `[ ${ ( i + " ]" ).padEnd ( 10 ) } ${ arguments [ i ] }` )
    }
}

testArguments ( 27, false, "Fill", [ 7, 4, 5 ], null )

✏️ arguments.callee

⤴️ ⤵️

У объекта arguments есть свойство arguments.callee -

ссылка на выполняемую функцию ( функцию-"хозяина" объекта arguments )

☕ 1

function testArguments () {
        console.log ( arguments.callee )
}

testArguments ( 5, false )

В свойстве arguments.callee находится ссылка на саму функцию testArguments

☕ 2

Объявим функцию getArguments:

function getArguments ( param ) {
    return param ? param : arguments.callee
}

которая, если ей был передан аргумент, возвращает значение этого аргумента, в противном случае возвращает ссылку на саму себя

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

var x = getArguments ()
var y = getArguments ( "Привет!" )
результат вызова функции без аргументов мы поместили в переменную x,
а результат вызова с аргументом "Привет!" мы поместили в переменную y
Теперь выведем в консоль переменные x и y
в переменной x находится точная копия функции getArguments
а в переменной y - строка "Привет!"
Вызовем функцию x: x ( "До свидания!" )
и получим строку "До свидания!"

🔧 как мы можем использовать arguments.callee

⤴️ ⤵️
Мы знаем, что функция является объектом 
Значит, у нее могут быть свойства
предположим, мы хотим динамически определять 
свойства объекта-функции 
внутри самой функции

Объявим функцию, которая "сама себя лечит",
т.е. сама добавляет себе свойства и методы:

☕ 3

function setProperty ( prop, val ) {
    arguments.callee [ prop ] = val
}
Теперь заставим ее создать себе парочку свойств:
setProperty ( "isActive", false )
setProperty ( "value", 50 )
Ну, и для пущей убедительности 
заставим ее создать себе метод:
setProperty ( "method", function () {
    console.log ( "А еще я умею вышивать крестиком" )
} ) 

здесь мы передаем ей в качестве второго аргумента функцию

Теперь проверим, что эти свойства и метод появились у функции setProperty

Выведем в консоль свойства isActive и value функции setProperty и вызовем ее метод method

☕ 4

⤴️ ⤵️

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

Пусть это будет функция, вычисляющая факториал числа

var factorial = function ( num ) {
        var res = 1, n = 1
        while ( n <= num )  res *= n++
}

"модифицируем" ее следующим образом:

var factorial = function ( num ) {
     if ( !arguments.callee.res )  arguments.callee.res = []
     var res = 1, n = 1
     while ( n <= num )  res *= n++
     arguments.callee.res.push ( res )
     return res
}

☕ 5

⤴️ ⤵️
var buttons = []
for ( var n = 0; n < 5; n++ ) {
    buttons [ n ] = document.body.appendChild ( 
          document.createElement( 'button' ) 
    )
    buttons [ n ].innerText = n
    buttons [ n ].onclick = function ( event ) {
       if ( !arguments.callee.res )
             arguments.callee.res = []
       arguments.callee.res.push ( event.timeStamp )
             console.log ( arguments.callee.res )
    }
}
В этом примере создаются анонимные функции,
которые обрабатывают событие click  кнопок
Каждая функция "накапливает" данные
о времени клика на кнопке
в массиве arguments.callee.res
Модифицируем этот код:
var buttons = []
for ( var n = 0; n < 5; n++ ) {
    buttons [ n ] = document.body.appendChild ( 
             document.createElement( 'button' ) 
    )
    buttons [ n ].innerText = n
    buttons [ n ].onclick = function ( event ) {
        if ( !arguments.callee.clicksTime )
            arguments.callee.clicksTime = []
        arguments.callee.clicksTime.push ( event.timeStamp )
        console.log ( arguments.callee.clicksTime )
        arguments.callee.res = arguments.callee.clicksTime.length > 1 ? 
            arguments.callee.clicksTime [ arguments.callee.clicksTime.length - 1 ] -
            arguments.callee.clicksTime [ arguments.callee.clicksTime.length - 2 ] : 0

        console.info ( `Интервал между последними кликами: ${arguments.callee.res}` )
    }
}
Что теперь делает каждый обработчик
клика на кнопке ?

🎓 КОНТЕКСТ ИСПОЛНЕНИЯ

⤴️ ⤵️
Когда происходит вызов функции, она активируется
Ей нужно где-то безопасно "разместить" 
свои данные, с которыми она будет работать

Кроме аргументов, которые она получит при вызове, 
у нее могут быть свои внутренние данные, 
которые нужны для временного хранения 
промежуточных результатов вычислений

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

⚠️ Контекст выполнения создается каждый раз, когда происходит вызов функции

Что же будет в этом контексте?

✅ При вызове функции создается объект, содержащий все необходимые переменные

Этот объект в спецификации языка называется LexicalEnvironment

В этом объекте будут храниться полученные аргументы и все переменные функции

поэтому его еще называют объектом переменных или объектом активации

Когда функция завершит работу, объект LexicalEnvironment будет удален из памяти

✅ Однако функция может использовать какие-то переменные, которых нет в ее LexicalEnvironment

Они являются внешними, и находятся в другом контексте

Но они доступны функции

Функция "видит" их, поэтому они находятся в ее "области видимости"

⚠️ Область видимости - важная часть контекста выполнения

⚠️ Каждый вызов функции приводит к созданию нового контекста выполнения

Создание контекста выполнения происходит в два этапа:

✅ сразу после вызова функции, 
    но до начала выполнения кода
✅ на этапе выполнения кода

При каждом возврате ( return ) происходит выход из контекста выполнения

Пока выполнение функции не завершено, ее контекст будет активным

Поскольку функции могут вызывать друг друга, их контекст помещается в стек

( очередь: последним пришел - первым ушел )

Верхним в этом стеке всегда будет текущий контекст исполнения

📒 Lexical Environment

⤴️ ⤵️

Объект активации ( Lexical Environment ) содержит аргументы функции и все объявленные внутри функции переменные ( включая функции )

Таким образом, объект активации можно сравнить со шкафчиком для хранения "личных вещей" функции

⚠️ Получить доступ к объекту активации невозможно 

📒 hoisting

⤴️ ⤵️

1️⃣ На первом этапе формирования контекста исполнения:

  • создается объект переменных ( или объект активации ),
  • определяется область видимости
  • устанавливается значение this

2️⃣ На втором этапе ( выполнения ) внутренним переменным присваиваются значения, код интерпретируется и выполняется

Обратите внимание на тот факт, что объявления всех внутренних переменных и вложенных функций происходит на первом этапе, независимо от порядка их появления в коде

А вот присвоение переменным значений происходит на втором этапе, когда код начинает выполняться

Это приводит к "поднятию" ( hoisting ) объявлений переменных и функций

☕ 1

⤴️ ⤵️
function delegat () {
    console.log ( x )
    y = x + 5
    console.log ( y )
    x = 5,    y = 10
    return  x * 4 +  y / 2
    var x = 1, y = 1
}
Хотя объявление переменных x и y стоит в коде после оператора return, при формировании контекста исполнения на первом этапе будет формироваться объект активации ( Lexical Environment ), и все переменные, объявленные внутри функции, будут включены в этот объект
Таким образом, объявление переменных x и y "поднимется" ( произойдет до начала выполнения кода функции )
Но присваивание значений происходит уже на втором этапе, и код будет выполняться последовательно, то есть на момент исполнения кода console.log ( x ) значение переменной x еще не будет определено, поэтому в консоли будет undefined
Аналогично, на момент выполнения кода y = x + 5 значение переменной x будет undefined, поэтому результатом операции присваивания y = x + 5 будет NaN, что и выдаст в консоль код console.log ( y )
После этого будет выполнен код x = 5, y = 10
Переменные x и y получат значения
Поэтому функция вернет значение 25
Присваивание значений x = 1, y = 1 не произойдет, поскольку выход из контекста выполнения будет раньше этого кода

☕ 2

⤴️ ⤵️
var treg = 5

function delegat () {
        treg = 10
        return
        function treg (  ) {
                return
        }
}
delegat ()
console.log ( treg )  // 5
В этом случае объявление функции treg всплывает на этапе создания объекта активации, поэтому переменная treg окажется объявленной в контексте функции delegat, и не затронет переменную treg, объявленную в глобальном контексте
Это будут разные переменные, хотя идентификаторы у них совпадают
Поэтому в результате в консоли будет 5

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

⤴️ ⤵️

Область видимости ( scope ) ограничивает действие идентификаторов переменных и функций

Представьте себе двух человек по имени Саша:
👨‍💼 парня и 🙎 девушку

Есть две комнаты, 
и парень Саша  👨‍💼 находится в первой комнате, 
а девушка Саша 🙎 - во второй

В каждой комнате есть наблюдатель

Если мы спросим наблюдателя из первой комнаты: 
"Кем является Саша?", 
то он ответит: "Парень" 👨‍💼

Зададим аналогичный вопрос наблюдателю из второй комнаты, 
и получим ответ: "Девушка" 🙎

Это происходит потому, что у каждой комнаты есть 
своя область видимости

Однако область видимости вложенных функций 
будет несколько иной

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

Наблюдатель в коробке 2 будет видеть 
не только содержимое коробки 2, 
но и содержимое коробки 1 
и комнаты, 
внутри которой находятся все коробки

но он не может увидеть содержимое коробки 3, 
хотя наблюдатель в коробке 3 его отлично видит... 
как и наблюдателей во всех остальных коробках 
и в комнате

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

⚠️ Все доступные ей чужие "шкафчики" 
представляют собой  
📌 цепочку областей видимости функции,
которая является частью 
ее 📌 контекста выполнения

☕ 3

⤴️ ⤵️
var __num = 1
function changeNum () {
   __num = 10
}
changeNum ()
переменная __num объявлена в глобальной области видимости, в которой также объявлена функция changeNum
При объявлении переменной __num присвоено значение 1
Поскольку внутри функции changeNum нет объявления переменной __num, то при формировании контекста выполнения функции changeNum эта переменная не попадает в объект активации ( "шкафчик для личных вещей" ) функции changeNum
Тогда при выполнении присваивания __num = 10 происходит следующее:
функция changeNum, не найдя такой переменной в собственном "шкафчике", обращается к внешнему "шкафчику", где такая переменная есть, ей-то и будет присвоено значение 10
Таким образом, для каждого контекста выполнения существует своя цепочка областей видимости
Цепочка областей видимости включает области видимости всех предыдущих контекстов в стеке

☕ 4

⤴️ ⤵️
var __num = 1
function __showNum () {
        console.info ( "Вошли в контекст исполнения функции __showNum" )
        console.info ( `__num === ${__num}` )
        return
        function __num () {}
}
__showNum ()
console.info ( "Вышли из контекста исполнения функции __showNum" )
console.info ( `Теперь __num === ${__num}` )
В этом примере показано, как работает механизм hoisting
Переменная __num объявляется в глобальной области видимости с присвоением ей значения 1
Внутри тела функции __showNum после оператора return объявляется функция __num
На первый взгляд, при последовательном выполнении кода это объявление не должно сработать, поскольку оператор return стоит выше
Однако все объявления собираются в объект активации до того, как код начинает выполняться
Поэтому на момент, когда начнется выполнение кода функции __showNum (), функция __num () будет уже объявлена и будет благополучно находиться в объекте активации функции __showNum ()
Благодаря этому объявления переменных и функций "поднимаются" в области видимости "родителя" ( в нашем случае "родителем" является функция __showNum () )
Убедиться в этом позволяет вывод в консоль переменной __num
После завершения выполнения кода функции __showNum () ее контекст будет "демонтирован", и опять активным станет глобальный контекст, в котором переменная __num имеет значение 1

☕ 5

⤴️ ⤵️
var __num = 1
function __showNum () {
    console.info ( "Вошли в контекст исполнения функции __showNum" )
    console.info ( `(1) ${__num}` )
    __num ()
    console.info ( `(2) ${__num}` )
    __num = 10
    console.info ( `(3) ${__num}` )
    return
    function __num () { __num = 5 }
}
__showNum ()
console.info ( `( global ) ${__num}` )
  Здесь так же происходит поднятие объявления функции __num ()
  Но функция __num () переопределяет значение переменной __num
  В контексте выполнения функции __showNum на момент присваивания переменной __num значения 5 нет другой объявленной переменной __num, кроме самой функции __num ()
  Таким образом, до вызова функции __num () в консоль будет выведена функция
  После вызова функции __num () в консоль будет выведено новое значение ( 5 ) переменной __num
  После этого будет выполнено присваивание нового значения переменной __num и вывод в консоль ( 10 )
  Когда функция __showNum завершит работу, и ее контекст будет "демонтирован", станет активным глобальный контекст, в котором переменная __num имеет значение 1
  В этом примере функция __num () переопределила саму себя ( была "function", стала "number" )

📒 this

⤴️ ⤵️

this - это еще одна составляющая контекста исполнения функции

this является ссылкой на контекст вызова функции

С помощью ключевого слова this можно получить доступ из функции ( или метода ) к свойствам объекта, в контексте которого была вызвана функция

☕ 6

function func () {
   console.log ( this )
}

при вызове функции func () в консоль будет выведен объект window

Внутри функции func () this указывает на объект window

☕ 7

function func () {
   child ()
   function child () {
      console.log ( 'child this: ', this )
   }
}

func ()  // window

☕ 8

Если же функция является методом объекта, то ее контекстом вызова будет этот объект

var human = {
    name: "Ivan",
    say: function () {
        console.log ( 'this: ', this )
    }
}

human.say () // будет выведен объект  human

☕ 9

Теперь посмотрим на функцию как на объект

function say () {
   console.log ( 'function say: this: ', this )
}
function girl () {
   console.log ( 'function girl: this: ', this )
}

Добавим функции girl свойство say:

girl.say = say

А теперь вызовем свойство say функции girl:

girl.say ()  //  girl

и саму функцию girl:

girl ()     // window

🔗 w3schools

⤴️

💼 Упражнения

© Irina H.Fylyppova 2018
Использование данных материалов или любой их части коммерческими школами ( курсами ) является нарушением авторских прав


Новая версия


1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19

Занятие 1

⤵️

Занятие 2

⤴️ ⤵️

Занятие 3

⤴️ ⤵️

Занятие 4

⤴️ ⤵️

Занятие 5

⤴️ ⤵️

Занятие 6

⤴️ ⤵️

Занятие 7

⤴️ ⤵️

Занятие 8

⤴️ ⤵️

Занятие 9

⤴️ ⤵️

Занятие 10

⤴️ ⤵️

Занятие 11

⤴️ ⤵️

Занятие 12

⤴️ ⤵️

Занятие 13

⤴️ ⤵️

Занятие 14

⤴️ ⤵️

Занятие 15

⤴️ ⤵️

Занятие 16

⤴️ ⤵️

Занятие 17

⤴️ ⤵️

Занятие 18

⤴️ ⤵️

Занятие 19

⤴️ ⤵️

⤴️

ico20 Дополнительно
dir-20 Справочная инфо

Clone this wiki locally