Поиск…
Вступление
Функции в JavaScript предоставляют организованный, многоразовый код для выполнения набора действий. Функции упрощают процесс кодирования, предотвращают избыточную логику и упрощают выполнение кода. В этом разделе описывается декларация и использование функций, аргументов, параметров, операторов возврата и области видимости в JavaScript.
Синтаксис
Пример функции (x) {return x}
var example = function (x) {return x}
(function () {...}) (); // Вызываемая функция Expression Expression (IIFE)
var instance = new Пример (x);
методы
fn.apply (valueForThis [, arrayOfArgs])
fn.bind (valueForThis [, arg1 [, arg2, ...]])
fn.call (valueForThis [, arg1 [, arg2, ...]])
ES2015 + (ES6 +):
const example = x => {return x}; // Явная функция стрелки
const example = x => x; // Функция непрямого возврата стрелки
const example = (x, y, z) => {...} // Функция со стрелкой содержит несколько аргументов
(() => {...}) (); // IIFE с использованием функции стрелки
замечания
Информацию о функциях стрелок см. В документации по функциям стрелок .
Функции как переменная
Объявление нормальной функции выглядит следующим образом:
function foo(){
}
Функция, подобная этой, доступна из любого места в ее контексте по ее имени. Но иногда это может быть полезно для обработки ссылок на функции, таких как ссылки на объекты. Например, вы можете назначить объект переменной на основе некоторого набора условий, а затем позже получить свойство из одного или другого объекта:
var name = 'Cameron';
var spouse;
if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };
var spouseName = spouse.name;
В JavaScript вы можете сделать то же самое с функциями:
// Example 1
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };
hash('Fred');
В приведенном выше примере hash
является нормальной переменной. Ему назначается ссылка на функцию, после которой функция, с которой он ссылается, может быть вызвана с помощью круглых скобок, как объявление нормальной функции.
Приведенный выше пример ссылается на анонимные функции ... функции, которые не имеют собственного имени. Вы также можете использовать переменные для обозначения названных функций. Приведенный выше пример можно переписать следующим образом:
// Example 2
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = sha1Hash;
else if ( hashAlgorithm === 'md5' ) hash = md5Hash;
hash('Fred');
function md5Hash(value){
// ...
}
function sha1Hash(value){
// ...
}
Или вы можете назначить ссылки на функции из свойств объекта:
// Example 3
var hashAlgorithms = {
sha1: function(value) { /**/ },
md5: function(value) { /**/ }
};
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = hashAlgorithms.sha1;
else if ( hashAlgorithm === 'md5' ) hash = hashAlgorithms.md5;
hash('Fred');
Вы можете назначить ссылку на функцию, удерживаемую одной переменной, другой, опуская круглые скобки. Это может привести к простой ошибке: попытка присвоить возвращаемое значение функции другой переменной, но случайно назначить ссылку на функцию.
// Example 4
var a = getValue;
var b = a; // b is now a reference to getValue.
var c = b(); // b is invoked, so c now holds the value returned by getValue (41)
function getValue(){
return 41;
}
Ссылка на функцию похожа на любое другое значение. Как вы видели, ссылку можно присвоить переменной, а ссылочное значение этой переменной впоследствии может быть присвоено другим переменным. Вы можете передавать ссылки на такие функции, как любое другое значение, в том числе передавать ссылку на функцию как возвращаемое значение другой функции. Например:
// Example 5
// getHashingFunction returns a function, which is assigned
// to hash for later use:
var hash = getHashingFunction( 'sha1' );
// ...
hash('Fred');
// return the function corresponding to the given algorithmName
function getHashingFunction( algorithmName ){
// return a reference to an anonymous function
if (algorithmName === 'sha1') return function(value){ /**/ };
// return a reference to a declared function
else if (algorithmName === 'md5') return md5;
}
function md5Hash(value){
// ...
}
Вам не нужно назначать ссылку на функцию переменной для ее вызова. Этот пример, строящий пример 5, вызовет функцию getHashingFunction, а затем немедленно вызовет возвращаемую функцию и передаст ее возвращаемое значение в hashedValue.
// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );
Примечание по подъему
Имейте в виду, что, в отличие от обычных деклараций функций, переменные, которые ссылаются на функции, не «поднимаются». В примере 2 функции md5Hash
и sha1Hash
определены в нижней части скрипта, но доступны повсюду сразу. Независимо от того, где вы определяете функцию, интерпретатор «поднимает» ее до вершины своей области, делая ее немедленно доступной. Это не относится к определениям переменных, поэтому код, следующий ниже, сломается:
var functionVariable;
hoistedFunction(); // works, because the function is "hoisted" to the top of its scope
functionVariable(); // error: undefined is not a function.
function hoistedFunction(){}
functionVariable = function(){};
Анонимная функция
Определение анонимной функции
Когда функция определена, вы часто указываете ей имя и затем вызываете ее с использованием этого имени:
foo();
function foo(){
// ...
}
Когда вы определяете функцию таким образом, среда выполнения Javascript хранит вашу функцию в памяти, а затем создает ссылку на эту функцию, используя имя, которое вы назначили ей. Это имя затем доступно в текущей области. Это может быть очень удобным способом создания функции, но Javascript не требует, чтобы вы назначили имя функции. Также совершенно законно:
function() {
// ...
}
Когда функция определена без имени, она известна как анонимная функция. Функция сохраняется в памяти, но среда выполнения не создает для вас ссылку на нее. На первый взгляд может показаться, что такая вещь не нужна, но есть несколько сценариев, где анонимные функции очень удобны.
Назначение анонимной функции переменной
Очень часто использование анонимных функций заключается в том, чтобы назначить их переменной:
var foo = function(){ /*...*/ };
foo();
Это использование анонимных функций более подробно рассматривается в функции как переменная
Поставка анонимной функции в качестве параметра для другой функции
Некоторые функции могут принимать ссылку на функцию как параметр. Они иногда называются «инъекциями зависимостей» или «обратными вызовами», потому что они позволяют функции вашего вызова «перезванивать» на ваш код, предоставляя вам возможность изменить способ поведения вызываемой функции. Например, функция карты объекта Array позволяет выполнять итерацию по каждому элементу массива, а затем строить новый массив, применяя функцию преобразования к каждому элементу.
var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]
Было бы утомительно, неряшливо и ненужно создавать именованную функцию, которая загромождала бы вашу область видимости только функцией в этом одном месте и нарушала естественный поток и чтение вашего кода (коллеге пришлось бы оставить этот код, чтобы найти ваш чтобы понять, что происходит).
Возвращение анонимной функции из другой функции
Иногда полезно возвращать функцию в результате другой функции. Например:
var hash = getHashFunction( 'sha1' );
var hashValue = hash( 'Secret Value' );
function getHashFunction( algorithm ){
if ( algorithm === 'sha1' ) return function( value ){ /*...*/ };
else if ( algorithm === 'md5' ) return function( value ){ /*...*/ };
}
Немедленное обращение к анонимной функции
В отличие от многих других языков, область видимости в Javascript является функциональной, а не блочной. (См. Обзор функций ). В некоторых случаях, однако, необходимо создать новую область. Например, обычно при создании кода с помощью <script>
обычно создается новая область, а не разрешается определять имена переменных в глобальной области действия (что сопряжено с риском возникновения других скриптов с именами переменных). Общим способом обработки этой ситуации является определение новой анонимной функции, а затем ее немедленное обращение, безопасное скрытие переменных в рамках анонимной функции и отсутствие доступа к вашему коду третьим лицам через имя пропущенной функции. Например:
<!-- My Script -->
<script>
function initialize(){
// foo is safely hidden within initialize, but...
var foo = '';
}
// ...my initialize function is now accessible from global scope.
// There's a risk someone could call it again, probably by accident.
initialize();
</script>
<script>
// Using an anonymous function, and then immediately
// invoking it, hides my foo variable and guarantees
// no one else can call it a second time.
(function(){
var foo = '';
}()) // <--- the parentheses invokes the function immediately
</script>
Автономные анонимные функции
Иногда полезно, чтобы анонимная функция могла ссылаться на себя. Например, функции, возможно, придется рекурсивно называть себя или добавлять свойства к себе. Если функция анонимна, это может быть очень сложно, так как для этого требуется знание переменной, которой была назначена функция. Это идеальное решение:
var foo = function(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) foo(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Bad.
Цель здесь заключалась в том, что анонимная функция рекурсивно называет себя, но когда значение foo изменяется, вы получаете потенциально сложную задачу для отслеживания ошибок.
Вместо этого мы можем дать анонимной функции ссылку на себя, предоставив ей личное имя, например:
var foo = function myself(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) myself(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Whassup?
Обратите внимание, что имя функции ограничено. Имя не просочилось во внешнюю область:
myself(false); // ReferenceError: myself is not defined
Этот метод особенно полезен при работе с рекурсивными анонимными функциями в качестве параметров обратного вызова:
// Calculate the fibonacci value for each number in an array:
var fib = false,
result = [1,2,3,4,5,6,7,8].map(
function fib(n){
return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 );
});
// result = [1, 1, 2, 3, 5, 8, 13, 21]
// fib = false (the anonymous function name did not overwrite our fib variable)
Выражения мгновенной вызывной функции
Иногда вы не хотите, чтобы ваша функция была доступна / хранилась как переменная. Вы можете создать выражение с выраженной немедленной вызывной функцией (IIFE для краткости). Это, по сути, самоисполняющиеся анонимные функции . Они имеют доступ к окружающему пространству, но сама функция и любые внутренние переменные будут недоступны извне. Важно отметить, что даже если вы называете свою функцию, IIFE не поднимаются, как стандартные функции, и не могут быть вызваны именем функции, с которой они были объявлены.
(function() {
alert("I've run - but can't be run again because I'm immediately invoked at runtime,
leaving behind only the result I generate");
}());
Это еще один способ написать IIFE. Обратите внимание, что закрывающая скобка перед точкой с запятой была перемещена и размещена сразу после закрывающей фигурной скобки:
(function() {
alert("This is IIFE too.");
})();
Вы можете легко передать параметры в IIFE:
(function(message) {
alert(message);
}("Hello World!"));
Кроме того, вы можете возвращать значения в окружение:
var example = (function() {
return 42;
}());
console.log(example); // => 42
При необходимости можно назвать IIFE. Хотя это часто встречается, этот шаблон имеет несколько преимуществ, например, предоставление ссылки, которая может быть использована для рекурсии, и может упростить отладку, поскольку имя включено в стоп-код.
(function namedIIFE() {
throw error; // We can now see the error thrown in 'namedIIFE()'
}());
Хотя обертывание функции в скобках является наиболее распространенным способом обозначить парсер Javascript, чтобы ожидать выражения, в местах, где выражение уже ожидается, нотация может быть сделана более кратким:
var a = function() { return 42 }();
console.log(a) // => 42
Arrow-версия сразу вызываемой функции:
(() => console.log("Hello!"))(); // => Hello!
Обзор функций
Когда вы определяете функцию, она создает область видимости .
Все, что определено в функции, недоступно кодом вне функции. Только код внутри этой области может видеть объекты, определенные внутри области.
function foo() {
var a = 'hello';
console.log(a); // => 'hello'
}
console.log(a); // reference error
Вложенные функции возможны в JavaScript и применяются те же правила.
function foo() {
var a = 'hello';
function bar() {
var b = 'world';
console.log(a); // => 'hello'
console.log(b); // => 'world'
}
console.log(a); // => 'hello'
console.log(b); // reference error
}
console.log(a); // reference error
console.log(b); // reference error
Когда JavaScript пытается решить ссылку или переменную, он начинает искать ее в текущей области. Если он не может найти это объявление в текущей области, он поднимается на одну область для поиска. Этот процесс повторяется до тех пор, пока декларация не будет найдена. Если парсер JavaScript достигает глобальной области действия и до сих пор не может найти ссылку, будет выбрана эталонная ошибка.
var a = 'hello';
function foo() {
var b = 'world';
function bar() {
var c = '!!';
console.log(a); // => 'hello'
console.log(b); // => 'world'
console.log(c); // => '!!'
console.log(d); // reference error
}
}
Это поведение восхождения также может означать, что одна ссылка может «затенять» по так называемой ссылке во внешнем масштабе, поскольку она становится видимой первой.
var a = 'hello';
function foo() {
var a = 'world';
function bar() {
console.log(a); // => 'world'
}
}
То, как JavaScript разрешает обзор, также относится к ключевому слову const
. Объявление переменной с ключевым словом const
означает, что вам не разрешено переназначать это значение, но объявление ее в функции приведет к созданию новой области действия и новой переменной.
function foo() {
const a = true;
function bar() {
const a = false; // different variable
console.log(a); // false
}
const a = false; // SyntaxError
a = false; // TypeError
console.log(a); // true
}
Однако функции не являются единственными блоками, которые создают область (если вы используете let
или const
). Объявления let
и const
имеют область действия ближайшего оператора блока. См. Здесь более подробное описание.
Связывание `this` и аргументов
Когда вы ссылаетесь на метод (свойство, являющееся функцией) в JavaScript, он обычно не запоминает объект, к которому он был первоначально привязан. Если метод должен ссылаться на этот объект, так как this
он не сможет, и его вызов, вероятно, приведет к сбою.
Вы можете использовать метод .bind()
для функции, чтобы создать оболочку, которая включает в себя значение this
и любое количество ведущих аргументов.
var monitor = {
threshold: 5,
check: function(value) {
if (value > this.threshold) {
this.display("Value is too high!");
}
},
display(message) {
alert(message);
}
};
monitor.check(7); // The value of `this` is implied by the method call syntax.
var badCheck = monitor.check;
badCheck(15); // The value of `this` is window object and this.threshold is undefined, so value > this.threshold is false
var check = monitor.check.bind(monitor);
check(15); // This value of `this` was explicitly bound, the function works.
var check8 = monitor.check.bind(monitor, 8);
check8(); // We also bound the argument to `8` here. It can't be re-specified.
Если не в строгом режиме, функция использует глобальный объект ( window
в браузере) как this
, если функция не вызывается как метод, не привязана или не .call
синтаксисом метода .call
.
window.x = 12;
function example() {
return this.x;
}
console.log(example()); // 12
В строгом режиме this
по умолчанию undefined
window.x = 12;
function example() {
"use strict";
return this.x;
}
console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
Оператор привязки
Оператор double double bind может использоваться как сокращенный синтаксис концепции, описанной выше:
var log = console.log.bind(console); // long version
const log = ::console.log; // short version
foo.bar.call(foo); // long version
foo::bar(); // short version
foo.bar.call(foo, arg1, arg2, arg3); // long version
foo::bar(arg1, arg2, arg3); // short version
foo.bar.apply(foo, args); // long version
foo::bar(...args); // short version
Этот синтаксис позволяет писать нормально, не беспокоясь о привязке this
везде.
Функции привязки консоли к переменным
var log = console.log.bind(console);
Использование:
log('one', '2', 3, [4], {5: 5});
Выход:
one 2 3 [4] Object {5: 5}
Почему ты бы так поступил?
Один случай использования может быть, когда у вас есть пользовательский регистратор, и вы хотите определить время выполнения, которое нужно использовать.
var logger = require('appLogger');
var log = logToServer ? logger.log : console.log.bind(console);
Аргументы функции, параметры аргументов «аргументы», «отдых» и «спред»
Функции могут принимать входные данные в виде переменных, которые могут использоваться и назначаться внутри их собственной области. Следующая функция принимает два числовых значения и возвращает их сумму:
function addition (argument1, argument2){
return argument1 + argument2;
}
console.log(addition(2, 3)); // -> 5
объект arguments
Объект arguments
содержит все параметры функции, которые содержат значение по умолчанию . Он также может использоваться, даже если параметры явно не объявлены:
(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]
Хотя при печати arguments
вывод напоминает массив, это на самом деле объект:
(function() { console.log(typeof arguments) })(); // -> object
Параметры останова: function (...parm) {}
В ES6 синтаксис ...
при использовании в объявлении параметров функции преобразует переменную справа в один объект, содержащий все остальные параметры, предоставленные после объявленных. Это позволяет вызывать функцию с неограниченным количеством аргументов, которые станут частью этой переменной:
(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123
Параметры распространения: function_name(...varb);
В ES6 синтаксис ...
также можно использовать при вызове функции, поместив объект / переменную вправо. Это позволяет передавать элементы этого объекта в эту функцию как один объект:
let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42
Именованные функции
Функции могут быть названы или неназванными ( анонимные функции ):
var namedSum = function sum (a, b) { // named
return a + b;
}
var anonSum = function (a, b) { // anonymous
return a + b;
}
namedSum(1, 3);
anonSum(1, 3);
4
4
Но их имена являются частными в своей области:
var sumTwoNumbers = function sum (a, b) {
return a + b;
}
sum(1, 3);
Uncaught ReferenceError: сумма не определена
Именованные функции отличаются от анонимных функций в нескольких сценариях:
- Когда вы отлаживаете, имя функции будет отображаться в трассировке ошибки / стека
- Именованные функции поднимаются, а анонимные функции не
- Именованные функции и анонимные функции ведут себя по-разному при обработке рекурсии
- В зависимости от версии ECMAScript, именованные и анонимные функции могут относиться к свойству
name
функции по-разному
Именованные функции поднимаются
При использовании анонимной функции функция может вызываться только после строки объявления, тогда как именованная функция может быть вызвана перед объявлением. Рассматривать
foo();
var foo = function () { // using an anonymous function
console.log('bar');
}
Uncaught TypeError: foo не является функцией
foo();
function foo () { // using a named function
console.log('bar');
}
бар
Именованные функции в рекурсивном сценарии
Рекурсивную функцию можно определить как:
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
//you could call 'say' directly,
//but this way just illustrates the example
var sayHelloTimes = say;
sayHelloTimes(2);
Здравствуйте!
Здравствуйте!
Что, если где-то в вашем коде переопределяется первоначальное связывание функций?
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Здравствуйте!
Uncaught TypeError: скажем, не является функцией
Это можно решить, используя именованную функцию
// The outer variable can even have the same name as the function
// as they are contained in different scopes
var say = function say (times) {
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it uses the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Здравствуйте!
Здравствуйте!
И как бонус, именованная функция не может быть установлена undefined
, даже изнутри:
var say = function say (times) {
// this does nothing
say = undefined;
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it's using the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
Здравствуйте!
Здравствуйте!
Свойство name
функций
Перед ES6, названные функции имели их name
свойства устанавливают их имена функций, а также анонимные функции имели их name
свойство устанавливается в пустую строку.
var foo = function () {}
console.log(foo.name); // outputs ''
function foo () {}
console.log(foo.name); // outputs 'foo'
Post ES6, именованные и неназванные функции устанавливают свои свойства name
:
var foo = function () {}
console.log(foo.name); // outputs 'foo'
function foo () {}
console.log(foo.name); // outputs 'foo'
var foo = function bar () {}
console.log(foo.name); // outputs 'bar'
Рекурсивная функция
Рекурсивная функция - это просто функция, которая называла бы себя.
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Вышеупомянутая функция показывает базовый пример того, как выполнять рекурсивную функцию для возврата факториала.
Другим примером было бы получение суммы четных чисел в массиве.
function countEvenNumbers (arr) {
// Sentinel value. Recursion stops on empty array.
if (arr.length < 1) {
return 0;
}
// The shift() method removes the first element from an array
// and returns that element. This method changes the length of the array.
var value = arr.shift();
// `value % 2 === 0` tests if the number is even or odd
// If it's even we add one to the result of counting the remainder of
// the array. If it's odd, we add zero to it.
return ((value % 2 === 0) ? 1 : 0) + countEvens(arr);
}
Важно, чтобы такие функции выполняли какую-то проверку значения часового, чтобы избежать бесконечных циклов. В первом примере выше, когда n
меньше или равно 1, рекурсия останавливается, позволяя возвращать результат каждого вызова обратно в стек вызовов.
Карринг
Currying - это преобразование функции n
arity или аргументов в последовательность n
функций, принимающих только один аргумент.
Варианты использования. Когда значения некоторых аргументов доступны перед другими, вы можете использовать currying для разложения функции в ряд функций, которые завершают работу поэтапно, по мере поступления каждого значения. Это может быть полезно:
- Когда значение аргумента почти никогда не изменяется (например, коэффициент преобразования), но вам нужно поддерживать гибкость установки этого значения (а не жесткого кодирования его как константы).
- Когда результат функции curried полезен до того, как будут выполнены другие функции в карри.
- Чтобы проверить прибытие функций в определенной последовательности.
Например, объем прямоугольной призмы можно объяснить функцией трех факторов: длины ( l
), ширины ( w
) и высоты ( h
):
var prism = function(l, w, h) {
return l * w * h;
}
Вариант этой функции с картой будет выглядеть так:
function prism(l) {
return function(w) {
return function(h) {
return l * w * h;
}
}
}
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;
Вы можете назвать эту последовательность функций prism(2)(3)(5)
, которая должна быть оценена до 30.
Без какой-либо дополнительной техники (например, с библиотеками) каррирование имеет ограниченную синтаксическую гибкость в JavaScript (ES 5/6) из-за отсутствия значений placeholder; таким образом, хотя вы можете использовать var a = prism(2)(3)
для создания частично примененной функции , вы не можете использовать prism()(3)(5)
.
Использование оператора возврата
Оператор return может быть полезным способом создания вывода для функции. Оператор return особенно полезен, если вы не знаете, в каком контексте функция будет использоваться.
//An example function that will take a string as input and return
//the first character of the string.
function firstChar (stringIn){
return stringIn.charAt(0);
}
Теперь, чтобы использовать эту функцию, вам нужно помещать ее вместо переменной где-то еще в вашем коде:
Использование результата функции в качестве аргумента для другой функции:
console.log(firstChar("Hello world"));
Выход консоли будет:
> H
Оператор return завершает функцию
Если мы изменим функцию в начале, мы можем продемонстрировать, что оператор return завершает функцию.
function firstChar (stringIn){
console.log("The first action of the first char function");
return stringIn.charAt(0);
console.log("The last action of the first char function");
}
Выполнение этой функции будет выглядеть так:
console.log(firstChar("JS"));
Консольный выход:
> The first action of the first char function
> J
Он не будет печатать сообщение после оператора return, так как функция теперь завершена.
Оператор возврата, охватывающий несколько строк:
В JavaScript вы обычно можете разделить строку кода на многие строки для удобства чтения или организации. Это действительный JavaScript:
var
name = "bob",
age = 18;
Когда JavaScript видит неполное выражение, например var
он смотрит на следующую строку, чтобы завершить себя. Однако, если вы сделаете ту же ошибку с оператором return
, вы не получите то, что ожидаете.
return
"Hi, my name is "+ name + ". " +
"I'm "+ age + " years old.";
Этот код вернет undefined
потому что return
сам по себе является полным утверждением в Javascript, поэтому он не будет смотреть на следующую строку, чтобы завершить себя. Если вам нужно разделить оператор return
на несколько строк, поставьте значение рядом с ним, прежде чем вы его разложите, например.
return "Hi, my name is " + name + ". " +
"I'm " + age + " years old.";
Передача аргументов по ссылке или значению
В JavaScript все аргументы передаются по значению. Когда функция назначает новое значение переменной аргумента, это изменение не будет отображаться вызывающему:
var obj = {a: 2};
function myfunc(arg){
arg = {a: 5}; // Note the assignment is to the parameter variable itself
}
myfunc(obj);
console.log(obj.a); // 2
Тем не менее, изменения , сделанные в (вложенных) свойств таких аргументов, будут видны вызывающему абоненту:
var obj = {a: 2};
function myfunc(arg){
arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5
Это можно рассматривать как вызов по ссылке : хотя функция не может изменить объект вызывающего объекта, присвоив ему новое значение, он может мутировать объект вызывающего.
Поскольку примитивные значащие аргументы, такие как числа или строки, неизменяемы, нет возможности для их мутации:
var s = 'say';
function myfunc(arg){
arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'
Когда функция хочет мутировать объект, переданный как аргумент, но не хочет фактически мутировать объект вызывающего, переменная аргумента должна быть переназначена:
var obj = {a: 2, b: 3};
function myfunc(arg){
arg = Object.assign({}, arg); // assignment to argument variable, shallow copy
arg.a = 5;
}
myfunc(obj);
console.log(obj.a); // 2
В качестве альтернативы мутации аргумента на месте функции могут создавать новое значение на основе аргумента и возвращать его. Затем вызывающий может назначить его даже исходной переменной, которая была передана как аргумент:
var a = 2;
function myfunc(arg){
arg++;
return arg;
}
a = myfunc(a);
console.log(obj.a); // 3
Позвонить и подать заявку
Функции имеют два встроенных метода, которые позволяют программисту передавать аргументы и this
переменную по-разному: call
и apply
.
Это полезно, поскольку функции, которые работают на одном объекте (объект, который является свойством), могут быть перенастроены для работы на другом совместимом объекте. Кроме того, аргументы могут быть заданы одним выстрелом как массивы, аналогичные оператору spread ( ...
) в ES6.
let obj = {
a: 1,
b: 2,
set: function (a, b) {
this.a = a;
this.b = b;
}
};
obj.set(3, 7); // normal syntax
obj.set.call(obj, 3, 7); // equivalent to the above
obj.set.apply(obj, [3, 7]); // equivalent to the above; note that an array is used
console.log(obj); // prints { a: 3, b: 5 }
let myObj = {};
myObj.set(5, 4); // fails; myObj has no `set` property
obj.set.call(myObj, 5, 4); // success; `this` in set() is re-routed to myObj instead of obj
obj.set.apply(myObj, [5, 4]); // same as above; note the array
console.log(myObj); // prints { a: 3, b: 5 }
В ECMAScript 5 был добавлен еще один метод, называемый bind()
в дополнение к call()
и apply()
чтобы явно установить this
значение функции в конкретный объект.
Он ведет себя совершенно иначе, чем два других. Первый аргумент bind()
- this
значение для новой функции. Все остальные аргументы представляют собой именованные параметры, которые должны быть постоянно заданы в новой функции.
function showName(label) {
console.log(label + ":" + this.name);
}
var student1 = {
name: "Ravi"
};
var student2 = {
name: "Vinod"
};
// create a function just for student1
var showNameStudent1 = showName.bind(student1);
showNameStudent1("student1"); // outputs "student1:Ravi"
// create a function just for student2
var showNameStudent2 = showName.bind(student2, "student2");
showNameStudent2(); // outputs "student2:Vinod"
// attaching a method to an object doesn't change `this` value of that method.
student2.sayName = showNameStudent1;
student2.sayName("student2"); // outputs "student2:Ravi"
Параметры по умолчанию
До ECMAScript 2015 (ES6) значение параметра по умолчанию можно было бы присвоить следующим образом:
function printMsg(msg) {
msg = typeof msg !== 'undefined' ? // if a value was provided
msg : // then, use that value in the reassignemnt
'Default value for msg.'; // else, assign a default value
console.log(msg);
}
ES6 предоставил новый синтаксис, в котором условие и переназначение, изображенные выше, больше не нужны:
function printMsg(msg='Default value for msg.') {
console.log(msg);
}
printMsg(); // -> "Default value for msg."
printMsg(undefined); // -> "Default value for msg."
printMsg('Now my msg in different!'); // -> "Now my msg in different!"
Это также показывает, что если параметр отсутствует при вызове функции, его значение сохраняется как undefined
, поскольку его можно подтвердить, явно предоставив его в следующем примере (используя функцию стрелки ):
let param_check = (p = 'str') => console.log(p + ' is of type: ' + typeof p);
param_check(); // -> "str is of type: string"
param_check(undefined); // -> "str is of type: string"
param_check(1); // -> "1 is of type: number"
param_check(this); // -> "[object Window] is of type: object"
Функции / переменные в качестве значений по умолчанию и повторное использование параметров
Значения параметров по умолчанию не ограничиваются числами, строками или простыми объектами. Функция также может быть установлена как значение по умолчанию callback = function(){}
:
function foo(callback = function(){ console.log('default'); }) {
callback();
}
foo(function (){
console.log('custom');
});
// custom
foo();
//default
Существуют определенные характеристики операций, которые могут выполняться с использованием значений по умолчанию:
- Ранее объявленный параметр может быть повторно использован как значение по умолчанию для значений предстоящих параметров.
- Внутренние операции разрешены при назначении значения по умолчанию для параметра.
- Переменные, существующие в той же области объявляемой функции, могут использоваться в значениях по умолчанию.
- Функции могут быть вызваны, чтобы обеспечить их возвращаемое значение в значение по умолчанию.
let zero = 0;
function multiply(x) { return x * 2;}
function add(a = 1 + zero, b = a, c = b + a, d = multiply(c)) {
console.log((a + b + c), d);
}
add(1); // 4, 4
add(3); // 12, 12
add(2, 7); // 18, 18
add(1, 2, 5); // 8, 10
add(1, 2, 5, 10); // 8, 20
Повторное использование возвращаемого значения функции в новом значении вызова:
let array = [1]; // meaningless: this will be overshadowed in the function's scope
function add(value, array = []) {
array.push(value);
return array;
}
add(5); // [5]
add(6); // [6], not [5, 6]
add(6, add(5)); // [5, 6]
значения arguments
и длина при отсутствии параметров в вызове
Объект массива arguments
сохраняет только параметры, значения которых не являются значениями по умолчанию, то есть те, которые явно указаны при вызове функции:
function foo(a = 1, b = a + 1) {
console.info(arguments.length, arguments);
console.log(a,b);
}
foo(); // info: 0 >> [] | log: 1, 2
foo(4); // info: 1 >> [4] | log: 4, 5
foo(5, 6); // info: 2 >> [5, 6] | log: 5, 6
Функции с неизвестным количеством аргументов (вариативные функции)
Чтобы создать функцию, которая принимает неопределенное количество аргументов, в зависимости от среды есть два метода.
Всякий раз, когда вызывается функция, у нее есть объект Array-like arguments в своей области, содержащий все аргументы, переданные функции. Индексирование или повторение этого приведет к доступу к аргументам, например
function logSomeThings() {
for (var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
}
}
logSomeThings('hello', 'world');
// logs "hello"
// logs "world"
Обратите внимание, что вы можете преобразовать arguments
в фактический массив, если это необходимо; см.: Преобразование объектов массива в массивы
Начиная с ES6, функция может быть объявлена последним параметром с помощью оператора rest ( ...
). Это создает массив, содержащий аргументы с этого момента и далее
function personLogsSomeThings(person, ...msg) {
msg.forEach(arg => {
console.log(person, 'says', arg);
});
}
personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"
Функции также можно вызвать аналогичным образом, синтаксис распространения
const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]
logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]
Этот синтаксис может использоваться для вставки произвольного количества аргументов в любую позицию и может использоваться с любым итерабельным ( apply
принимает только объекты, подобные массиву).
const logArguments = (...args) => console.log(args)
function* generateNumbers() {
yield 6
yield 5
yield 4
}
logArguments('a', ...generateNumbers(), ...'pqr', 'b')
// output: Array [ "a", 6, 5, 4, "p", "q", "r", "b" ]
Получить имя объекта функции
ES6 :
myFunction.name
Объяснение по MDN . С 2015 года работает в nodejs и во всех основных браузерах, кроме IE.
ES5 :
Если у вас есть ссылка на функцию, вы можете сделать:
function functionName( func )
{
// Match:
// - ^ the beginning of the string
// - function the word 'function'
// - \s+ at least some white space
// - ([\w\$]+) capture one or more valid JavaScript identifier characters
// - \( followed by an opening brace
//
var result = /^function\s+([\w\$]+)\(/.exec( func.toString() )
return result ? result[1] : ''
}
Частичное применение
Подобно currying, частичное приложение используется для уменьшения количества аргументов, переданных функции. В отличие от currying, число не должно опускаться одним.
Пример:
Эта функция ...
function multiplyThenAdd(a, b, c) {
return a * b + c;
}
... можно использовать для создания другой функции, которая всегда будет умножаться на 2, а затем добавить 10 к переданному значению;
function reversedMultiplyThenAdd(c, b, a) {
return a * b + c;
}
function factory(b, c) {
return reversedMultiplyThenAdd.bind(null, c, b);
}
var multiplyTwoThenAddTen = factory(2, 10);
multiplyTwoThenAddTen(10); // 30
Часть «приложения» частичного приложения просто означает фиксирование параметров функции.
Состав функции
Составление нескольких функций в одном - это обычная практика функционального программирования;
композиция делает конвейер, через который наши данные будут проходить и модифицироваться, просто работая над функциональным составом (точно так же, как привязка фрагментов дорожки вместе) ...
вы начинаете с отдельных функций ответственности:
const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
const sign = x => x + ',\nmade with love';
и легко создать дорожку преобразования:
const formatText = compose(capitalize, sign);
formatText('this is an example')
//This is an example,
//made with love
NB Состав достигается с помощью функции полезности, обычно называемой compose
как в нашем примере.
Реализация compose
присутствует во многих библиотеках служебных программ JavaScript ( lodash , rambda и т. Д.), Но вы также можете начать с простой реализации, такой как:
const compose = (...funs) =>
x =>
funs.reduce((ac, f) => f(ac), x);