больше того же….
больше информации о функциях
У нас могут быть параметры «по умолчанию» для функций. Один из способов добиться этого — использовать || короткое замыкание оператора. Например…
const restuarantBookings = []; function createBooking(firstName, secondName, time, vegetarian) { // short circuiting to provide a default value if not passed into the function as an argument vegetarian = vegetarian || "not specified"; const booking = { firstName, secondName, time, vegetarian }; restuarantBookings.push(booking); }; createBooking("Paul", "Smith", 22.30); console.log(restuarantBookings);
Таким образом, мы создали значение по умолчанию для вегетарианца «не указано», если не было подтверждено, является ли человек вегетарианцем или нет.
Этот способ сделать это был старым способом.
Новый способ заключается в следующем….
const restuarantBookings = []; function createBooking(firstName, secondName, time, vegetarian = "not specified") { const booking = { firstName, secondName, time, vegetarian }; restuarantBookings.push(booking); }; createBooking("Paul", "Smith", 22.30); console.log(restuarantBookings);
Мы также можем динамически вычислять значения для помещения в массив на основе аргументов. Например…
const restuarantBookings = []; function createBooking( vegetarian = "not specified", birthday = false, discount = (birthday ? 10 : 0) ) { const booking = { vegetarian, birthday, discount }; restuarantBookings.push(booking); }; createBooking(true); console.log(restuarantBookings);
В приведенном выше коде мы передали один аргумент (true) в функцию createBooking, которая указывала, был ли человек вегетарианцем, но мы не передавали второй аргумент, чтобы сказать, был ли это день рождения человека, и, следовательно, тернарный оператор , в основном подсчитали, что скидка на еду должна быть нулевой по умолчанию.
{vegetarian: true, birthday: false, discount: 0}
Если, с другой стороны, мы передаем второй аргумент как true, то значение будет динамически обновляться…
const restuarantBookings = []; function createBooking( vegetarian = "not specified", birthday = false, discount = (birthday ? 10 : 0) ) { const booking = { vegetarian, birthday, discount }; restuarantBookings.push(booking); }; createBooking(true, true); console.log(restuarantBookings);
что приводит к этому….
{vegetarian: true, birthday: true, discount: 10}
Это нормально, когда параметр по умолчанию является последним параметром, но что, если мы хотим пропустить параметры в середине?
В этом случае мы можем просто передать undefined в качестве аргумента функции там, где мы хотим пропустить…
const restuarantBookings = []; function createBooking(firstName, secondName = "no surname", time = "time not confirmed", vegetarian = "not specified") { const booking = { firstName, secondName, time, vegetarian }; restuarantBookings.push(booking); }; createBooking("Paul", undefined, undefined, true); console.log(restuarantBookings);
который дает результат…
{firstName: 'Paul', secondName: 'no surname', time: 'time not confirmed', vegetarian: true}
Примитивные значения против объектов внутри функций
Если мы передаем в функцию примитивное значение, то само значение копируется, однако, если мы передаем объект в функцию, а затем изменяем его, мы фактически изменяем сам объект.
например…
// primitive value - copied const birthYear = 1985; // object - changed by function const person = { name: "bob", age: 21, male: true } // first confirm values in console log console.log(birthYear); console.log(person.name); // passing these into a function const personData = function (personsBirthYear, personsName) { personsBirthYear = 1990; personsName.name = "fred"; } personData(birthYear, person); // re-check values in console log console.log(birthYear); console.log(person.name);
это возвращается….
1985 bob 1985 fred
Таким образом, мы передали в функцию «personData» два аргумента («birthYear», являющееся примитивным значением, и «person», являющееся объектом), и функция personData использовала их в качестве двух параметров.
Внутри функции «personData» мы присвоили 1990 человекуBirthYear (которое было передано как переменная BirthdayYear), а также мы присвоили «fred» для personName.name (которое было передано как объект person)
Вы можете видеть, что когда мы повторно проверяем значения примитивной переменной 'birthYear', они не изменились, потому что значение внутри функции является просто копией, локальной для функции, но фактическим значением для человека. Ключ имени теперь изменился, поэтому он доказывает, что когда вы ссылаетесь на объекты и манипулируете ими внутри функций, это напрямую влияет на сам объект.
Функции высшего порядка
Функции более высокого порядка могут вызывать другие функции для выполнения работы за них.
Например…
const lowerCaseChanger = function(str) { return str.toLowerCase(); } // Higher Order function const transformer = function(str, fn) { console.log(`my transformed string is: ${fn(str)}`); } transformer("HELLO WORLD", lowerCaseChanger);
поэтому функция преобразования ожидает два аргумента, строку и функцию, поэтому мы предоставляем строку для изменения и имя функции, которая будет выполнять работу.
Таким образом, строка преобразуется в нижний регистр, а затем может использоваться в функции «трансформер».
Так что это в принципе то же самое…
console.log(`my transformed string is: ${lowerCaseChanger("HELLO WORLD")}`);
функции обратного вызова
функции обратного вызова — это именно то, на что они похожи… функции, вызываемые, когда что-то происходит
Например…
const makeScreenTurnRed = function() { document.querySelector("body").style.backgroundColor = "red"; } // callback function when button is clicked document.querySelector(".button").addEventListener("click", makeScreenTurnRed);
Таким образом, в приведенном выше примере addEventListener() является функцией более высокого порядка, а makeScreenTurnRed — функцией обратного вызова.
функции, возвращающие функции
Функция может «вернуть» другую функцию. При этом вы можете эффективно присвоить «возврат» (то есть функцию) переменной, что означает, что затем вы можете вызывать переменную как функцию.
Например…
const greet = function(greeting) { return function (personsName){ console.log(`${greeting} ${personsName}!`); } }; // now assign greet function with "Welcome" as argument to variable const greeter = greet("Welcome"); // now call this variable with an argument for personsName greeter("Bob");
который возвращает «Добро пожаловать, Боб!»
Эту функцию приветствия также можно было бы сократить здесь с помощью функций со стрелками… так что в основном это точно так же, как это….
const greet = greeting => personsName => console.log(`${greeting} ${personsName}!`); // now assign greet function with "Welcome" as argument to variable const greeter = greet("Welcome"); // now call this variable with an argument for personsName greeter("Bob");
методы call() apply() bind()
вызов()
Метод call() можно использовать, когда мы хотим указать конкретный объект, когда мы сталкиваемся с несколькими объектами, некоторые из которых могут иметь функции.
Объекты, в которых есть функции, могут использовать ключевое слово this для ссылки на значения внутри одного и того же объекта, однако, если мы назначаем функцию в объекте переменной, а затем вызываем функцию, мы пытаемся использовать функцию который использует ключевое слово this, которое не будет работать.
Это имеет больше смысла на примере.
Следующее НЕ будет работать, потому что, когда мы пытаемся вызвать функцию bookTable, мы пытаемся использовать this в функциональном выражении, которое не может использовать ключевое слово this.
const restaurantOne = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [], // function in the object book: function(time, day) { console.log(`${this.firstName} ${this.lastName} booked a meal at ${time} on ${day}`); this.bookings.push(`reservation made for ${this.firstName} at ${time}`); } }; const restaurantTwo = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [] }; // assign the function in first object to variable... const bookTable = restaurantOne.book //now attempt to book with the function variable... bookTable(2230, "wednesday");
Чтобы приведенный выше код работал, нам нужно использовать метод call(), чтобы явно указать, что мы хотим использовать функциональное выражение в объекте restaurantOne.
const restaurantOne = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [], // function in the object book: function(time, day) { console.log(`${this.firstName} ${this.lastName} booked a meal at ${time} on ${day}`); this.bookings.push(`reservation made for ${this.firstName} at ${time}`); } }; const restaurantTwo = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [] }; // assign the function in first object to variable... const bookTable = restaurantOne.book //now attempt to book with the function variable... bookTable.call(restaurantOne, 2230, "wednesday");
Который возвращает…
bob smith booked a meal at 2230 on wednesday
Мы также можем подтвердить, что функция отправила информацию в массив в объекте «restaurantOne».
console.log(restaurantOne.bookings);
который возвращает…
['reservation made for bob at 2230']
Использование этого метода вызова теперь означает, что мы можем воспользоваться преимуществами выражения функции, даже если мы вызываем другой объект.
Например…
const restaurantOne = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [], // function in the object book: function(time, day) { console.log(`${this.firstName} ${this.lastName} booked a meal at ${time} on ${day}`); this.bookings.push(`reservation made for ${this.firstName} at ${time}`); } }; const restaurantTwo = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [] }; // assign the function in first object to variable... const bookTable = restaurantOne.book //now attempt to book with the function variable... bookTable.call(restaurantTwo, 1730, "friday"); console.log(restaurantTwo.bookings);
который возвращает в консоль следующее...
bob smith booked a meal at 1730 on friday ['reservation made for bob at 1730']
Таким образом, мы видим, что выражение функции в объекте один использовалось для передачи в массив объекта два!
Метод Применить()
Метод применения очень похож на метод вызова, однако вместо того, чтобы передавать отдельные аргументы, вы передаете массив значений.
Например…
const restaurantOne = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [], // function in the object book: function(time, day) { console.log(`${this.firstName} ${this.lastName} booked a meal at ${time} on ${day}`); this.bookings.push(`reservation made for ${this.firstName} at ${time}`); } }; const restaurantTwo = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [] }; // assign the function in first object to variable... const bookTable = restaurantOne.book // an array of the data you wish to pass in const guestInfo = [1415, "sunday"]; //now attempt to book with the function variable... bookTable.apply(restaurantOne, guestInfo); console.log(restaurantOne.bookings);
Однако это больше не используется очень часто, так как вы можете просто использовать оператор распространения ‘…’ с методом call().
const restaurantOne = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [], // function in the object book: function(time, day) { console.log(`${this.firstName} ${this.lastName} booked a meal at ${time} on ${day}`); this.bookings.push(`reservation made for ${this.firstName} at ${time}`); } }; const restaurantTwo = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [] }; // assign the function in first object to variable... const bookTable = restaurantOne.book // an array of the data you wish to pass in const guestInfo = [1415, "sunday"]; //now attempt to book with the function variable... bookTable.call(restaurantOne, ...guestInfo); console.log(restaurantOne.bookings);
связывать()
Bind не вызывает функцию в объекте, а вместо этого «привязывает» функцию к конкретному объекту. На самом деле это создает совершенно новую функцию каждый раз, когда вы привязываете ее к объекту.
Например…
const restaurantOne = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [], // function in the object book: function(time, day) { console.log(`${this.firstName} ${this.lastName} booked a meal at ${time} on ${day}`); this.bookings.push(`reservation made for ${this.firstName} at ${time}`); } }; const restaurantTwo = { firstName: "bob", lastName: "smith", vegetarian: true, bookings: [] }; // assign the function in first object to variable... const bookTable = restaurantOne.book // now create (bind) the function to the objects... const bookRestaurantOne = bookTable.bind(restaurantOne); const bookRestaurantTwo = bookTable.bind(restaurantTwo); // now just use the one you want... bookRestaurantOne(2245, "tuesday"); bookRestaurantTwo(1120, "friday"); console.log(restaurantOne.bookings); console.log(restaurantTwo.bookings);
который возвращает….
bob smith booked a meal at 2245 on tuesday bob smith booked a meal at 1120 on friday ['reservation made for bob at 2245'] ['reservation made for bob at 1120']
Вы также можете использовать привязку для создания новых функций, которые будут включать некоторые предустановленные значения, которые могут помочь в создании вариантов функций.
например, с объявлением функции (не внутри какого-либо объекта):
// simple calculator function const calculator = function(x,y,z) { return (x + y) * z; } // now assign the function to a variable const calcFunc = calculator; // now use bind method to assign to a variable with preset values (2 and 3) and null used as the first argument, as there is no object to reference! const presetValueCalc = calcFunc.bind(null, 2, 3); // now use the function with the default variables console.log(presetValueCalc(4));
который возвращает…
20 20
В этом случае мы использовали «null» в качестве первого аргумента, так как метод привязки ожидает, что «this» будет указано в качестве первого аргумента, который является объектом, к которому будет привязана функция, однако функция не привязана к объект, поэтому мы используем null, чтобы эффективно пропустить этот первый параметр.
Вот очень похожий код, только на этот раз функция находится внутри объекта, поэтому в этом случае первый параметр в методе привязки указывает объект, к которому должна быть привязана функция….
// calculator function INSIDE an object const calculatorObjectOne = { name: "my first calculator object", calculator: function(x,y,z) { return (x + y) * z; } }; // another object to deomonstrate binding... const calculatorObjectTwo = { name: "my second calculator object", }; // now assign the function in the first object to a variable const useCalc = calculatorObjectOne.calculator; // now bind the function to both these objects, with the first argument being the 'this' which is the object to bind to const calcDefaultOne = useCalc.bind(calculatorObjectOne, 2,3); const calcDefaultTwo = useCalc.bind(calculatorObjectTwo, 2,3); // now use the function from both objects console.log(calcDefaultOne(4)); console.log(calcDefaultTwo(4));
Что возвращает тот же результат, только на этот раз мы указали объект для привязки в качестве первого параметра.
IIFE (выражение немедленно вызываемой функции)
Функции IIFE запускаются только ОДИН РАЗ!
По сравнению с обычным объявлением функции, которое можно вызывать несколько раз, IIF вызывается один раз и сразу…
// normal function declaration which can be called multiple times const normalFunction = function() { console.log("called!"); } // call the function as many times as you want! normalFunction(); normalFunction(); normalFunction(); normalFunction(); normalFunction(); // IIFE function - called only once! (function () { console.log("called only once...immediately!"); })();
IIFE не может быть вызван снова, так как не на что ссылаться!
Обратите внимание на специальный синтаксис IIFE, заключающийся в заключении всего функционального выражения в (), а затем в конце другого ()
Закрытия
Замыкания позволяют функции иметь «приватные» переменные. Закрытие — это функция, имеющая доступ к родительской области видимости даже после закрытия родительской функции.
Чтобы лучше понять это, лучше всего показать пример проблемы, когда следующий код не будет работать….
// Function to increment counter function add() { let counter = 0; counter += 1; console.log(counter); } // Call add() 3 times add(); add(); add();
На первый взгляд может показаться, что в журнале консоли ожидается следующее:
1 2 3
Но это совсем не так... Что вы увидите:
1 1 1
это связано с тем, что переменная счетчика будет увеличиваться до единицы, а затем она будет сохранена, однако после завершения этой функции эта переменная счетчика по существу больше не существует…. поэтому, когда вы снова вызываете функцию, используя add(), вы в основном начинаете сначала.
Переменная counter является локальной для функции. Это не глобальная переменная. Так, например, ожидаемый результат может быть получен путем перемещения переменной за пределы функции...
let counter = 0; // Function to increment counter function add() { counter += 1; console.log(counter); } // Call add() 3 times add(); add(); add();
Одна потенциальная проблема с этим подходом заключается в том, что вы потенциально можете изменить значение счетчика с помощью совершенно другой функции, потому что эта переменная общедоступна для кода.
Эту «проблему» можно решить с помощью замыканий….
Ниже приведен пример вложения одной функции в другую функцию. «Вложенная» функция будет иметь доступ к переменной родительской функции. Таким образом, хотя сама переменная не является общедоступной «глобальной» переменной, она может оставаться частной, но вложенная функция по-прежнему будет иметь к ней доступ, даже если сама переменная не находится внутри вложенной функции.
// IIFE Function to increment counter const add = (function () { let counter = 0; // nested function return function () { counter += 1; console.log(counter) } })(); add(); add(); add();
Мы также могли бы присвоить полную функцию переменной, а затем вызвать эту переменную…
// Function to increment counter const add = function () { let counter = 0; // nested function return function () { counter += 1; console.log(counter) } }; // Assign function to variable const addFunction = add(); addFunction(); addFunction(); addFunction();
Это работает, потому что новая переменная addFunction имеет доступ к контексту выполнения функции add. Из-за этого он всегда имеет доступ к переменной counter.
вот почему это не сработало бы, если бы функция не была назначена переменной….
// Function to increment counter const add = function () { let counter = 0; // nested function return function () { counter += 1; console.log(counter) } }; // will NOT work as expected add(); add(); add();
Мы также можем выполнить console.dir для переменной addFunction, чтобы убедиться, что она действительно имеет доступ к переменной счетчика внутри области действия функции add.
console.dir(addFunction); anonymous() length: 0 name: "" prototype: {constructor: ƒ} arguments: (...) caller: (...) [[FunctionLocation]]: app.js:783 [[Prototype]]: ƒ () [[Scopes]]: Scopes[1] 0: Closure (add) {counter: 3}
[[Scopes]] — это внутреннее свойство, к которому нельзя получить доступ из нашего кода, но оно по-прежнему сохраняется в JavaScript.