Javascript - самовыполняющиеся функции: зачем их использовать, если я могу создать локальную область видимости с несамоисполняющимися функциями?
Я знаю, что здесь и в других местах есть много сообщений о самофункционирующих функциях, но у меня все еще есть некоторые вопросы после прочтения сообщений.
Зачем мне когда-либо назначать самозапускающуюся функцию для переменной? Если кажется, что они выполняют себя в любом случае.
var myFunc=(function() { console.log('Hello World'); })();
Я много читал о том, что причина использования самозапускающихся функций состоит в том, чтобы сохранять переменные закрытыми. Если у меня есть функция, которая не выполняет сам себя, то все, что я определяю внутри этой функции, все равно будет приватным?!
(function() { var name="my Name" console.log(name); })(); vs. function() { var name="my Name" console.log(name); }; //its the same
Поэтому я не совсем понимаю, как самоисполняющиеся функции поддерживают локальную область видимости (как вы можете сделать это, используя не самовыполняющиеся функции), поэтому единственная причина, которую я вижу, - это использовать их, когда вы хотите выполнить их автоматически, например, при загрузке страницы.
Спасибо!
еще один вопрос:
var test=(function myFunc(){
var name="Hello World"
return {
test1: function(){
return name;
},
test2:function(){
return name+"1"
}
}
})()
test.test1()
vs
var test=function myFunc(){
var name="Hello World"
return {
test1: function(){
return name;
},
test2:function(){
return name+"1"
}
}
}
test.test1()
-> Что именно здесь происходит, что из-за IIFE я могу фактически выполнить test.test1(), а не с обычной функцией?
8 ответов
Вы обычно заключаете свои функции в анонимную функцию, когда хотите сохранить свою область действия. Это также часть шаблона модуля, который все еще довольно популярен:
https://toddmotto.com/mastering-the-module-pattern/
Затем вы можете присвоить результат этого IIFE переменной, чтобы получить доступ к вашей области, только вызвав эту переменную.
myScope.myLocallyScopedProperty or myScope[myLocallyScopedProperty]
Ваша другая функция должна вызываться вручную, и она также доступна из любого места.
Предлагаю прочитать статью Тодда Мото, это многое объясняет.
Если этот IIFE ничего не возвращает, тогда действительно абсолютно бесполезно присвоить его чему-либо. Хотя, конечно, могут быть примеры, когда IIFEs возвращают то, что вы хотите использовать позже; в этом случае IIFE является частной областью действия для установки какого-либо объекта, например:
var foo = (function () { var bar = 'something'; // here be dragons return baz; })();
Это дает вам частную область для сборки
baz
без излишней утечки временных переменных в глобальную область.В этих примерах нет разницы, за исключением того, что второй не выполняется и, следовательно, никогда ничего не делает. Сфера охвата и цель для определения объема остаются неизменными.
Вы используете самостоятельно выполняемые функции, чтобы вывести из области видимости только то, что вам нужно. Я думаю, у меня есть несколько четкий пример:
let myObject = (function(){
let privateVariable = "I'm private";
function privateMethod() {
//private method
};
function methodToExpose() {
//this method I will expose
}
//what is returned here, is what is public
return {
PublicMethod: methodToExpose
//other public properties
}
}());
Итак, функция запускается немедленно, и в результате получается объект, который определяется тем, что я возвратил из функции.
Другой пример, который я мог бы вам дать, - это сохранить переменные текущей области видимости внутри замыкания, но вы бы не стали так часто их использовать, поскольку теперь у нас есть let
, Практический пример:
<span id="1">old</span>
<span id="2">old</span>
<span id="3">old</span>
<span id="4">old</span>
<script>
var toPrint = "";
for (var i = 1; i <= 4; i++) {
toPrint = "new: " + i;
document.getElementById(i.toString()).addEventListener('click', function(event){ event.target.innerHTML = toPrint; })
}
</script>
Когда вы щелкнете по диапазону, значение будет заменено значением... "new: 4"! Это потому, что когда вы закончили выполнение, это значение имеет значение toPrint. Функция, назначенная событию click, извлекает этот toPrint, и в тот момент, когда он его получает, он "новый: 4". Мы решаем это с помощью замыканий:
<span id="1">old</span>
<span id="2">old</span>
<span id="3">old</span>
<span id="4">old</span>
<script>
var toPrint = "";
for (var i = 1; i <= 4; i++) {
toPrint = "new: " + i;
document.getElementById(i.toString()).addEventListener('click', function(event){
var currPrint = toPrint;
return function(event){ event.target.innerHTML = currPrint ; };
}())
}
</script>
Используя самовыполняющуюся функцию, мы сохраняем текущее значение toPrint в переменной currPrint в локальной области видимости. Когда мы позже щелкаем по диапазону, функция, назначенная даже клику, будет использовать переменную currPrint, которая содержит значение, которое toPrint имело в момент назначения функции, а не значение, которое toPrint имеет при завершении выполнения.
Обратите внимание, что это решается также с помощью let вместо var, но все же это пример самовыполняющихся функций:)
1: Назначить IIFE для локальной переменной имеет смысл для чего-то подобного:
var getID = (function () {
var id = 0;
return function () { return id++; };
})();
Таким образом, вы можете получить новые идентификаторы, не рискуя сбросить внутренний счетчик из любого места в коде, кроме как путем повторного выделения переменной.
2: По сути, вы создаете Scope, создавая функцию. Но если вы не выполните его, ну, это ничего не делает. Так что, если у вас есть это:
function () { return 'foo' };
Как вы хотите вызвать его, если он не назначен переменной или не имеет имени? Само по себе это ничего не сделает, так как это не называется. Нечто подобное является мертвым кодом и может быть безопасно удалено.
Во-первых, вкратце, это не самореализующиеся функции. (Это была бы рекурсивная функция.) Это выражения, вызванные встроенными функциями (IIFE). Функция не вызывает сама себя, выражение вызывает функцию.
Зачем мне когда-либо назначать самозапускающуюся функцию для переменной?
Это не то, что делает этот код. Он присваивает результат вызова IIFE переменной. Вы будете использовать его, когда хотите получить такой результат, например:
var x = (function() {
var n = 0;
return {
increment: function() { return ++n; }
};
})();
console.log(typeof x); // "object"
console.log(x.increment()); // 1
console.log(x.increment()); // 2
x
не получает IIFE, он получает то, что возвращает IIFE - в данном случае объект с функцией на нем.
Я много читал о том, что причина использования самозапускающихся функций состоит в том, чтобы сохранять переменные закрытыми. Если у меня есть функция, которая не выполняет сам себя, то все, что я определяю внутри этой функции, все равно будет приватным?!
Да, это правда. Вы используете IIFE, когда вам нужно только один раз сделать то, что находится внутри IIFE. В противном случае, безусловно, вы определяете функцию, даете ей имя, а затем повторно используете ее там, где вам это нужно. Переменные внутри функции действительно являются частными для нее (если не представлены каким-либо образом) и являются специфическими для каждого вызова функции.
Твоя первая вещь не имеет никакого смысла
var myFunc = =(function() {
console.log('Hello World');
})();
myFunc
это не функция, и это undefined
,
Смысл, на мой взгляд, для самоисполняющейся функции - упаковывать некоторый код, который должен быть выполнен сразу же.
var p1=1, p2=2, obj = {
prop: (function(x,y){ return x+y;})(p1, p2)
}
ИЛИ избегая перезаписи уже определенных функций / объектов в случае, если ваш скрипт будет вставлен в уже существующее приложение, а также создавая некие приватные методы, если вам нравится:
function aFunction() {
console.log('a code');
}
(function(w) {
function aFunction() {
console.log('b code');
}
w.run = function() {
aFunction();
};
})(window)
aFunction();
run();
В форме IIFE (или непосредственно вызванных выражений функций) они могут затем использоваться для создания плагинов или использоваться в качестве пространств имен и присоединяться к окну / jquery / или другому объекту глобального уровня для последующего использования.
Когда вы называете функцию, например, присваиваете анонимную функцию переменной, вы можете использовать ее позже, вызывая переменную с круглыми скобками, в вашем примере определив myFunc с помощью
var myFunc=(function() {
console.log('Hello World');
});
Используйте это позже в коде как myFunc()
;
В вашем примере вы сохраняете выходные данные функции непосредственно в переменной, вызывая ее немедленно, и выходные данные не сохраняются.
Итак, если вы позже напишите console.log(myFunc);
, есть undefined
в качестве вывода.
Лучший пример IIFE из ваших примеров кода приведен ниже.
(function() {
var name="my Name"
console.log(name);
})();
Он выполняет, делает console.log и все. Ничего не добавляет к пространству имен или глобальному объекту в вашем случае.
Последний из ваших примеров определяет функцию, затем не выполняет ее, и, поскольку ей не назначена именованная переменная или имя, она дает синтаксическую ошибку и не может быть использована позже в коде. Итак, приведенный ниже пример бесполезен.
function() {
var name="my Name"
console.log(name);
};
Вы добавили еще два примера с var test
знак равно function myFunc
, Первый будет хорошо работать с test.test1()
, Для второго вам нужно сначала оценить test как функцию, а затем вызвать ее функцию, например test().test1()
,
Я думаю, вы что-то здесь упускаете. Просто чтобы прояснить основные вещи - если вы присваиваете функцию, которая выполняется самостоятельно, переменной return value
функции при выполнении в это время присваивается переменной, а не самой функции.
var myFunc = (function() {
console.log('Hello World');
})();
myFunc(); // will throw error: TypeError: myFunc is not a function
myFunc === undefined
var myFunc = (function() {
console.log('Hello World');
return 'japp';
})();
myFunc(); // will throw error: TypeError: myFunc is not a function
myFunc === 'japp'
Так почему же этот шаблон?
IIFE очень полезны для
ограничить сферу, если вы объявляете
var test = 'test'; // var test actually will be assigned as a property of the current context (this) window.test = 'test'; // so you pollute the global namespace which is not a good practice
так что это будет лучше
(function() { var test = 'test'; })();
Еще один очень хороший момент с ИИФ заключается в том, что вы можете добиться дизайна с "рядовыми"
var myFunc; (function() { var i = 0; // i is available for myFunc with private access myFunc = function() { console.log( ++i ) }; })(); myFunc(); // logs 1 myFunc(); // logs 2