Каждый раз, когда Вы видите функциональное ключевое слово в другой функции, внутренняя функция имеет доступ к переменным во внешней функции.
function foo(x) {
var tmp = 3;
function bar(y) {
console.log(x + y + (++tmp)); // will log 16
}
bar(10);
}
foo(2);
Это будет всегда регистрироваться 16, потому что bar
может получить доступ x
, который был определен как аргумент foo
, и он может также получить доступ tmp
от foo
.
, Что закрытие. Функция не имеет к [1 138] возврат для вызова закрытия. Просто получающие доступ переменные за пределами Вашего непосредственного лексического контекста создает закрытие .
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + (++tmp)); // will also log 16
}
}
var bar = foo(2); // bar is now a closure.
bar(10);
вышеупомянутая функция также зарегистрируется 16, потому что bar
может все еще относиться к [1 110] и tmp
, даже при том, что это больше не непосредственно в объеме.
Однако с тех пор tmp
все еще бродит вокруг внутренний bar
закрытие, оно также увеличивается. Это будет увеличено каждый раз, когда Вы звоните bar
.
самый простой пример закрытия - это:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
, Когда функция JavaScript вызывается, новый контекст выполнения создается. Вместе с аргументами функции и родительским объектом, этот контекст выполнения также получает все переменные, объявленные за пределами него (в вышеупомянутом примере, и и 'b').
возможно создать больше чем одну функцию закрытия, или путем возврата списка их или путем установки их на глобальные переменные. Все они будут относиться к тот же x
и тот же tmp
, они не делают свои собственные копии.
Здесь номер x
является литеральным числом. Как с другими литералами в JavaScript, когда foo
назван, номер x
, скопировал в [1 120] как его аргумент x
.
, С другой стороны, JavaScript всегда использует ссылки при контакте с объектами. Если говорят, Вы звонили foo
с объектом, закрытие, которое он возвращает, будет ссылка тот исходный объект!
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
console.log(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);
Как ожидалось, каждый вызов к [1 123] увеличит x.memb
. То, что не могло бы ожидаться, то, что x
просто называет тот же объект age
переменная! После того, как несколько вызовов к [1 127], age.memb
будут 2! Эта ссылка является основанием для утечек памяти с объектами HTML.
Нет, это не так. возможно, что Scala будет работать как Java-эквивалентный байт-код, если этот байт-код не поддерживает обобщенные универсальные шаблоны.
Когда вы спрашиваете «что нужно изменить?» , ответ будет: спецификация байт-кода . В настоящее время байт-код не позволяет определять параметризованный тип переменной. Было решено, что, поскольку модификация байт-кода для поддержки реифицируемых дженериков нарушит обратную совместимость , дженерики должны быть реализованы посредством стирания типа .
Чтобы получить вокруг этого, Scala использовала мощь своего неявного
механизма для определения манифеста
, который можно импортировать в любой области видимости для обнаружения информации о типах во время выполнения. Манифесты являются экспериментальными и в основном недокументированными, но они входят в состав библиотеки в версии 2.8 . Вот еще один хороший ресурс на обобщенных дженериках / манифестах Scala
В дополнение к oxbow_lakes, в Stack Overflow есть вопрос о , как обойти стирание типа в Scala .
«неявный манифест» - это трюк компилятора Scala, который не делает обобщения в Scala обобщенными. Компилятор Scala, когда он видит функцию с параметром «неявный m: Manifest [A]» и , он знает общий тип A на сайте вызова, он оборачивает класс A и его общий тип параметры в манифест и сделать его доступным внутри функции. Однако, если он не может определить истинный тип A, то у него нет возможности создать манифест. Другими словами, Manifest должен быть передан по цепочке вызова функции, если это необходимо внутренней функции.
scala> def typeName[A](a: A)(implicit m: reflect.Manifest[A]) = m.toString
typeName: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String
scala> typeName(List(1))
res6: java.lang.String = scala.collection.immutable.List[int]
scala> def foo[A](a: A) = typeName(a)
<console>:5: error: could not find implicit value for parameter m:scala.reflect.Manifest[A].
def foo[A](a: A) = typeName(a)
^
scala> def foo[A](a: A)(implicit m: reflect.Manifest[A]) = typeName(a)
foo: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String
scala> foo(Set("hello"))
res8: java.lang.String = scala.collection.immutable.Set[java.lang.String]