Эта статья является второй частью моего обсуждения концепции объектов в Javascript. Если вы не заценили первую часть, то тут! Обычно при изучении javascript функции и объекты рассматриваются как два разных понятия. Однако Javascript не похож на большинство языков программирования с точки зрения того, как он обрабатывает функции. В этой части этой серии вы узнаете, что функции на самом деле являются объектами.

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

Example
Functions as constants
let fn = function doSomething() {console.log('something')}
console.log(fn) // function doSomething() {console.log('something')}
fn() // 'something'
Functions as values of keys of an object
const printer = { print: (value) => console.log(value)}; 
printer.print("mic check")
Functions as array items
const add = (x,y) => x + y;
const subtract = (x,y) => x - y;
const multiply = (x,y) => x * y; 
const arrayOfFunctions = [add, subtract, multiply];
console.log(arrayOfFunctions[0](1,1)) // 2
Higher Order Functions
const timeout = () => setTimeout(() => alert("WoW"), 1000)

Вышеупомянутая концепция порождает парадигму программирования, называемую функциональным программированием. В функциональном программировании очень распространены функции высшего порядка. Функция высшего порядка – это функция, которая принимает функцию в качестве аргумента или возвращает функцию. Я надеюсь, что теперь вы понимаете, почему некоторые методы массива называются методами массива более высокого порядка.

Функции как объекты

Как и любой объект, когда функция создается, она находится в памяти Javascript и, как и любой объект, может иметь свойства и методы. По умолчанию функция имеет некоторые свойства, о которых вы можете не знать. У него есть свойство имени. Это свойство является необязательным, так как не все функции имеют имя (анонимные функции).

Функция имеет свойство кода. Это свойство неверно истолковано для самой функции. Однако функция НЕ является вызываемым фрагментом кода, а функция имеет вызываемое свойство кода.

function doSomething () {
console.log('I will do something')
}
doSomething === name property
console.log('I will do something') === code property(The invocable part of a function)

Более того,

function me() {
}
me.name = "Bryan"
me.hobbies = function () {
console.log('I write programs with Javascript')
}
console.log(me.name) //Bryan
me.hobbies() //'I write programs with Javascript'

В приведенном выше примере показано, что вы можете добавлять свойства и методы к функции. Это означает, что функция ЯВЛЯЕТСЯ ОБЪЕКТОМ. Важно, чтобы у вас была эта мысленная модель функции, поскольку она дает лучшее понимание того, как Javascript работает под капотом. Функция — это не просто контейнер кода. Это нечто большее. В Javascript это объект.

Выражение функции и оператор функции.

Иногда функция может быть оператором, иногда это может быть выражение. Выражение в Javascript возвращает значение, а оператор — нет. Функциональный оператор может быть вызван в любом месте кода. Возможно, это не новость для вас, потому что вы знаете, что функция может быть вызвана в любом месте программы. Однако вызов функционального оператора может быть сделан лексически над самой функцией.

statement()
//This is a function statement.
function statement () {
console.log('This is a functional statement')
}

Однако функциональное выражение должно вызываться лексически ниже функции.

expression()
//expression is not defined
let expression = function() {
console.log('This is a functional statement')
}

Если описанное выше поведение кажется вам странным. Вот что происходит под капотом движка Javascript. javascript, который мы пишем, проходит две фазы во время выполнения.

Фаза создания и фаза исполнения. На этапе создания Javascript в основном помещает все переменные и функции в память. Он изначально присваивает undefined переменным и помещает функции в память. Этот процесс известен как подъем. Итак, когда функция вызывается на этапе выполнения, она уже находится в памяти, поэтому лексическая позиция вызова функции не имеет значения.

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

Подробнее о ключевом слове this и функциях жирной стрелки!

Я говорил о ключевом слове «это» в предыдущей статье. Тем не менее, я хочу объяснить это в контексте функции жирной стрелки es6.

Ключевое слово this в Javascript может ссылаться на разные места в памяти в зависимости от контекста.

function a() {
console.log(this);
this.newvariable = 'hello';
}
var b = function() {
console.log(this);
}
a();
b();

В приведенном выше примере ключевое слово this указывает на глобальный объект в Javascript. Это означает, что вызов this добавит методы и свойства к глобальному объекту Javascript.

var c = {
name: 'The c object',
log: function() {
this.name = 'Updated c object';
console.log(this)
}
}
c.log();

Как вы уже догадались, все, что находится слева от оператора точки, будет называться «это». Следовательно, ссылкой на this будет объект c. Если вы это ясно понимаете, то результат следующего примера будет несколько сбивать с толку.

var c = {
name: 'The c object',
log: function() {
this.name = 'Updated c object';
var setname = function(newname) {
this.name = newname;
}
setname('Updated again! The c object');
console.log(this);
}
}

Обратите внимание, что вызов функции setname не обновил свойство имени объекта c. Это странное поведение в Javascript. Всякий раз, когда вы добавляете функцию внутри метода объекта, ключевое слово this ссылается на глобальный объект. Это плохо для Javascript и может быть очень запутанным. Javascript великолепен, но у него есть свои недостатки.

Есть два основных способа обхода. Первый метод исходит из того, что объекты хранятся по ссылкам в Javascript. Это означает, что мы можем сохранить ссылку на правильное «это» в другой переменной, как показано в приведенном ниже примере.

var c = {
name: 'The c object',
log: function() {
var self = this;
self.name = 'Updated c object';
console.log(self);
var setname = function(newname) {
self.name = newname;
}
setname('Updated again! The c object');
console.log(self);
}
}
c.log();

Приведенный выше пример ссылается на точное «это» в var self. Это означает, что у нас всегда есть правильная ссылка в методе log. Это довольно ловкий трюк для решения проблемы. Второй способ решить эту проблему — использовать функцию жирной стрелки.

var c = {
name: 'The c object',
log: function() {
this.name = 'Updated c object';
console.log(this);
var setname = (newname) => {
this.name = newname
console.log(this)
}
setname('Updated again! The c object')
}
}
c.log();

Функция жирной стрелки es6 была введена, чтобы обуздать проблему неправильного обращения к ключевому слову «this» в функциях в методах. Ссылка this в функции с жирной стрелкой наследуется от ближайшего объекта вне области действия функции.

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

Немедленно вызываемые функциональные выражения (IIFE)

Как следует из названия, IIFE — это функции, которые выполняются или вызываются сразу после создания. Немедленно могут быть вызваны только функциональные выражения. Однако оператор функции можно преобразовать в выражение, заключив его в круглые скобки. Делая это, вы обманываете анализатор синтаксиса Javascript, чтобы анализировать функцию как выражение, а не оператор, который затем сразу же вызывается.

// function statement
function greet(name) {
console.log('Hello ' + name);
}
greet('John');
// using a function expression
var greetFunc = function(name) {
console.log('Hello ' + name);
};
greetFunc('John');
// using an Immediately Invoked Function Expression (IIFE)
var greeting = function(name) {
return 'Hello ' + name;
}('John');
console.log(greeting);
// IIFE converting function statement to expression with paranthesis
var firstname = 'John';
(function(name) {
var greeting = 'Inside IIFE: Hello';
console.log(greeting + ' ' + name);
}(firstname)); // IIFE

Вы, вероятно, скажете: «Ну, это приятно знать, но когда я действительно использую эту концепцию при создании приложения Javascript?» Эта концепция пригодится при написании безопасного кода. Безопасный код — это, по сути, способ защитить ваш код от переопределения переменных при использовании var.

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

Понимание замыканий

Закрытие, в данном случае, это не то, что вы получаете после того, как отношения закончились. Замыкание в Javascript — это тема, которая действительно проверит ваши знания о том, как Javascript работает внутри. Идея замыкания не может быть полностью понята без понимания фазы выполнения Javascript контекста выполнения движка.

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

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

EXAMPLE 1
function greet(whattosay) {
return function(name) {
console.log(whattosay + ' ' + name);
}
}
var sayHi = greet('Hello');
sayHi('Bryan') OR greet('Hello')('Bryan')
EXAMPLE 2
function makeGreeting(language) {
return function(firstname, lastname) {
if (language === 'en') {
console.log('Hello ' + firstname + ' ' + lastname);
}
if (language === 'es') {
console.log('Hola ' + firstname + ' ' + lastname);
}

}

}
var greetEnglish = makeGreeting('en');
var greetSpanish = makeGreeting('es');
greetEnglish('John', 'Doe');
greetSpanish('John', 'Doe');

В приведенных выше примерах видно, что даже после удаления контекста выполнения родительских функций дочерние функции по-прежнему имеют доступ к родительским переменным. Javascript, определяющий область действия функции или заключающий функцию в необходимые переменные, называется замыканием. Вот, собственно, и все, что нужно для закрытия!

Привязать, позвонить и подать заявку

Как было сказано ранее, функции являются объектами. И как объекты они имеют различные методы и свойства. bind(), call(), apply() — это наиболее распространенные методы, которые вы будете использовать. Все эти методы при вызове принимают параметр (объект), который должен заменить ссылку на переменную «this».

.Привязать

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

Example 1
var person = {
firstname: 'John',
lastname: 'Doe',
getFullName: function() {
var fullname = this.firstname + ' ' + this.lastname;
return fullname;
}
}
var logName = function(lang1, lang2) {
console.log('Logged: ' + this.getFullName());
console.log('Arguments: ' + lang1 + ' ' + lang2)
}
var logPersonName = logName.bind(person);
logPersonName('en');
Example 2
function multiply(a, b) {
return a*b;
}
var multipleByTwo = multiply.bind(this, 2);
console.log(multipleByTwo(4));
//8

.Позвонить и .Подать заявку

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

var person = {
firstname: 'John',
lastname: 'Doe',
getFullName: function() {
var fullname = this.firstname + ' ' + this.lastname;
return fullname;
}
}
var logName = function(lang1, lang2) {
console.log(this.getFullName());
}
var logPersonName = logName.bind(person);
logPersonName('en');
logName.call(person, 'en', 'es');
logName.apply(person, ['en', 'es']);

Я знаю, что это была довольно длинная статья, но я надеюсь, что вы нашли ее полезной. Пожалуйста, оставьте любые вопросы, которые могут у вас возникнуть, в разделе комментариев. Если вы нашли это достойным аплодисментов, дайте несколько или больше! Спасибо за чтение!