2015-08-19 6 views
0

Я просто изучаю работу закрытия и начал играть с помощью некоторого кода. К моему пониманию, закрытие - это какая-то память, которая сохраняет среду функции в этом состоянии, когда было создано и оставлено замыкание (когда оно вернулось или привязано к событию), даже когда родительская функция закончилась.Закрытие Javascript и глобальные переменные

Так что я пробовал код ниже, который работает как ожидалось. Две кнопки, которые переключают свои соответствующие текстовые поля с «check» на «saved».

//var v1; 
 
//var v2; 
 
//var v3; 
 

 
function func1(src, action, arg) { 
 
    document.getElementById(arg).value = "check"; 
 
    document.getElementById(src.id).onclick = function() { 
 
    func2(src, action, arg); 
 
    }; 
 
    //v1 = src; 
 
    //v2 = action; 
 
    //v3 = arg; 
 
} 
 

 
function func2(v1, v2, v3) { 
 
    document.getElementById(v3).value = "saved"; 
 
    document.getElementById(v1.id).onclick = function() { 
 
    func1(v1, v2, v3); 
 
    }; 
 
}
<input type=text id="t1"> 
 
<input type=button id="btn1" value="Go 1" onclick="func1(this, 'edit', 't1')"> 
 
<input type=text id="t2"> 
 
<input type=button id="btn2" value="Go 2" onclick="func1(this, 'edit', 't2')">

Но теперь наступает замешательство. Когда я использую globals для построения замыкания в func2(), ошибка переключателей. Смотрите ниже код:

var v1; 
 
var v2; 
 
var v3; 
 

 
function func1(src, action, arg) { 
 
    document.getElementById(arg).value = "check"; 
 
    document.getElementById(src.id).onclick = function() { 
 
    func2(); 
 
    }; 
 
    v1 = src; 
 
    v2 = action; 
 
    v3 = arg; 
 
} 
 

 
function func2() { 
 
    document.getElementById(v3).value = "saved"; 
 
    document.getElementById(v1.id).onclick = function() { 
 
    func1(v1, v2, v3); 
 
    }; 
 
}
<input type=text id="t1"> 
 
<input type=button id="btn1" value="Go 1" onclick="func1(this,'edit','t1')"> 
 
<input type=text id="t2"> 
 
<input type=button id="btn2" value="Go 2" onclick="func1(this,'edit','t2')">

Нажмите на Go1 -> Textbox1 = проверить; Нажмите Go2 -> Textbox2 = check; Но теперь нажмите Go1 -> Textbox2 (вместо 1) = сохранено.

Так что, кажется, что переменные v1, v2, v3 в замыкании по-прежнему используют глобальные значения вне замыкания. Может кто-нибудь объяснить, почему и что делать, это работает с глобалами?

Благодаря T.J.Crowder я обновил свой код, чтобы использовать закрытые переменные в закрытии. Но, к сожалению, это все еще не работает. Такое же поведение, как и второй блок кода.

var v1; 
 
var v2; 
 
var v3; 
 

 
function func1(src, action, arg) { 
 
    document.getElementById(arg).value = "check"; 
 
    document.getElementById(src.id).onclick = function() { 
 
    func2(); 
 
    }; 
 
    v1 = src; 
 
    v2 = action; 
 
    v3 = arg; 
 
} 
 

 
function func2() { 
 
    var private1 = v1; 
 
    var private2 = v2; 
 
    var private3 = v3; 
 

 
    document.getElementById(private3).value = "saved"; 
 
    document.getElementById(private1.id).onclick = function() { 
 
    func1(private1, private2, private3); 
 
    }; 
 
}
<input type=text id="t1"> 
 
<input type=button id="btn1" value="Go 1" onclick="func1(this,'edit','t1')"> 
 
<input type=text id="t2"> 
 
<input type=button id="btn2" value="Go 2" onclick="func1(this,'edit','t2')">

+0

* "... в func2() переключатели ошибка вокруг" * А? –

+0

Второй блок кода должен вести себя так же, как это делает первый блок кода. Но это не так. – Smithy

+2

Нет, не следует. Он должен вести себя так, как он себя ведет. –

ответ

1

Таким образом, кажется, что переменные v1, v2, v3 в замыкании до сих пор использует глобальные значения вне замыкания

Правильно, потому что ничто не затенение их , так вот как они в конечном итоге становятся разрешенными.

В своем первом блоке коды, в onclick обработчиках используют src, action и arg аргументов, передаваемые в вызове func1 и v1, v2 и v3 аргументов, передаваемых в вызове func2.

Во втором примере, поскольку вы удалили аргументы до func2, единственная причина, по которой эти идентификаторы находятся в области для функции onclick, которую она создает, - из-за глобальных переменных; если бы не глобальные, вы бы получили ReferenceError, потому что эти идентификаторы были бы неразрешимы. Таким образом, используются глобальные переменные.

Если вы передали аргументы func2 во втором примере с одинаковыми именами (не очень хорошая идея, но только для объяснения), то идентификаторы, используемые закрытием , которые он создает, будут разрешать вместо этих аргументов против глобальных.

Это может быть полезно, чтобы объяснить, что делает эта строка:

document.getElementById(v1.id).onclick = function() { func1(v1,v2,v3); }; 

, что строка создает функцию, которая является закрытие по контексту, в котором он был создан, вызов func2 (и этот контекст относится к контексту, в котором он был создан, и так далее до глобального контекста). Закрытие onclick не содержит копий переменных, которые были в области видимости, когда он был создан, он имеет прочную ссылку. Поэтому, когда функция запускается, это значение переменных в этот момент, который используется. В первом примере эти значения никогда не меняются, потому что они являются значениями аргументов, переданных func2 во время вызова, который создал закрытие, и ничто никогда не меняет их. Однако во втором примере эти значения различаются, потому что вы используете глобальные переменные, а не аргументы, переданные в func2.

Поскольку закрытие имеет ссылку на контекст, в котором он был создан, контекст каждый вызов до func2 сохраняется закрытием, созданным в рамках этого вызова. Поэтому, если блокировки, созданные несколькими вызовами, сохраняются, для вызовов func2 сохраняется множество контекстов, поэтому сохраняется множество копий аргументов (каждый из них используется закрытием, созданным для этого контекста). Более простой пример может помочь:

// A global variable 
 
var global = "g"; 
 

 
// A function that creates and returns a closure 
 
function foo(arg) { 
 
    return function() { 
 
     snippet.log("global = " + global +) ", arg = " + arg); 
 
    }; 
 
} 
 

 
// Create a closure over arg = 42 
 
var f1 = foo(42); 
 
f1(); // global = g, arg = 42 
 

 
// Change global 
 
global = "g+"; 
 
f1(); // global = g+, arg = 42 
 

 
// Create a second closure over a second arg, 67 
 
var f2 = foo(67); 
 
f2(); // global = g+, arg = 67 
 

 
// Change global again 
 
global = "g++"; 
 
f1(); // global = g++, arg = 42 
 
f2(); // global = g++, arg = 67
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> 
 
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Обратите внимание, как каждая крышка имеет свою собственную копию arg, но они оба разделяют global.

Этот фрагмент кода показывает, что замыкание имеет является ссылкой на arg, не скопировать его значение:

function foo(arg) { 
 
    return { 
 
    showArg: function() { 
 
     console.log("arg = " + arg); 
 
    }, 
 
    incrementArg: function() { 
 
     ++arg; 
 
    } 
 
    }; 
 
} 
 
var o1 = foo(42); 
 
o1.showArg(); // arg = 42 
 
o1.incrementArg(); 
 
o1.showArg(); // arg = 43
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> 
 
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Вы можете найти эту статью на моем анемией маленький блог полезный: Closures are not complicated


Вот фрагмент, демонстрирующий захват тогдашних глобалов в закрытии, вызывающий func2, но мы в итоге просто дублируем то, что у нас уже есть, src, action и arg, поэтому нет смысла.

var v1; 
 
var v2; 
 
var v3; 
 

 
function func1(src, action, arg) { 
 
    var p1 = src, p2 = action, p3 = arg; // Just duplicates what we already have 
 
    document.getElementById(arg).value = "check"; 
 
    document.getElementById(src.id).onclick = function() { 
 
    func2(p1, p2, p3); // We could just use src, action, and arg here like your first example 
 
    }; 
 
    v1 = src; 
 
    v2 = action; 
 
    v3 = arg; 
 
} 
 

 
function func2(v1, v2, v3) { 
 
    document.getElementById(v3).value = "saved"; 
 
    document.getElementById(v1.id).onclick = function() { 
 
    func1(v1, v2, v3); 
 
    }; 
 
}
<input type=text id="t1"> 
 
<input type=button id="btn1" value="Go 1" onclick="func1(this,'edit','t1')"> 
 
<input type=text id="t2"> 
 
<input type=button id="btn2" value="Go 2" onclick="func1(this,'edit','t2')">

+0

Исправьте меня, если я ошибаюсь. «Постоянная ссылка» означает, что память, используемая переменной, не очищается, когда переменная используется закрытием? Есть ли способ получить личную копию глобального? Я попытался установить новые переменные внутри func2() как «private1 = v1» и использовать «private1» в закрытии, но это не работает. – Smithy

+0

@Smithy: память, занятая переменной, не выпущена (это то, что вы имели в виду, когда она покраснела?), Пока ничего не ссылается на нее. Переменная существует в объекте, называемом объектом привязки переменных *, который является частью контекста * выполнения вызова 'func2'. Закрытия, созданные в контексте, получают ссылку на этот объект. Пока существует ограничение, этот объект существует, а переменные/arguments/etc. внутри него есть. –

+0

@Smithy: * «Есть ли способ получить личную копию глобального? Я пытался установить новые переменные внутри func2() как« private1 = v1 »и использовать« private1 »в закрытии, но это не работает. «Это действительно работает, это обычная техника. Возможно, вы захотите снова попробовать этот эксперимент, возможно, где-то там где-то там. –

Смежные вопросы