call
и apply
Эти два метода объекта Function
позволяют вызвать функцию, указав this
и параметры:
function Person(name, surname){
this.name = name
this.surname = surname
this.getFullName = function(){
return this.name + (this.fatherName ? " " + this.fatherName + " " : " ") + this.surname
}
}
function say(greet, aftergreet){
alert(greet + " " + this.getFullName() + aftergreet)
}
var person = new Person("Ivan", "Petroff")
say.call(person,"Hello", "!!111!!!")
say.apply(person,["Hi", "by apply"]) //feel the difference
То, что apply
принимает массив в качестве набора параметров функции помогает творить чудеса:
var someArray = [1,5,7,-17,100500];
Math.min(someArray) //Math.min так не умеет
Math.min(1,5,7,-17,100500,-123) // зато умеет так
Math.min.apply(Math,someArray) // ...и мы этим воспользуемся...
// или же сделаем свой min в someArray:
someArray.min = function(){
return Math.min.apply(Math,someArray);
}
someArray.min()
// или же сделаем свой min для всех массивов
Array.prototype.min = function(){
return Math.min.apply(Math, this)
};
[100, 500, -100500].min()
Функция в JS находится сразу в двух контекстах: динамическом (this
, значения параметров) и лексическом (переменные из более высоких областей
видимости в месте определения функции). Динамический контекст - это контекст вызова функции - значение параметров и окружение на момент вызова;
лексический контекст - контекст определения функции, её вложенности в другие области видимости, доступ к которым функция имеет и после окончания
выполнения функций-владельцев этих областей видимости.
function makeAdder(x){
function adder(y){
return x + y;
}
return adder
}
var add5 = makeAdder(5);
var greeter = makeAdder("Hi, ");
alert(add5(2));
alert(greeter("John"));
В примере выше 5
,"Hi, "
, 2
и "John"
находятся в динамическом контексте вызова функции. А вот значение x
в adder
находится в лексическом
контексте. На момент запуска функций add5
и greeter
функции makeAdder
уже давно отработали, однако значение x
и область видимости продолжают
присутствовать в функциях add5
и greeter
. Это называется замыканием - данные в локальной области видимости отработавшей функции и код, который
использует эти данные впоследствии. В широком смысле замыкание является объектом, так как хранит в единой сущности код и данные.
В контексте JS замыкания очень удобны для огораживания кода, сохранения состояния переменных "на будущее", для функций обратного вызова. Так же замыкания удобны для организации приватных полей у объектов JS.
function makeCounter(){
var counter = 0;
return function(){
return counter++;
}
}
Функция выше создает счетчик, значение которого можно узнать из возвращаемой анонимной функции. При этом счетчик увеличится на 1. Если расширить функционал данного примера для чтения и декремента счетчика, получим, например:
function makeCounter(){
var counter = 0;
function inc(){
return counter++;
}
function dec(){
return counter--;
}
function read(){
return counter;
}
return [inc,dec,read];
}
Результатом выполнения makeCounter
будет массив функций. Однако намного более наглядным будет создание именованного массива, т. е. объекта:
function makeCounter(){
var counter = 0;
function inc(){
return counter++;
}
function dec(){
return counter--;
}
function read(){
return counter;
}
return {inc: inc, dec: dec, read: read};
}
... что во многом схоже с обычным объектом сделанным через функцию-конструктор:
function Person(name, surname){
this.name = name
this.surname = surname
var age = 0
function checkNewAge(){
return (!isNaN(this.newAge) && Number.isInteger(this.newAge) && (this.newAge > 0) && (this.newAge < 100))
}
this.setAge = function(newAge){
this.newAge = +newAge
if (checkNewAge()){
age = this.newAge;
}
return age
}
this.getAge = function(){
return age
}
this.getOriginalName = function(){
return name;
}
this.getOriginalSurname = function(){
return surname;
}
}
var person = new Person("Ivan", "Petroff")
alert(person.setAge(50))
alert(person.setAge(125))
alert(person.setAge(25))
person.name = "John";
person.surname = "Doe";
alert(person.getOriginalName());
alert(person.getOriginalSurname());
Рекурсия - это прямой или косвенный самовызов функции. Обычно применяется для обработки деревьев в структурах данных или иных вложенностей.
Например факториал (!
) - произведение всех чисел от 1
до N
можно определить как:
N ! = 1 x 2 x 3 x 4 x .... x N,
или рекурсивно:
N ! = N x (N - 1) !
function factorial(N){
if (N <= 1){
return 1;
}
return N * factorial(N -1);
}
factorial ( 5 ); // это 5 * factorial(4), что, в свою очередь, будет 4 * factorial(3) и так далее
При отладке рекурсий обращайте внимания на вложенность вызовов (Call Stack) и областей видимостей (Scopes).