Суммировать
Определение: замыкания позволяют функции получать доступ к переменным и функциям и манипулировать ими в той области видимости, в которой она была объявлена, и могут быть вызваны, даже если область действия во время объявления исчезает.
применение:
- частная переменная
- Обратные вызовы и таймеры
- привязать контекст функции
- Частично примененная функция
- Перегрузка функций: кэш-память, перенос функций
- Непосредственные функции: независимая область видимости, лаконичный код, циклы, обертки библиотек классов, ограничение имен в области видимости параметрами
предисловие
В последнее время я был занят проектами компании, и у меня нет времени продолжать интервью и оскорбления, я нашел время только для чтения «JavaScript Ninja Cheats».
Я был немного взволнован и бессонница сегодня вечером, поэтому я просто написал краткое изложение знаний закрытия.
Содержание в основном все из читов ниндзя.Если вы думаете, что это хорошо написано, вы можете прочитать книгу внимательно; если вы думаете, что это плохо написано, это может быть потому, что я не понимаю его должным образом, что приводит к ошибкам в моем собственном мышлении в тексте, или я могу опустить это.Пошаговый процесс в книге приводит к пропуску некоторых знаний. По разным причинам, пожалуйста, поправьте меня.
текст
Прочитав много статей, они говорят об определении замыканий и преимуществах и недостатках замыканий. Как по мне, добавим применение замыканий.
Определение замыкания можно найти во многих статьях, я помню, что есть угол, который говорит, что пока есть доступ к внешним переменным, это замыкание, и есть другой угол, что все функции являются замыканиями.
Я думаю, что эти ответы правильные, но интервьюеру неудобно продолжать спрашивать или ему нелегко направлять интервьюера. Итак, если бы я брал интервью, я бы использовал определение из The Ninja Cheats:Замыкание — это область, созданная при создании функции, позволяющая собственной функции получать доступ и манипулировать переменными вне собственной функции.. Это немного запутанно, более ясная версия:Замыкания позволяют функции получать доступ и манипулировать переменными и функциями в объявленной области, и даже если объявленная область исчезает, могут быть вызваны. Следует отметить, что замыкание — это не снимок состояния на момент создания, а реальная инкапсуляция, которую можно модифицировать, пока существует замыкание.
Самое простое закрытие:
// 全局作用于就是一个闭包
var outerVal = 'lionel';
function outerFn(){
console.log(outerVal)
}
outerFn() // lionel
Сложнее то, что мы думаем:
var outerVal = 'lionel';
var later;
function outerFn(){
var innerVal = 'karma';
function innerFn(){
console.log(outerVal, innerVal);
}
later = innerFn;
}
outerFn(); // 此时outerFn的作用域已经消失了
later(); // lionel karma
Непонятно, этот пример мы можем понять, замыкания не являются снимками:
var later;
function outerFn(){
function innerFn(){
console.log(lateVal)
}
later = innerFn;
}
console.log(lateVal); // undefined
var lateVal = 'lionel'; // 变量提升,闭包声明的那一刻存在这个变量
outerFn();
later(); // lionel
Недостаток всем знаком, информация в замыкании всегда будет храниться в памяти. Решение состоит в том, чтобы очищать ссылки там, где, по вашему мнению, можно, как в приведенном выше примере, использовать Later = null , чтобы замыкание могло быть очищено при следующей сборке мусора.
Сосредоточимся на закрытиипрактическое применение.
1. Частные переменные
Распространенное использование замыканий, инкапсулирующих приватные переменные. Пользователь не может напрямую получить и изменить значение переменной, но должен вызвать метод, и такое использование может создать частную переменную, доступную только для чтения. Мы понимаем из следующего примера:
function People(num) { // 构造器
var age = num;
this.getAge = function() {
return age;
};
this.addAge = function() {
age++;
};
}
var lionel = new People(23); // new方法会固化this为lionel哦
lionel.addAge();
console.log(lionel.age); // undefined
console.log(lionel.getAge()); // 24
var karma = new People(20);
console.log(karma.getAge()); // 20
Как показано на рисунке ниже, атрибут age не существует в lionel, age существует только в области видимости нового процесса, а в getAge и addAge мы видим, что их область действия содержит замыкание People.
2. Обратные вызовы и таймеры
Я мало говорил об этой части.
3. Контекст функции привязки
Я могу быть немного сбит с толку, когда впервые увидел это приложение. Если хорошенько подумать, мы видели его много раз. Вот как реализована функция bind(). Вот еще одна простая реализация кода:
Function.prototype.myBind = function() {
var fn = this,
args = [...arguments],
object = args.shift();
return function() {
return fn.apply(object, args.concat(...arguments))
}
}
Обратите внимание: bind() не является заменой для применения и вызова. Основной целью этого метода является управление последующими контекстами выполнения с помощью анонимных функций и замыканий.
В-четвертых, функция частичного приложения
Функция частичного применения возвращает функцию с параметрами предварительной обработки, чтобы ее можно было вызвать позже. Просто посмотрите на код
Function.prototype.partial = function() {
var fn = this,
args = [...arguments];
return function() {
var arg = 0;
var argsTmp = [...args]
for (var i=0; i<argsTmp.length && arg < arguments.length; i++) {
if (argsTmp[i] === undefined) {
argsTmp[i] = arguments[arg++]
}
}
return fn.apply(this, argsTmp)
}
}
function addAB(a ,b) {
console.log( a + b);
}
var hello = addAB.partial('hello ', undefined);
hello('lionel'); // hello lionel
hello('karma'); // hello karma
var bye = addAB.partial(undefined, ' bye')
bye('lionel'); // lionel bye
bye('karma'); // karma bye
Приведенный выше пример может быть немного сложным для понимания, вот упрощенный пример:
function add(a) {
return function(b) {
console.log( a + b);
};
}
var hello = add('hello ')
hello('lionel'); // hello lionel
hello('karma'); // hello karma
эммм... Я долго писал сюда, чтобы изучить разницу между каррированием и частичными функциями, и наконец нашел статью, которая соответствует моей идее: частичные функции и каррирование функций, пожалуйста, поправьте меня, если я ошибаюсь.
Пять, перегрузка функций
1 кэш-память
Мы можем обернуть функцию через замыкание, чтобы человек, который вызывает нашу функцию, не знал, что мы приняли метод кэширования, или, другими словами, результат вычисления может быть кэширован без каких-либо дополнительных действий со стороны вызывающей стороны.
Function.prototype.memoized = function(key) {
this._values = this._values || {};
return this._values[key] !== undefined ?
this._values[key] + ' memoized' :
this._values[key] = this.apply(this, arguments);
}
Function.prototype.memoize = function() {
var fn = this;
return function() {
// return fn.memoized.apply(fn, arguments);
console.log(fn.memoized.apply(fn, arguments))
}
}
var computed = (function(num){
// 这里有超级超级复杂的计算,耗时特别久
console.log('----计算了很久-----')
return 2
}).memoize();
computed(1); // ----计算了很久----- 2
computed(1); // 2 memoized
2 функциональная оболочка
Пример ниже написан не так хорошо, как в книге.
function wrap(object, method, wrapper){
var fn = object[method];
return object[method] = function() {
return wrapper.apply(this, [fn.bind(this)].concat(...arguments))
}
}
let util = {
reciprocal: function(tag){
console.log(1 / tag)
}
}
wrap(util, 'reciprocal', function(original, tag){
return tag == 0 ? 0 : original(tag)
})
util.reciprocal(0); // 0
6. Немедленная функция
Следующий рисунок является хорошей иллюстрацией того, почему мгновенные функции вводятся в замыканиях:
1 независимый прицел
var button = $('#mybtn');
(function(){
var numClicks = 0;
button.click = function(){
alert(++numClicks)
}
})
2 Краткий код
// 例如有如下data
data = {
a: {
b: {
c: {
get: function(){},
set: function(){},
add: function(){}
}
}
}
}
// 第一种调用这三个方法的代码如下, 繁琐
data.a.b.c.get();
data.a.b.c.set();
data.a.b.c.add();
// 第二种方法如下, 引入多余变量
var short = data.a.b.c;
short.get();
short.set();
short.add();
// 第三种使用即时函数 优雅
(function(short){
short.get();
short.set();
short.add();
})(data.a.b.c)
3 цикла
Эта часть вызывает setTimeout в классическом цикле for для печати i. Причина, по которой i выводится как фиксированное значение, заключается в том, что замыкание — это не моментальный снимок, а ссылка на переменную. Когда оно выполняется в асинхронной очереди, i изменилось.
Решение состоит в том, чтобы использовать другое замыкание и непосредственную функцию.
4 оболочки библиотеки классов
// 下方的代码展示了,为什么jquery库中,它可以放心的用jquery而不担心这个变量被替换
(function(){
var jQuery = window.jQuery = function() {
// Initialize
};
// ...
})()
5 Ограничение имен в области действия параметрами
// 当我们担心jquery中的$符号,被其他库占用,导致我们代码出问题的时候,
// 用下面的方法,就可以放心大胆的用啦(不过要注意:如果jQuery也被占用的话就...)
(function($){
$.post(...)
})(jQuery)