Function
Содержание:
- JS Tutorial
- Using “func.call” for the context
- The JavaScript call() Method
- Function Expression в сравнении с Function Declaration
- Функция как объект. Методы call и apply
- Выставляем значение this с помощью Apply или Call
- Используем call() и apply(), чтобы выставлять this в Callback функциях
- Переходим к нескольким аргументам с «func.apply»
- Javascript Bind Method
- Прозрачное кеширование
- Вложенные функции
- Сборка мусора
- Даты, Intl.DateTimeFormat
- Блоки кода и циклы, IIFE
- 使用 “func.call” 设定上下文
- Локаль
- JS Tutorial
- Итого
JS Tutorial
JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS LetJS ConstJS Arrow FunctionJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS Version ES5JS Version ES6JS JSON
Using “func.call” for the context
The caching decorator mentioned above is not suited to work with object methods.
For instance, in the code below stops working after the decoration:
The error occurs in the line that tries to access and fails. Can you see why?
The reason is that the wrapper calls the original function as in the line . And, when called like that, the function gets .
We would observe a similar symptom if we tried to run:
So, the wrapper passes the call to the original method, but without the context . Hence the error.
Let’s fix it.
There’s a special built-in function method func.call(context, …args) that allows to call a function explicitly setting .
The syntax is:
It runs providing the first argument as , and the next as the arguments.
To put it simply, these two calls do almost the same:
They both call with arguments , and . The only difference is that also sets to .
As an example, in the code below we call in the context of different objects: runs providing , and the next line sets :
And here we use to call with the given context and phrase:
In our case, we can use in the wrapper to pass the context to the original function:
Now everything is fine.
To make it all clear, let’s see more deeply how is passed along:
- After the decoration is now the wrapper .
- So when is executed, the wrapper gets as an argument and (it’s the object before dot).
- Inside the wrapper, assuming the result is not yet cached, passes the current () and the current argument () to the original method.
The JavaScript call() Method
The method is a predefined
JavaScript method.
It can be used to invoke (call) a method
with an owner object as an argument (parameter).
With , an object can use a method belonging to another object.
This example calls the fullName method of person, using it on
person1:
Example
var person = {
fullName: function() {
return this.firstName + » » + this.lastName;
}}
var person1 = {
firstName:»John»,
lastName: «Doe»
}
var person2 = {
firstName:»Mary»,
lastName: «Doe»}
person.fullName.call(person1); // Will return «John
Doe»
This example calls the fullName method of person, using it on
person2:
Example
var person = {
fullName: function() {
return this.firstName + » » + this.lastName;
}}
var person1 = {
firstName:»John»,
lastName: «Doe»
}
var person2 = {
firstName:»Mary»,
lastName: «Doe»}
person.fullName.call(person2); // Will return «Mary Doe»
Function Expression в сравнении с Function Declaration
Давайте разберём ключевые отличия Function Declaration от Function Expression.
Во-первых, синтаксис: как определить, что есть что в коде.
-
Function Declaration: функция объявляется отдельной конструкцией «function…» в основном потоке кода.
-
Function Expression: функция, созданная внутри другого выражения или синтаксической конструкции. В данном случае функция создаётся в правой части «выражения присваивания» :
Более тонкое отличие состоит, в том, когда создаётся функция движком JavaScript.
Function Expression создаётся, когда выполнение доходит до него, и затем уже может использоваться.
После того, как поток выполнения достигнет правой части выражения присваивания – с этого момента, функция считается созданной и может быть использована (присвоена переменной, вызвана и т.д. ).
С Function Declaration всё иначе.
Function Declaration можно использовать во всем скрипте (или блоке кода, если функция объявлена в блоке).
Другими словами, когда движок JavaScript готовится выполнять скрипт или блок кода, прежде всего он ищет в нём Function Declaration и создаёт все такие функции. Можно считать этот процесс «стадией инициализации».
И только после того, как все объявления Function Declaration будут обработаны, продолжится выполнение.
В результате, функции, созданные, как Function Declaration могут быть вызваны раньше своих определений.
Например, так будет работать:
Функция была создана, когда движок JavaScript подготавливал скрипт к выполнению, и такая функция видна повсюду в этом скрипте.
…Если бы это было Function Expression, то такой код вызовет ошибку:
Функции, объявленные при помощи Function Expression, создаются тогда, когда выполнение доходит до них. Это случится только на строке, помеченной звёздочкой . Слишком поздно.
Ещё одна важная особенность Function Declaration заключается в их блочной области видимости.
В строгом режиме, когда Function Declaration находится в блоке , функция доступна везде внутри блока. Но не снаружи него.
Для примера давайте представим, что нам нужно создать функцию в зависимости от значения переменной , которое мы получим во время выполнения кода. И затем запланируем использовать её когда-нибудь в будущем.
Такой код, использующий Function Declaration, работать не будет:
Это произошло, так как объявление Function Declaration видимо только внутри блока кода, в котором располагается.
Вот ещё один пример:
Что можно сделать, чтобы была видима снаружи ?
Верным подходом будет воспользоваться функцией, объявленной при помощи Function Expression, и присвоить значение переменной, объявленной снаружи , что обеспечит нам нужную видимость.
Такой код работает, как ожидалось:
Можно упростить этот код ещё сильнее, используя условный оператор :
Когда использовать Function Declaration, а когда Function Expression?
Как правило, если нам понадобилась функция, в первую очередь нужно рассматривать синтаксис Function Declaration, который мы использовали до этого. Он даёт нам больше свободы в том, как мы можем организовывать код. Функции, объявленные таким образом, можно вызывать до их объявления.
Также функции вида чуть более заметны в коде, чем . Function Declaration легче «ловятся глазами».
…Но если Function Declaration нам не подходит по какой-то причине (мы рассмотрели это в примере выше), то можно использовать объявление при помощи Function Expression.
Функция как объект. Методы call и apply
Последнее обновление: 1.11.2015
В JavaScript функция тоже является объектом — объектом Function и тоже имеет прототип, свойства, методы. Все функции, которые используются в программе,
являются объектами Function и имеют все его свойства и методы.
Например, мы можем создать функцию с помощью конструктора Function:
var square = new Function('n', 'return n * n;'); console.log(square(5));
В конструктор Function может передаваться ряд параметров. Последний параметр представляет собой само тело функции в виде строки. Фактически
строка содержит код javascript. Предыдущие аргументы содержат названия параметров. В данном случае определяется функция возведения числа в квадрат, которая имеет один
параметр n.
Среди свойств объекта Function можно выделить следующие:
-
arguments: массив аргументов, передаваемых в функцию
-
length: определяет количество аргументов, которые ожидает функция
-
caller: определяет функцию, вызвавшую текущую выполняющуюся функцию
-
name: имя функции
-
prototype: прототип функции
С помощью прототипа мы можем определить дополнительные свойства:
function display(){ console.log("привет мир"); } Function.prototype.program ="Hello"; console.log(display.program); // Hello
Среди методов надо отметить методы call() и apply().
Метод call() вызывает функцию с указанным значением this и аргументами:
function add(x, y){ return x + y; } var result = add.call(this, 3, 8); console.log(result); // 11
указывает на объект, для которого вызывается функция — в данном случае это глобальный объект window. После this передаются значения для параметров.
При передаче объекта через первый параметр, мы можем ссылаться на него через ключевое слово :
function User (name, age) { this.name = name; this.age = age; } var tom = new User("Том", 26); function display(){ console.log("Ваше имя: " + this.name); } display.call(tom); // Ваше имя: Том
В данном случае передается только одно значение, поскольку функция display не принимает параметров. То есть функция будет вызываться для объекта tom.
Если нам не важен объект, для которого вызывается функция, то можно передать значение null:
function add(x, y){ return x + y; } var result = add.call(null, 3, 8); console.log(result); // 11
На метод похож метод apply(), который также вызывает функцию и в качестве первого параметра также
получает объект, для которого функция вызывается. Только теперь в качестве второго параметра передается массив аргументов:
function add(x, y){ return x + y; } var result = add.apply(null, ); console.log(result); // 11
НазадВперед
Выставляем значение this с помощью Apply или Call
Как и в примере с , мы также можем указывать значение для this, когда вызываем функцию используя методы и . Первый параметр в и выставляет this значение объекту на котором вызвана функция.
Вот очень быстрый и показательный пример для начинающих, перед тем, как мы окунёмся в использование Apply и Call:
// Глобальная переменная для демонстрации var avgScore = "global avgScore";// Функция function avg (arrayOfScores) {// Складываем все показатели var sumOfScores = arrayOfScores.reduce (function (prev, cur, index, array) { return prev + cur; });// В этом случае this будет привязан к глобальному объекту, пока мы не выставим его с call() или apply() this.avgScore = sumOfScores / arrayOfScores.length; } var gameController = { scores: , avgScore:null }// Если мы выполним функцию avg, то this внутри функции будет привязана к глобальному объекту: avg (gameController.scores);// Вот, что получаем: console.log (window.avgScore); // 46.4 console.log (gameController.avgScore); // null// Сбрасываем avgScore avgScore = "global avgScore";// Чтобы указать, что значение this привязано к gameController, // Мы вызываем call() метод: avg.call (gameController, gameController.scores); console.log (window.avgScore); //global avgScore console.log (gameController.avgScore); // 46.4
А сейчас очень внимательно! Обратите внимание, что первый аргумент в выставляет значение. В предыдущем примере, оно выставляется на объект
А другие аргументы, после первого, передаются как параметры функции .
Методы и практически идентичны при работе с выставлением значения , за исключением того, что вы передаёте параметры функции в как массив, в то время, как в , параметры передаются в индивидуальном порядке. Но дальше ещё интереснее. У есть ещё одна фича, которой нет у и очень скоро вы о ней узнаете.
Используем call() и apply(), чтобы выставлять this в Callback функциях
// Создаём объект со свойствами и методами// Далее мы передадим метод, как колбэк другой функции var clientData = { id: 094545, fullName: "Not Set",// Метод на объекте clientData setUserName: function (firstName, lastName) {// тут мы выставляем fullName свойство в данном объекте this.fullName = firstName + " " + lastName; } }
Сама функция, тут очень внимательно:
function getUserInput (firstName, lastName, callback, callbackObj) {// Использование метода apply ниже, выставит this для callbackObj callback.apply (callbackObj, ); }
Метод выставляет значение для . Так мы можем выполнить колбэк с указанным значением , так что параметры переданные колбэку, будут выставлены на объекте :
// Объект clientData будет использоваться методом Apply, чтобы выставить значение this. getUserInput ("Barack", "Obama", clientData.setUserName, clientData);// Получаем в консоль console.log (clientData.fullName); // Barack Obama
Методы , и всегда используются для того, чтобы выставлять значение , при вызове метода, но каждый слегка по-своему и это отображается на том, как мы можем контролировать и поддерживать наш код
Значение в JavaScript также важно, как и любая другая часть языка. Помните, что у нас есть 3 вышеупомянутых метода, которые просто необходимы для эффективной и правильной работы с этим самым
Всегда старайтесь перепроверять то, что предполагается на месте .
Заимствование функций с помощью Apply и Call (Важно знать)
Вообще и в JavaScript используют в основном, чтобы заимствовать функции. С этими методами, мы можем выполнять заимствования также, как и с методом , но при этом быть более гибкими. Давайте рассмотрим следующие примеры:
Переходим к нескольким аргументам с «func.apply»
Теперь давайте сделаем ещё более универсальным. До сих пор он работал только с функциями с одним аргументом.
Как же кешировать метод с несколькими аргументами ?
Здесь у нас есть две задачи для решения.
Во-первых, как использовать оба аргумента и для ключа в коллекции ? Ранее для одного аргумента мы могли просто сохранить результат и вызвать , чтобы получить его позже. Но теперь нам нужно запомнить результат для комбинации аргументов . Встроенный принимает только одно значение как ключ.
Есть много возможных решений:
- Реализовать новую (или использовать стороннюю) структуру данных для коллекции, которая более универсальна, чем встроенный , и поддерживает множественные ключи.
- Использовать вложенные коллекции: будет , которая хранит пару . Тогда получить мы сможем, вызвав .
- Соединить два значения в одно. В нашем конкретном случае мы можем просто использовать строку как ключ к . Для гибкости, мы можем позволить передавать хеширующую функцию в декоратор, которая знает, как сделать одно значение из многих.
Для многих практических применений третий вариант достаточно хорош, поэтому мы будем придерживаться его.
Также нам понадобится заменить на , чтобы передавать все аргументы обёрнутой функции, а не только первый.
Вот более мощный :
Теперь он работает с любым количеством аргументов.
Есть два изменения:
- В строке вызываем для создания одного ключа из . Здесь мы используем простую функцию «объединения», которая превращает аргументы в ключ . В более сложных случаях могут потребоваться другие функции хеширования.
- Затем в строке используем для передачи как контекста, так и всех аргументов, полученных обёрткой (независимо от их количества), в исходную функцию.
Вместо мы могли бы написать .
Синтаксис встроенного метода func.apply:
Он выполняет , устанавливая и принимая в качестве списка аргументов псевдомассив .
Единственная разница в синтаксисе между и состоит в том, что ожидает список аргументов, в то время как принимает псевдомассив.
Эти два вызова почти эквивалентны:
Есть только одна небольшая разница:
- Оператор расширения позволяет передавать перебираемый объект в виде списка в .
- А принимает только псевдомассив .
Так что эти вызовы дополняют друг друга. Для перебираемых объектов сработает , а где мы ожидаем псевдомассив – .
А если у нас объект, который и то, и другое, например, реальный массив, то технически мы могли бы использовать любой метод, но , вероятно, будет быстрее, потому что большинство движков JavaScript внутренне оптимизируют его лучше.
Передача всех аргументов вместе с контекстом другой функции называется «перенаправлением вызова» (call forwarding).
Простейший вид такого перенаправления:
При вызове из внешнего кода его не отличить от вызова исходной функции.
Javascript Bind Method
Bind là một function nằm trong function prototype do đó chỉ có function mới có thể gọi dc nó. Chúng ta gọi Bind method dùng để xác định tham số “this” cho một function.
bind() cho phép chúng ta dễ dàng thiết lập một đối tượng cụ thể sẽ bị ràng buộc này khi một chức năng hoặc phương pháp được gọi.
Các tác dụng của bind
Cho pheps chúng ta set giá trị của biến «this»
ví dụ: https://jsfiddle.net/knz9fv5c/
Mục đích trong ví dụ này ta sẽ xem xet bind có thể set giá trị “this” trong object như nào.
Ta có 1 object user chứa function showInfo có nhiệm vụ hiển thị thông tin của object user đó thông qua biến “this”
Ở trường hợp đầu tiên khi không sử dụng bind ta có thể thấy khi click vào button giá trị “this” trong hàm showInfo không được gọi ( bằng “undefined”) .
Ở trường hợp thứ 2 khi ta dùng bind để set giá trị this của user. Lúc này ta có thể bind giá trị của user với this. Kết quả ta thấy show được thông tin của user rồi đó .
Một trường hợp khác với global và local variable.
Ta có ví dụ tương tự như sau:
Ta thấy nếu ko sử dụng bind thì biến global data được gọi còn nếu sử dụng bind sẽ set giá trị cho this nên biến local sẽ được gọi.
Cho phép tạo một Curry Function
Không chỉ bind được giá trị “this” , hàm bind còn có thể bind được các tham số truyền vào cho hàm . Tức là ta có thể tao ra một function mới từ function cũ bằng cách gắn mặc định một tham số cho function cũ đó . Đó là cách tạo một Curry Function
Giả sử mình có một hàm log đơn gilản có 3 tham số truyền vào như sau:
Bây h mình muốn tạo một hàm log acces ngày hôm nay mình sẽ tạo ra sử dụng hàm cũ như sau:
Ta có thể tạo function mới bằng cách sử dụng bind bằng cách gán mặc định 2 tham số đầu tiên như sau:
Khi gọi logAccessToday(“Server Access”) đều ra một thông báo như nhau.
Прозрачное кеширование
Представим, что у нас есть функция , выполняющая ресурсоёмкие вычисления, но возвращающая стабильные результаты. Другими словами, для одного и того же она всегда возвращает один и тот же результат.
Если функция вызывается часто, то, вероятно, мы захотим кешировать (запоминать) возвращаемые ею результаты, чтобы сэкономить время на повторных вычислениях.
Вместо того, чтобы усложнять дополнительной функциональностью, мы заключим её в функцию-обёртку – «wrapper» (от англ. «wrap» – обёртывать), которая добавит кеширование. Далее мы увидим, что в таком подходе масса преимуществ.
Вот код с объяснениями:
В коде выше – это декоратор, специальная функция, которая принимает другую функцию и изменяет её поведение.
Идея состоит в том, что мы можем вызвать с любой функцией, в результате чего мы получим кеширующую обёртку. Это здорово, т.к. у нас может быть множество функций, использующих такую функциональность, и всё, что нам нужно сделать – это применить к ним .
Отделяя кеширующий код от основного кода, мы также сохраняем чистоту и простоту последнего.
Результат вызова является «обёрткой», т.е. «оборачивает» вызов в кеширующую логику:
С точки зрения внешнего кода, обёрнутая функция по-прежнему делает то же самое. Обёртка всего лишь добавляет к её поведению аспект кеширования.
Подводя итог, можно выделить несколько преимуществ использования отдельной вместо изменения кода самой :
- Функцию можно использовать повторно. Мы можем применить её к другой функции.
- Логика кеширования является отдельной, она не увеличивает сложность самой (если таковая была).
- При необходимости мы можем объединить несколько декораторов (речь об этом пойдёт позже).
Вложенные функции
Функция называется «вложенной», когда она создаётся внутри другой функции.
Это очень легко сделать в JavaScript.
Мы можем использовать это для упорядочивания нашего кода, например, как здесь:
Здесь вложенная функция создана для удобства. Она может получить доступ к внешним переменным и, значит, вывести полное имя. В JavaScript вложенные функции используются очень часто.
Что ещё интереснее, вложенная функция может быть возвращена: либо в качестве свойства нового объекта (если внешняя функция создаёт объект с методами), либо сама по себе. И затем может быть использована в любом месте
Не важно где, она всё так же будет иметь доступ к тем же внешним переменным
Например, здесь, вложенная функция присваивается новому объекту в конструкторе:
А здесь мы просто создаём и возвращаем функцию «счётчик»:
Давайте продолжим с примером . Он создаёт функцию «counter», которая возвращает следующее число при каждом вызове. Несмотря на простоту, немного модифицированные варианты этого кода применяются на практике, например, в генераторе псевдослучайных чисел и во многих других случаях.
Как же это работает изнутри?
Когда внутренняя функция начинает выполняться, начинается поиск переменной изнутри-наружу. Для примера выше порядок будет такой:
- Локальные переменные вложенной функции…
- Переменные внешней функции…
- И так далее, пока не будут достигнуты глобальные переменные.
В этом примере будет найден на шаге . Когда внешняя переменная модифицируется, она изменится там, где была найдена. Значит, найдёт внешнюю переменную и увеличит её значение в лексическом окружении, которому она принадлежит. Как если бы у нас было .
Теперь рассмотрим два вопроса:
- Можем ли мы каким-нибудь образом сбросить счётчик из кода, который не принадлежит ? Например, после вызова в коде выше.
- Если мы вызываем несколько раз – нам возвращается много функций . Они независимы или разделяют одну и ту же переменную ?
Попробуйте ответить на эти вопросы перед тем, как продолжить чтение.
…
Готовы?
Хорошо, давайте ответим на вопросы.
- Такой возможности нет: – локальная переменная функции, мы не можем получить к ней доступ извне.
- Для каждого вызова создаётся новое лексическое окружение функции, со своим собственным . Так что, получившиеся функции – независимы.
Вот демо:
Надеюсь, ситуация с внешними переменными теперь ясна. Для большинства ситуаций такого понимания вполне достаточно, но в спецификации есть ряд деталей, которые мы, для простоты, опустили. Далее мы разберём происходящее ещё более подробно.
Сборка мусора
Обычно лексическое окружение очищается и удаляется после того, как функция выполнилась. Например:
Здесь два значения, которые технически являются свойствами лексического окружения. Но после того, как завершится, это лексическое окружение станет недоступно, поэтому оно удалится из памяти.
…Но, если есть вложенная функция, которая всё ещё доступна после выполнения , то у неё есть свойство , которое ссылается на внешнее лексическое окружение, тем самым оставляя его достижимым, «живым»:
Обратите внимание, если вызывается несколько раз и возвращаемые функции сохраняются, тогда все соответствующие объекты лексического окружения продолжат держаться в памяти. Вот три такие функции в коде ниже:. Объект лексического окружения умирает, когда становится недоступным (как и любой другой объект)
Другими словами, он существует только до того момента, пока есть хотя бы одна вложенная функция, которая ссылается на него
Объект лексического окружения умирает, когда становится недоступным (как и любой другой объект). Другими словами, он существует только до того момента, пока есть хотя бы одна вложенная функция, которая ссылается на него.
В следующем коде, после того как станет недоступным, лексическое окружение функции (и, соответственно, ) будет удалено из памяти;
Как мы видели, в теории, пока функция жива, все внешние переменные тоже сохраняются.
Но на практике движки JavaScript пытаются это оптимизировать. Они анализируют использование переменных и, если легко по коду понять, что внешняя переменная не используется – она удаляется.
Одним из важных побочных эффектов в V8 (Chrome, Opera) является то, что такая переменная становится недоступной при отладке.
Попробуйте запустить следующий пример в Chrome с открытой Developer Tools.
Когда код будет поставлен на паузу, напишите в консоли .
Как вы можете видеть – такой переменной не существует! В теории, она должна быть доступна, но попала под оптимизацию движка.
Это может приводить к забавным (если удаётся решить быстро) проблемам при отладке. Одна из них – мы можем увидеть не ту внешнюю переменную при совпадающих названиях:
До встречи!
Эту особенность V8 полезно знать. Если вы занимаетесь отладкой в Chrome/Opera, рано или поздно вы с ней встретитесь.
Это не баг в отладчике, а скорее особенность V8. Возможно со временем это изменится.
Вы всегда можете проверить это, запустив пример на этой странице.
Даты, Intl.DateTimeFormat
Синтаксис:
Первый аргумент – такой же, как и в , а в объекте мы можем определить, какие именно части даты показывать (часы, месяц, год…) и в каком формате.
Полный список свойств :
Свойство | Описание | Возможные значения | По умолчанию |
---|---|---|---|
Алгоритм подбора локали | lookup,best fit | best fit | |
Алгоритм подбора формата | , | ||
Включать ли время в 12-часовом формате | — 12-часовой формат, — 24-часовой | ||
Временная зона | Временная зона, например | ||
День недели | , , | ||
Эра | , , | ||
Год | , | или | |
Месяц | , , , , | или | |
День | , | или | |
Час | , | ||
Минуты | , | ||
second | Секунды | , | |
Название таймзоны (нет в IE11) | , |
Все локали обязаны поддерживать следующие наборы настроек:
- weekday, year, month, day, hour, minute, second
- weekday, year, month, day
- year, month, day
- year, month
- month, day
- hour, minute, second
Если указанный формат не поддерживается, то настройка задаёт алгоритм подбора наиболее близкого формата: – по и – по умолчанию, на усмотрение окружения (браузера).
Использование:
Например:
Длинная дата, с настройками:
Только время:
Блоки кода и циклы, IIFE
Предыдущие примеры сосредоточены на функциях. Но лексическое окружение существует для любых блоков кода .
Лексическое окружение создаётся при выполнении блока кода и содержит локальные переменные для этого блока. Вот пара примеров.
В следующем примере переменная существует только в блоке :
Когда выполнение попадает в блок , для этого блока создаётся новое лексическое окружение.
У него есть ссылка на внешнее окружение, так что может быть найдена. Но все переменные и Function Expression, объявленные внутри , остаются в его лексическом окружении и не видны снаружи.
Например, после завершения следующий не увидит , что вызовет ошибку.
Для цикла у каждой итерации своё отдельное лексическое окружение. Если переменная объявлена в , то она также в нём:
Обратите внимание: визуально находится снаружи. Но конструкция – особенная в этом смысле, у каждой итерации цикла своё собственное лексическое окружение с текущим в нём
И так же, как и в , ниже цикла невидима.
Мы также можем использовать «простые» блоки кода , чтобы изолировать переменные в «локальной области видимости».
Например, в браузере все скрипты (кроме ) разделяют одну общую глобальную область. Так что, если мы создадим глобальную переменную в одном скрипте, она станет доступна и в других. Но это становится источником конфликтов, если два скрипта используют одно и то же имя переменной и перезаписывают друг друга.
Это может произойти, если название переменной – широко распространённое слово, а авторы скрипта не знают друг о друге.
Если мы хотим этого избежать, мы можем использовать блок кода для изоляции всего скрипта или какой-то его части:
Из-за того, что у блока есть собственное лексическое окружение, код снаружи него (или в другом скрипте) не видит переменные этого блока.
В прошлом в JavaScript не было лексического окружения на уровне блоков кода.
Так что программистам пришлось что-то придумать. И то, что они сделали, называется «immediately-invoked function expressions» (аббревиатура IIFE), что означает функцию, запускаемую сразу после объявления.
Это не то, что мы должны использовать сегодня, но, так как вы можете встретить это в старых скриптах, полезно понимать принцип работы.
IIFE выглядит так:
Здесь создаётся и немедленно вызывается Function Expression. Так что код выполняется сразу же и у него есть свои локальные переменные.
Function Expression обёрнуто в скобки , потому что, когда JavaScript встречает в основном потоке кода, он воспринимает это как начало Function Declaration. Но у Function Declaration должно быть имя, так что такой код вызовет ошибку:
Даже если мы скажем: «хорошо, давайте добавим имя», – это не сработает, потому что JavaScript не позволяет вызывать Function Declaration немедленно.
Так что, скобки вокруг функции – это трюк, который позволяет показать JavaScript, что функция была создана в контексте другого выражения, и, таким образом, это функциональное выражение: ей не нужно имя и её можно вызвать немедленно.
Кроме скобок, существуют и другие пути показать JavaScript, что мы имеем в виду Function Expression:
Во всех перечисленных случаях мы объявляем Function Expression и немедленно выполняем его. Ещё раз заметим, что в настоящий момент нет необходимости писать подобный код.
使用 “func.call” 设定上下文
上面提到的缓存装饰者不适用于对象方法。
例如,在下面的代码中, 在装饰后停止工作:
错误发生在试图访问 并失败了的 行中。你能看出来为什么吗?
原因是包装器将原始函数调用为 行中的 。并且,当这样调用时,函数将得到 。
如果尝试运行下面这段代码,我们会观察到类似的问题:
因此,包装器将调用传递给原始方法,但没有上下文 。因此,发生了错误。
让我们来解决这个问题。
有一个特殊的内置函数方法 func.call(context, …args),它允许调用一个显式设置 的函数。
语法如下:
它运行 ,提供的第一个参数作为 ,后面的作为参数(arguments)。
简单地说,这两个调用几乎相同:
它们调用的都是 ,参数是 、 和 。唯一的区别是 还会将 设置为 。
例如,在下面的代码中,我们在不同对象的上下文中调用 : 运行 并提供了 ,然后下一行设置 :
在这里我们用带有给定上下文和 phrase 的 调用 :
在我们的例子中,我们可以在包装器中使用 将上下文传递给原始函数:
现在一切都正常工作了。
为了让大家理解地更清晰一些,让我们更深入地看看 是如何被传递的:
- 在经过装饰之后, 现在是包装器 。
- 因此,当 执行时,包装器将 作为参数,并且 (它是点符号 之前的对象)。
- 在包装器内部,假设结果尚未缓存, 将当前的 ()和当前的参数()传递给原始方法。
Локаль
Локаль – первый и самый важный аргумент всех методов, связанных с интернационализацией.
Локаль описывается строкой из трёх компонентов, которые разделяются дефисом:
- Код языка.
- Код способа записи.
- Код страны.
На практике не всегда указаны три, обычно меньше:
- – русский язык, без уточнений.
- – английский язык, используемый в Англии ().
- – английский язык, используемый в США ().
- – китайский язык (), записываемый упрощённой иероглифической письменностью (), используемый в Китае.
Также через суффикс можно указать расширения локалей, например – тайский язык (), используемый в Таиланде (), с записью чисел тайскими буквами (๐, ๑, ๒, ๓, ๔, ๕, ๖, ๗, ๘, ๙) .
Стандарт, который описывает локали – RFC 5464, языки описаны в IANA language registry.
Все методы принимают локаль в виде строки или массива, содержащего несколько локалей в порядке предпочтения.
Если локаль не указана или – берётся локаль по умолчанию, установленная в окружении (браузере).
– вспомогательная настройка, которую тоже можно везде указать, она определяет способ подбора локали, если желаемая недоступна.
У него два значения:
- – означает простейший порядок поиска путём обрезания суффикса, например → → → локаль по умолчанию.
- – использует встроенные алгоритмы и предпочтения браузера (или другого окружения) для выбора подходящей локали.
По умолчанию стоит .
Если локалей несколько, например то пытается подобрать наиболее подходящую локаль для первой из списка (китайская), если не получается – переходит ко второй (русской) и так далее. Если ни одной не нашёл, например на компьютере не совсем поддерживается ни китайский ни русский, то используется локаль по умолчанию.
Как правило, является здесь наилучшим выбором.
JS Tutorial
JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS LetJS ConstJS Arrow FunctionJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS Version ES5JS Version ES6JS JSON
Итого
Декоратор – это обёртка вокруг функции, которая изменяет поведение последней. Основная работа по-прежнему выполняется функцией.
Обычно безопасно заменить функцию или метод декорированным, за исключением одной мелочи. Если исходная функция предоставляет свойства, такие как или типа того, то декорированная функция их не предоставит. Потому что это обёртка. Так что нужно быть осторожным в их использовании. Некоторые декораторы предоставляют свои собственные свойства.
Декораторы можно рассматривать как «дополнительные возможности» или «аспекты», которые можно добавить в функцию. Мы можем добавить один или несколько декораторов. И всё это без изменения кода оригинальной функции!
Для реализации мы изучили методы:
- func.call(context, arg1, arg2…) – вызывает с данным контекстом и аргументами.
- func.apply(context, args) – вызывает , передавая как и псевдомассив как список аргументов.
В основном переадресация вызова выполняется с помощью :
Мы также рассмотрели пример заимствования метода, когда мы вызываем метод у объекта в контексте другого объекта. Весьма распространено заимствовать методы массива и применять их к . В качестве альтернативы можно использовать объект с остаточными параметрами , который является реальным массивом.
На практике декораторы используются для самых разных задач. Проверьте, насколько хорошо вы их освоили, решая задачи этой главы.