Стрелочные функции в JavaScript
Предпосылка: это в JavaScript
В этом посте мы обсудили еще несколько связанных с этим функций в JavaScript.
this и стрелочные функции:
Стрелочные функции, представленные в ES6, обеспечивают краткий способ написания функций на JavaScript.
Еще одно существенное преимущество, которое он предлагает, заключается в том, что он не связывает себя this
. Другими словами, контекст внутри стрелочных функций определяется лексически или статически.
Что мы имеем в виду?
В отличие от других функций, значение this
внутренней стрелочной функции не зависит от того, как они вызываются или как они определены, а зависит только от включающего их контекста.
Попробуем разобраться на примере:
Выход:
[Объект Object] undefined не определено лет
Причина, по которой мы получаем неопределенные выходные данные вместо правильной информации в качестве выходных данных, происходит потому, что function()
определенная как обратный вызов для setTimeout, имеет нормальный вызов функции, и, как мы знаем, это означает, что ее контекст установлен в глобальный контекст или в другой слова значение this
устанавливается для объекта окна.
Это происходит потому, что каждая обычная функция без стрелок определяет свой собственный this
или context в зависимости от их вызова. Контекст включающих объектов / функций не влияет на эту тенденцию к автоматическому определению своего собственного контекста.
Как решить эту проблему?
На ум приходит одно очевидное решение: что, если функция не определила свой собственный контекст? Что если бы она унаследовала контекст от info()
, потому что это означало бы, что эта функция () получит this
как было определено в info()
Что ж, именно это и делают стрелочные функции: они сохраняют значение this
из включаемых в них
контекст.
То есть, в приведенном выше примере, если бы функция, определенная как обратный вызов для setTimeout()
была стрелочной функцией, она унаследовала бы значение this
из охватывающего его контекста - info()
<!DOCTYPE html> <html> <body> <script> let People = function (person, age) { this .person = person; this .age = age; this .info = function () { // logs People document.write( this ); setTimeout(() => { // arrow function to make lexical "this" binding // here this=People."this" has been inherited document.write( this .person + " is " + this .age + " years old" ); }, 3000); } } let person1 = new People( 'John' , 21); // logs : John is 21 years old after 3 seconds person1.info(); </script> </body> </html> |
Выход:
[Объект Object] Джону 21 год
Таким образом, независимо от того, вызывалась ли стрелочная функция с использованием вызова функции или вызова метода, она сохраняет значение] this
из своего окружающего контекста. Другими словами, this
же, как и непосредственно за ее пределами.
Если используется вне любой включающей функции, стрелочная функция наследует глобальный контекст, тем самым устанавливая значение this
для глобального объекта.
это в отдельных методах:
Когда метод из любого объекта отделен от него или сохраняется в переменной, например: let separated = People.info
, он теряет ссылку на вызывающий объект.
Обратите внимание на отсутствие открывающих и закрывающих круглых скобок после info
. Это указывает на то, что мы не вызываем метод немедленно.
Например :
<!DOCTYPE html> <html> <body> <script> let People = function (person, age) { this .person = person; this .age = age; this .info = function () { // logs People document.write( this + ' ' ); // here this=People document.write( this .person + " is " + this .age + " years old" + '<br>' ); } } let person1 = new People( 'John' , 21); // logs : John is 21 years old person1.info(); // separating the method info() from its // object by storing it in a variable let separated = person1.info; // logs : undefined is undefined years old separated(); </script> </body> </html> |
Выход:
[object Object] Джону 21 год [окно объекта] undefined не определено лет
Как только мы person1
info()
от объекта person1, сохранив его separated
, мы потеряем все ссылки на объект person1
Мы больше не можем получить доступ к родительскому объекту, используя this
внутри разделенного метода, потому что контекст разделенного метода сбрасывается в глобальный контекст.
Таким образом, когда мы называем separated()
мы видим , что this
теперь установлено на глобальный объект окна.
Как решить эту проблему?
Один из способов - bind
значение объекта с методом при хранении метода separated
. Это гарантирует, что все ссылки на this
ссылаются на этот связанный объект даже в отдельном методе.
Это можно сделать с помощью bind()
следующим образом:
<!DOCTYPE html> <html> <body> <script> let People = function (person, age) { this .person = person; this .age = age; this .info = function () { // logs People document.write( this + ' ' ); // here this=People document.write( this .person + " is " + this .age + " years old" + '<br>' ); } } let person1 = new People( 'John' , 21); // logs : John is 21 years old person1.info(); let separated = person1.info.bind(person1); /* the bind(person1) statement ensures that "this" always refers to person1 inside the bound method- info() */ // logs : undefined is undefined years old separated(); </script> </body> </html> |
Выход:
[object Object] Джону 21 год [object Object] Джону 21 год
Примечание: мы могли бы использовать любой объект для bind()
метода info()
вместо code.person1, и выходные данные изменились бы соответствующим образом. Не обязательно привязывать метод к его родительскому объекту.
Если вы зашли так далеко, то заметили бы, что мы пару раз bind()
Давайте теперь изучим, что на самом деле представляют собой bind(), call() and apply()
связать, позвонить и применить
bind (), call () и apply () используются для изменения контекста функции. Все три из них помогают явно указать, какое значение this
должно быть внутри функции.
Но есть определенные различия в том, как каждый из них работает. Давайте изучим эти различия.
связывать()
bind () позволяет нам явно определить, какое значение this
будет иметь внутри функции, привязав объект к этой функции.
Связанный объект служит контекстом ( this
значением) для функции, к которой он был привязан.
Чтобы использовать его, нам в первую очередь нужны две вещи: объект для привязки к функции и функция, с которой этот объект должен быть привязан.
Общий синтаксис привязки:
boundfunction = someFunction.bind (someObject, additionalParams);
Первый аргумент, используемый внутри директивы bind (), служит this
а следующие за ним аргументы являются необязательными и служат аргументами для связанной функции.
<!DOCTYPE html> <html> <body> <script> let fruit = function (person, color) { this .person = person; this .color = color; this .displayInfo = function () { document.write( this .person + " is " + this .color + '<br>' ); } } let bindingObj = { // creating an object using object literal syntax person : "Banana" , color : "Yellow" , } // Constructor invocation to create an object fruit1 let fruit1 = new fruit( "Orange" , "orange" ); // logs :Orange is orange // Method invocation of displayInfo() fruit1.displayInfo(); // binding the separated method to a new object let newBound = fruit1.displayInfo.bind(bindingObj); // logs : Banana is Yellow newBound(); </script> </body> </html> |
Выход:
Апельсин оранжевый Банан желтый
Обратите внимание, что мы вызываем displayInfo () для fruit1, используя вызов метода: fruit1.displayInfo
и поэтому можем ожидать, что он будет иметь контекст fruit1
. Однако, похоже, это не так, так как «Banana is Yellow» регистрируется вместо «Orange is orange».
Это происходит потому, что мы явно привязываем значение this
внутри displayInfo () с помощью команды bind()
к bindingObj
.
Как мы видим, явная привязка объекта к функции отменяет ее обычные правила контекста и принудительно устанавливает все this
значения для привязанного объекта внутри него.
Часто при передаче функций он теряет свой контекст, например:
<!DOCTYPE html> <html> <body> <script> let noBinding = { persons : "John" , passAround() { // displayInfo function is passed // as a callback to setTimeout setTimeout( this .displayInfo, 3000); }, displayInfo() { // logs the Window object document.write( this + ' ' ); // logs undefined document.write( this .persons); } } noBinding.passAround(); </script> </body> </html> |
Выход:
[окно объекта] undefined
Чтобы предотвратить сброс контекста внутри функции, переданной как обратный вызов, мы явно привязываем значение this
чтобы оно было таким же, как оно было внутри passAround()
, то есть: к объекту binding
<!DOCTYPE html> <html> <body> <script> let binding = { persons : "John" , passAround() { // displayInfo function is passed as a callback // to setTimeout binding the context of displayInfo // to "this" = binding in this execution context setTimeout( this .displayInfo.bind( this ), 3000); }, displayInfo() { // logs the binding object document.write( this + ' ' ); // logs John document.write( this .persons); } } binding.passAround(); </script> </body> </html> |
Выход:
[объект Объект] Джон
call () и apply ()
call() and apply()
выполняют задачу, аналогичную связыванию, явно указывая, какое значение this
должно хранить внутри функции.
Однако одно из основных различий между ними и bind()
заключается в том, что call() and apply()
немедленно вызывают функцию, в отличие от простой подготовки копии функции с привязкой this
для использования в будущем.
Синтаксис:
звоните :
function.call (thisValue, arg1, arg2, ...)
применить :
function.apply (thisValue, [arg1, arg2, ...])
Первым аргументом в обоих случаях является значение this
которое мы хотим, чтобы вызываемая функция имела.
По сути, единственное различие между этими двумя методами заключается в том, что в apply
второй аргумент является объектом массива аргументов, а при call
все аргументы отправляются в формате, разделенном запятыми.
Например:
<!DOCTYPE html> <html> <body> <script> // Create two different objects to test call() and apply() let banana = { person : 'Banana' }; let orange = { person : 'Orange' }; function fruits(adj) { document.write( this + ' ' ); return ( this .person + " is " + adj + '<br>' ); } // call() and apply() will immediately invoke // the function they are being called on // logs : Banana is yummy document.write(fruits.call(banana, 'yummy' )); // logs: Orange is sour document.write(fruits.apply(orange, [ 'sour' ])); </script> </body> </html> |
Выход:
[object Object] Банан вкусный [объект Объект] Апельсин кислый
Примечание . Оба call()
и apply()
доступны в прототипе объекта Function по умолчанию.
это со слушателями событий
Внутри функций , используемых в качестве обратных вызовов для обработчиков событий, this
имеет значение элемента, обожженное событие.
<!DOCTYPE html> <html> <body> <button>Click me to see console logging < /button> <script> let elem = document.querySelector( 'btn' ) elem.addEventListener( 'click' , function () { // logs: btn document.write( this ) }) </script> </body> </html> |
Если функция обратного вызова, используемая здесь, является функцией стрелки, а наш прослушиватель событий вложен в другой метод, this
будет относиться к контексту внешнего метода вложенности, и мы больше не сможем получить доступ к элементу, к которому добавлен прослушиватель событий, используя this
, как в предыдущем примере кода.
К счастью, это можно легко решить, используя currentTarget
для элемента следующим образом:
<!DOCTYPE html> <html> <body> <button>Click me to see console logging < /button> <script> function outerfunc(elem) { this .clickHandler = function () { elem.addEventListener( 'click' , (e) => { // logs the<button />element document.write(e.currentTarget); // this has the value of outerfunc this .displayInfo(); }) } ; this .displayInfo = function () { document.write( ' Correctly identified!' ); } } let button = document.body.querySelector( 'button' ); let test = new outerfunc(button); test.clickHandler() </script> </body> </html> |
Выход:
[объект HTMLButtonElement] Определено правильно!
Как мы видим, использование currentTarget
позволяет нам получить доступ к элементу, к которому добавлен прослушиватель событий, в то время как this
позволяет нам получить доступ к контексту включающей функции, то есть: this
позволяет нам успешно вызвать метод displayInfo ().