- 浏览: 807599 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (360)
- Java (101)
- JPA/Hibernate (10)
- Spring (14)
- Flex/BlazeDS (37)
- Database (30)
- Lucene/Solr/Nutch (0)
- Maven/Ant (25)
- CXF/WebService (3)
- RPC/RMI/SOAP/WSDL (1)
- REST (6)
- TDD/BDD/JUnit (1)
- Servlet/JSP (2)
- AI/MachineLearning (3)
- Resource (1)
- 字符编码 (2)
- OOA/OOPS/UML (5)
- DesignPattern (8)
- 算法与数据结构 (11)
- Web&App Server (13)
- 并发&异步&无阻塞 (7)
- Entertainment (4)
- JavaScript/ExtJS (45)
- CodeStyle&Quality (1)
- svn/git/perforce (8)
- JSON (2)
- JavaScriptTesting (4)
- Others (6)
- RegularExpression (2)
- Linux/Windows (12)
- Protocal (2)
- Celebrities (1)
- Interview (1)
- 计算机语言 (1)
- English (2)
- Eclipse (5)
- TimeZone/时区 (1)
- Finance (1)
- 信息安全 (1)
- JMS/MQ (2)
- XSD/XML/DTD (3)
- Android (4)
- 投资 (3)
- Distribution (3)
- Excel (1)
最新评论
-
qdujunjie:
如果把m换成具体的数字,比如4或者5,会让读者更明白
m阶B树中“阶”的含义 -
java-admin:
不错,加油,多写点文章
关于Extjs的mixins和plugin -
xiehuaidong880827:
你好,我用sencha cmd打包完本地工程后,把app.js ...
ExtJS使用Sencha Cmd合并javascript文件为一个文件 -
KIWIFLY:
lwpan 写道inverse = "true&qu ...
Hibernate中什么时候使用inverse=true -
luedipiaofeng:
good
消除IE stop running this script弹出框
参考文章:
http://javascript.info/tutorial/initialization
http://javascript.info/tutorial/closures
http://javascriptissexy.com/understand-javascript-closures-with-ease/
http://www.w3schools.com/js/js_function_closures.asp
Initialization of functions and variables
Index
Instantiation of top-level variables
Function variables
Blocks do not have scope
Mechanics of variables and functions in JavaScript is completely different from most other languages.
Advanced topics become easy to grasp once you know how it works.
In JavaScript, all local variables and functions are properties of the special internal object, called LexicalEnvironment.
The top-level LexicalEnvironment in browser is window. It is also called a global object.
Instantiation of top-level variables
When the script is going to be executed, there is a pre-processing stage called variables instantiation.
1. First, the interpreter scans the code for <a href="/tutorial/functions-declarations-and-expressions">Function Declarations</a>, which are declared as function name {...} in the main code flow.
It takes every declaration, creates the function from it and puts it into window.
For example, consider the code:
var a = 5
function f(arg) { alert('f:'+arg) }
var g = function(arg) { alert('g:'+arg) }
At this stage, the browser finds function f, creates the function and stores it as window.f:
// 1. Function Declarations are initialized before the code is executed.
// so, prior to first line we have: window = { f: function }
var a = 5
function f(arg) { alert('f:'+arg) } // <-- FunctionDeclaration
var g = function(arg) { alert('g:'+arg) }
As a side effect, f can be called before it is declared:
f()
function f() { alert('ok') }
2. Second, the interpreter scans for var declarations and creates window properties. Assignments are not executed at this stage. All variables start as undefined.
// 1. Function declarations are initialized before the code is executed.
// window = { f: function }
// 2. Variables are added as window properties.
// window = { f: function, a: undefined, g: undefined }
var a = 5 // <-- var
function f(arg) { alert('f:'+arg) }
var g = function(arg) { alert('g:'+arg) } // <-- var
The value of g is a function expression, but the interpreter doesn’t care. It creates variables, but doesn’t assign them.
So to sum:
1. FunctionDeclarations become ready-to-use functions. That allows to call a function before it’s declaration.
2. Variables start as undefined.
3. All assignments happen later, when the execution reaches them.
As a side effect, it is impossible to have a variable and a function with the same name.
3. Then the code starts running.
When a variable or function is accessed, the interpreter gets it from window:
alert("a" in window) // true, because window.a exists
alert(a) // undefined, because assignment happens below
alert(f) // function, because it is Function Declaration
alert(g) // undefined, because assignment happens below
var a = 5
function f() { /*...*/ }
var g = function() { /*...*/ }
4. After the assignments, a becomes 5 and g becomes a function. In the code below, alerts are moved below. Note the difference:
var a = 5
var g = function() { /*...*/ }
alert(a) // 5
alert(g) // function
If a variable is not declared with var, then, of course, it doesn’t get created at initialization stage. The interpreter won’t see it:
alert("b" in window) // false, there is no window.b
alert(b) // error, b is not defined
b = 5
But after the assignment, b becomes the regular variable window.b as if it were declared:
b = 5
alert("b" in window) // true, there is window.b = 5
What will be the result?
if ("a" in window) {
var a = 1
}
alert(a)
Solution
The answer is 1.
Let’s trace the code to see why.
1. At initialization stage, window.a is created:
// window = {a:undefined}
if ("a" in window) {
var a = 1
}
alert(a)
2. "a" in window is true.
// window = {a:undefined}
if (true) {
var a = 1
}
alert(a)
So, if is executed and hence value of a becomes 1.
What will be the result (no var before a)?
if ("a" in window) {
a = 1
}
alert(a)
Solution
The answer is “Error: no such variable”, because there is no variable a at the time of"a" in window check.
So, the if branch does not execute and there is no a at the time of alert.
Function variables
When the function runs, on every function call, the new LexicalEnvironment is created and populated with arguments, variables and nested function declarations.
This object is used internally to read/write variables. Unlike window, the LexicalEnvironment of a function is not available for direct access.
Let’s consider the details of execution for the following function:
function sayHi(name) {
var phrase = "Hi, " + name
alert(phrase)
}
sayHi('John')
1. When the interpreter is preparing to start function code execution, before the first line is run, an empty LexicalEnvironment is created and populated with arguments, local variables and nested functions.
function sayHi(name) {
// LexicalEnvironment = { name: 'John', phrase: undefined }
var phrase = "Hi, " + name
alert(phrase)
}
sayHi('John')
Naturally, arguments have the starting value, but the local variables don’t.
2. Then the function code runs, eventually assignments are executed.
A variable assignment internally means that the corresponding property of theLexicalEnvironment gets a new value.
So, phrase = "Hi, "+name changes the LexicalEnvironment:
function sayHi(name) {
// LexicalEnvironment = { name: 'John', phrase: undefined }
var phrase = "Hi, " + name
// LexicalEnvironment = { name: 'John', phrase: 'Hi, John'}
alert(phrase)
}
sayHi('John')
The last line alert(phrase) searches the phrase in LexicalEnvironment and outputs it’s value.
3. At the end of execution, the LexicalEnvironment is usually junked with all its contents, because the variables are no longer needed. But (as we’ll see) it’s not always like that.
Specification peculiarities
If we look into the recent ECMA-262 specification, there are actually two objects.
The first is a VariableEnvironment object, which is actually populated by variables and functions, declared by FunctionDeclaration, and then becomes immutable.
The second is a LexicalEnvironment object, which is almost same asVariableEnvironment, but it is actually used during the execution.
A more formal description can be found in the ECMA-262 standard, sections 10.2-10.5 and 13.
It is also noted that in JavaScript implementations, these two objects can be merged into one. So, we evade irrelevant details and use the term LexicalEnvironmenteverywhere.
Blocks do not have scope
There is no difference between the following:
var i = 1
{
i = 5
}
…And the following
i = 1
{
var i = 5
}
All var declarations are processed before the execution in in both cases.
Unlike languages like Java, C etc, variables in JavaScript survive after a loop.
That’s again, because their scope is a function.
for(var i=0; i<5; i++) { }
alert(i) // 5, variable survives and keeps value
Declaring a variable in the loop is convenient, but doesn’t make the loop it’s scope.
What this test is going to alert? Why? (Don’t run it before you answer, please)
function test() {
alert(window)
var window = 5
}
test()
Solution
The var directive is processed on the pre-execution stage.
So, window becomes a local variable before it comes to alert:
LexicalEnvironment = {
window: undefined
}
So when the execution starts and reaches first line, variable window exists and is undefined.
How do you think, what will be the output? Why?
show clean source in new windowHide/show line numbersprint highlighted code
var value = 0
function f() {
if (1) {
value = 'yes'
} else {
var value = 'no'
}
alert(value)
}
f()
Solution
The var directive is processed and created as LexicalEnvironment property at pre-execution stage.
So, the line value='yes' performs an assignment to the local variable, and the last alert outputs 'yes'.
Closures 1
Closures
1. Access to outer variables
2. Nested functions
3. Closures
1. Mutability of LexicalEnvironment
2. The notorious closure loop
3. [[Scope]] for new Function
4. Summary
From the previous article, we know that a variable is a property of the LexicalEnvironment object.
Here we discuss access to outer variables and nested functions. In-depth understanding of closures follows automatically.
Access to outer variables
What if a variable is accessed, but it isn’t local? Like here:
var a = 5
function f() {
alert(a)
}
In this case, the the interpreter finds the variable in the outer LexicalEnvironment object.
The process consists of two steps:
1. First, ]when a function f is created, it is not created in an empty space.
There is a current LexicalEnvironment object. In the case above, it’s window (a is undefined at the time of function creation).
When a function is created, it gets a hidden property, named [[Scope]], which references currentLexicalEnvironment.
2. Later, when the function runs, it creates it’s own LexicalEnvironment and links it with [[Scope]].
So when a variable is not found in the local LexicalEnvironment, it is searched outside:
If a variable is read, but can not be found anywhere, the error is generated.
function f() {
alert(x) // reading x gives error, no x
}
Certain language constructs block the error, for example typeof x works if there is no x (and returnsundefined), but that’s an exception.
If a variable is set, but not found anywhere, then it is created in the outmost LexicalEnvironment, which iswindow.
function f() {
x = 5 // writing x puts it into window
}
Nested functions
Functions can be nested one inside another, forming a chain of LexicalEnvironments which can also be called a scope chain.
var a = 1
function f() {
function g() {
alert(a)
}
return g
}
var func = f()
func() // 1
LexicalEnvironments form a chain (from inside out):
// LexicalEnvironment = window = {a:1, f: function}
var a = 1
function f() {
// LexicalEnvironment = {g:function}
function g() {
// LexicalEnvironment = {}
alert(a)
}
return g
}
So, function g has access to g, a and f.
Create a function sum that will work like that: sum(a)(b) = a+b.
Yes, the syntax is dual brackets. Funny, isn’t it? For instance:
sum(1)(2) = 3
sum(5)(-1) = 4
Solution
The idea is that sum should return a function which knows a and will take care about next argument.
Can be done like this:
function sum(a) {
return function(b) { // takes a from outer LexicalEnvironment
return a+b
}
}
alert( sum(1)(2) )
alert( sum(5)(-1) )
Create a function sum that will work like that: sum(a)(b) = a+b and accepts any number of brackets.
For instance:
sum(1)(2) == 3
sum(5)(-1)(2) == 6
sum(6)(-1)(-2)(-3) == 0
sum(0)(1)(2)(3)(4)(5) == 15
Hint 1
Solution
To make sum(1) callable as sum(1)(2), it must return a function.
The function can be either called or converted to a number with valueOf.
The solution is really self-explanatory:
function sum(a) {
var sum = a
function f(b) {
sum += b
return f
}
f.toString = function() { return sum }
return f
}
alert( sum(1)(2) ) // 3
alert( sum(5)(-1)(2) ) // 6
alert( sum(6)(-1)(-2)(-3) ) // 0
alert( sum(0)(1)(2)(3)(4)(5) ) // 15
Closures
Nested function may continue to live after the outer function has finished:
function User(name) {
this.say = function(phrase) {
alert(name + ' says: ' + phrase)
}
}
var user = new User('John')
Marking up LexicalEnvironments:
Note, the this context is not related to scopes and variables. It does not participate here.
As we see, this.say is a property in the user object, so it continues to live after User completed.
And if you remember, when this.say is created, it (as every function) gets an internal reference this.say.[[Scope]] to current LexicalEnvironment. So, the LexicalEnvironment of the current User execution stays in memory. All variables of User also are it’s properties, so they are also carefully kept, not junked as usually.
The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.
• The inner function keeps a reference to the outer LexicalEnvironment.
• The inner function may access variables from it any time even if the outer function is finished.
• The browser keeps the LexicalEnvironment and all it’s properties(variables) in memory until there is an inner function which references it.
This is called a closure.
Mutability of LexicalEnvironment
Several function may share same outer LexicalEnvironment. In this case they can modify it’s properties.
In the example below, this.fixName changes name, which is used by this.say:
function User(name) {
this.fixName = function() {
name = 'Mr.' + name.toUpperCase()
}
this.say = function(phrase) {
alert(name + ' says: ' + phrase)
}
}
var user = new User('John')
// (1)
user.fixName()
// (2)
user.say("I'm alive!") // Mr.JOHN says: I'm alive!
Here user.fixName.[[Scope]] and user.say.[[Scope]] reference same LexicalEnvironment, which corresponds to new User run.
From (1) to (2), the LexicalEnvironment.name is updated, so both functions see the variable change.
Variables in outer LexicalEnvironment may change.
Inner functions always see the last value.
The notorious closure loop
The task below contains interesting tricks, best demonstrated by an example. Please, glance at the solution, or, much better, try to solve it.
Here is a function to create an army of shooters:
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) {
var shooter = function() { // a shooter is a function
alert(i) // which should alert it's number
}
shooters.push(shooter)
}
return shooters
}
var army = makeArmy()
var shooter = army[0] // first shooter
shooter() // alerts 10, should be 0
shooter = army[5] // 5th shooter
shooter() // alerts 10, should be 5
// all shooters alert same: 10 instead of 1,2,3...10.
Why all shooters alert the same? How to make each shooter output it’s number?
Solution
Note that the shooter function. Does not have a variable named i.
So, when it is called, the interpreter takes i from the outer LexicalEnvironment.
The problem is that at the time when shooters are run, function makeArmy has already finished the execution.
The loop has finished and the variable i is now 10.
There are two possible solutions.
The first one is to put the correct value into the shooting function itself.
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) {
var shooter = function() {
alert( arguments.callee.i )
}
shooter.i = i
shooters.push(shooter)
}
return shooters
}
var army = makeArmy()
army[0]() // 0
army[1]() // 1
Another, more advanced solution is to use an extra function to trap the current value of i:
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) {
var shooter = function(i) {
return function() {
alert( i )
}
}(i)
shooters.push(shooter)
}
return shooters
}
var army = makeArmy()
army[0]() // 0
army[1]() // 1
Let’s consider the highlighted fragment more thoroughly.
var shooter = function(i) {
return function() {
alert( i )
}
}(i)
Here, the actual shooting function is created as the result of an anonymous function(i)which is created and executed in one place.
So, when it comes to executing alert(i), it will be taken from LexicalEnvironment of the anonymous function.
So, the anonymous function traps current i into it’s LexicalEnvironment and allows the shooter to access it.
The last way is to wrap the whole loop into temporary function. Sometimes that’s more readable:
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) (function(i) {
var shooter = function() {
alert( i )
}
shooters.push(shooter)
})(i)
return shooters
}
var army = makeArmy()
army[0]() // 0
army[1]() // 1
The (function(i) { ... }) definition is wrapped into brackets to make sure the interpreter treats that as expression.
[[Scope]] for new Function
There is an exception to general scope binding rule. When you create a function using new Function, it’s[[Scope]] points to window, not to current LexicalEnvironment.
The following example demonstrates how a function, created with new Function ignores local variable aand outputs the global variable.
The regular behavior:
window.a = 1;
function getFunc() {
var a = 2;
var func = function() { alert(a) }
return func;
}
getFunc()() // 2, from LexicalEnvironemnt of getFunc
And now the function, created by new Function:
window.a = 1
function getFunc() {
var a = 2
var func = new Function('', 'alert(a)')
return func
}
getFunc()() // 1, from window
Summary
We discussed the following topics:
• How variables are handled in JavaScript.
• How scopes work.
• What is a closure and how to use it.
• Possible pitfalls and subtles in working with closures.
Closures in JavaScript is like a salt. You can live without it, but not very long. Usually people put it everywhere…
Closures 2
What is a closure?
A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.
The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s arguments object, however, even though it can call the outer function’s parameters directly.
You create a closure by adding a function inside another function.
A Basic Example of Closures in JavaScript:
function showName (firstName, lastName) {
var nameIntro = "Your name is ";
// this inner function has access to the outer function's variables, including the parameter
function makeFullName () {
return nameIntro + firstName + " " + lastName;
}
return makeFullName ();
}
showName ("Michael", "Jackson"); // Your name is Michael Jackson
Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.
A Classic jQuery Example of Closures:
$(function() {
var selections = [];
$(".niners").click(function() { // this closure has access to the selections variable
selections.push (this.prop("name")); // update the selections variable in the outer function's scope
});
});
Closures’ Rules and Side Effects
Closures have access to the outer function’s variable even after the outer function returns:
One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:
function celebrityName (firstName) {
var nameIntro = "This celebrity is ";
// this inner function has access to the outer function's variables, including the parameter
function lastName (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}
var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.
// The closure (lastName) is called here after the outer function has returned above
// Yet, the closure still has access to the outer function's variables and parameter
mjName ("Jackson"); // This celebrity is Michael Jackson
Closures store references to the outer function’s variables;
they do not store the actual value. Closures get more interesting when the value of the outer function’s variable changes before the closure is called. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:
function celebrityID () {
var celebrityID = 999;
// We are returning an object with some inner functions
// All the inner functions have access to the outer function's variables
return {
getID: function () {
// This inner function will return the UPDATED celebrityID variable
// It will return the current value of celebrityID, even after the changeTheID function changes it
return celebrityID;
},
setID: function (theNewID) {
// This inner function will change the outer function's variable anytime
celebrityID = theNewID;
}
}
}
var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.
mjID.getID(); // 999
mjID.setID(567); // Changes the outer function's variable
mjID.getID(); // 567: It returns the updated celebrityId variable
Closures Gone Awry
Because closures have access to the updated values of the outer function’s variables, they can also lead to bugs when the outer function’s variable changes with a for loop. Thus:
// This example is explained in detail below (just after this code box).
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id()); // 103
In the preceding example, by the time the anonymous functions are called, the value of i is 3 (the length of the array and then it increments). The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID. So every position in the returned array get id = 103, instead of the intended 100, 101, 102.
The reason this happened was because, as we have discussed in the previous example, the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value. So just as the previous example showed that we can access the updated variable with the closure, this example similarly accessed the i variable when it was changed, since the outer function runs the entire for loop and returns the last value of i, which is 103.
To fix this side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE), such as the following:
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE
return function () {
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
} () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
} (i); // immediately invoke the function passing the i variable as a parameter
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs [1]; console.log(cruiseID.id); // 101
http://javascript.info/tutorial/initialization
http://javascript.info/tutorial/closures
http://javascriptissexy.com/understand-javascript-closures-with-ease/
http://www.w3schools.com/js/js_function_closures.asp
Initialization of functions and variables
Index
Instantiation of top-level variables
Function variables
Blocks do not have scope
Mechanics of variables and functions in JavaScript is completely different from most other languages.
Advanced topics become easy to grasp once you know how it works.
In JavaScript, all local variables and functions are properties of the special internal object, called LexicalEnvironment.
The top-level LexicalEnvironment in browser is window. It is also called a global object.
Instantiation of top-level variables
When the script is going to be executed, there is a pre-processing stage called variables instantiation.
1. First, the interpreter scans the code for <a href="/tutorial/functions-declarations-and-expressions">Function Declarations</a>, which are declared as function name {...} in the main code flow.
It takes every declaration, creates the function from it and puts it into window.
For example, consider the code:
var a = 5
function f(arg) { alert('f:'+arg) }
var g = function(arg) { alert('g:'+arg) }
At this stage, the browser finds function f, creates the function and stores it as window.f:
// 1. Function Declarations are initialized before the code is executed.
// so, prior to first line we have: window = { f: function }
var a = 5
function f(arg) { alert('f:'+arg) } // <-- FunctionDeclaration
var g = function(arg) { alert('g:'+arg) }
As a side effect, f can be called before it is declared:
f()
function f() { alert('ok') }
2. Second, the interpreter scans for var declarations and creates window properties. Assignments are not executed at this stage. All variables start as undefined.
// 1. Function declarations are initialized before the code is executed.
// window = { f: function }
// 2. Variables are added as window properties.
// window = { f: function, a: undefined, g: undefined }
var a = 5 // <-- var
function f(arg) { alert('f:'+arg) }
var g = function(arg) { alert('g:'+arg) } // <-- var
The value of g is a function expression, but the interpreter doesn’t care. It creates variables, but doesn’t assign them.
So to sum:
1. FunctionDeclarations become ready-to-use functions. That allows to call a function before it’s declaration.
2. Variables start as undefined.
3. All assignments happen later, when the execution reaches them.
As a side effect, it is impossible to have a variable and a function with the same name.
3. Then the code starts running.
When a variable or function is accessed, the interpreter gets it from window:
alert("a" in window) // true, because window.a exists
alert(a) // undefined, because assignment happens below
alert(f) // function, because it is Function Declaration
alert(g) // undefined, because assignment happens below
var a = 5
function f() { /*...*/ }
var g = function() { /*...*/ }
4. After the assignments, a becomes 5 and g becomes a function. In the code below, alerts are moved below. Note the difference:
var a = 5
var g = function() { /*...*/ }
alert(a) // 5
alert(g) // function
If a variable is not declared with var, then, of course, it doesn’t get created at initialization stage. The interpreter won’t see it:
alert("b" in window) // false, there is no window.b
alert(b) // error, b is not defined
b = 5
But after the assignment, b becomes the regular variable window.b as if it were declared:
b = 5
alert("b" in window) // true, there is window.b = 5
What will be the result?
if ("a" in window) {
var a = 1
}
alert(a)
Solution
The answer is 1.
Let’s trace the code to see why.
1. At initialization stage, window.a is created:
// window = {a:undefined}
if ("a" in window) {
var a = 1
}
alert(a)
2. "a" in window is true.
// window = {a:undefined}
if (true) {
var a = 1
}
alert(a)
So, if is executed and hence value of a becomes 1.
What will be the result (no var before a)?
if ("a" in window) {
a = 1
}
alert(a)
Solution
The answer is “Error: no such variable”, because there is no variable a at the time of"a" in window check.
So, the if branch does not execute and there is no a at the time of alert.
Function variables
When the function runs, on every function call, the new LexicalEnvironment is created and populated with arguments, variables and nested function declarations.
This object is used internally to read/write variables. Unlike window, the LexicalEnvironment of a function is not available for direct access.
Let’s consider the details of execution for the following function:
function sayHi(name) {
var phrase = "Hi, " + name
alert(phrase)
}
sayHi('John')
1. When the interpreter is preparing to start function code execution, before the first line is run, an empty LexicalEnvironment is created and populated with arguments, local variables and nested functions.
function sayHi(name) {
// LexicalEnvironment = { name: 'John', phrase: undefined }
var phrase = "Hi, " + name
alert(phrase)
}
sayHi('John')
Naturally, arguments have the starting value, but the local variables don’t.
2. Then the function code runs, eventually assignments are executed.
A variable assignment internally means that the corresponding property of theLexicalEnvironment gets a new value.
So, phrase = "Hi, "+name changes the LexicalEnvironment:
function sayHi(name) {
// LexicalEnvironment = { name: 'John', phrase: undefined }
var phrase = "Hi, " + name
// LexicalEnvironment = { name: 'John', phrase: 'Hi, John'}
alert(phrase)
}
sayHi('John')
The last line alert(phrase) searches the phrase in LexicalEnvironment and outputs it’s value.
3. At the end of execution, the LexicalEnvironment is usually junked with all its contents, because the variables are no longer needed. But (as we’ll see) it’s not always like that.
Specification peculiarities
If we look into the recent ECMA-262 specification, there are actually two objects.
The first is a VariableEnvironment object, which is actually populated by variables and functions, declared by FunctionDeclaration, and then becomes immutable.
The second is a LexicalEnvironment object, which is almost same asVariableEnvironment, but it is actually used during the execution.
A more formal description can be found in the ECMA-262 standard, sections 10.2-10.5 and 13.
It is also noted that in JavaScript implementations, these two objects can be merged into one. So, we evade irrelevant details and use the term LexicalEnvironmenteverywhere.
Blocks do not have scope
There is no difference between the following:
var i = 1
{
i = 5
}
…And the following
i = 1
{
var i = 5
}
All var declarations are processed before the execution in in both cases.
Unlike languages like Java, C etc, variables in JavaScript survive after a loop.
That’s again, because their scope is a function.
for(var i=0; i<5; i++) { }
alert(i) // 5, variable survives and keeps value
Declaring a variable in the loop is convenient, but doesn’t make the loop it’s scope.
What this test is going to alert? Why? (Don’t run it before you answer, please)
function test() {
alert(window)
var window = 5
}
test()
Solution
The var directive is processed on the pre-execution stage.
So, window becomes a local variable before it comes to alert:
LexicalEnvironment = {
window: undefined
}
So when the execution starts and reaches first line, variable window exists and is undefined.
How do you think, what will be the output? Why?
show clean source in new windowHide/show line numbersprint highlighted code
var value = 0
function f() {
if (1) {
value = 'yes'
} else {
var value = 'no'
}
alert(value)
}
f()
Solution
The var directive is processed and created as LexicalEnvironment property at pre-execution stage.
So, the line value='yes' performs an assignment to the local variable, and the last alert outputs 'yes'.
Closures 1
Closures
1. Access to outer variables
2. Nested functions
3. Closures
1. Mutability of LexicalEnvironment
2. The notorious closure loop
3. [[Scope]] for new Function
4. Summary
From the previous article, we know that a variable is a property of the LexicalEnvironment object.
Here we discuss access to outer variables and nested functions. In-depth understanding of closures follows automatically.
Access to outer variables
What if a variable is accessed, but it isn’t local? Like here:
var a = 5
function f() {
alert(a)
}
In this case, the the interpreter finds the variable in the outer LexicalEnvironment object.
The process consists of two steps:
1. First, ]when a function f is created, it is not created in an empty space.
There is a current LexicalEnvironment object. In the case above, it’s window (a is undefined at the time of function creation).
When a function is created, it gets a hidden property, named [[Scope]], which references currentLexicalEnvironment.
2. Later, when the function runs, it creates it’s own LexicalEnvironment and links it with [[Scope]].
So when a variable is not found in the local LexicalEnvironment, it is searched outside:
If a variable is read, but can not be found anywhere, the error is generated.
function f() {
alert(x) // reading x gives error, no x
}
Certain language constructs block the error, for example typeof x works if there is no x (and returnsundefined), but that’s an exception.
If a variable is set, but not found anywhere, then it is created in the outmost LexicalEnvironment, which iswindow.
function f() {
x = 5 // writing x puts it into window
}
Nested functions
Functions can be nested one inside another, forming a chain of LexicalEnvironments which can also be called a scope chain.
var a = 1
function f() {
function g() {
alert(a)
}
return g
}
var func = f()
func() // 1
LexicalEnvironments form a chain (from inside out):
// LexicalEnvironment = window = {a:1, f: function}
var a = 1
function f() {
// LexicalEnvironment = {g:function}
function g() {
// LexicalEnvironment = {}
alert(a)
}
return g
}
So, function g has access to g, a and f.
Create a function sum that will work like that: sum(a)(b) = a+b.
Yes, the syntax is dual brackets. Funny, isn’t it? For instance:
sum(1)(2) = 3
sum(5)(-1) = 4
Solution
The idea is that sum should return a function which knows a and will take care about next argument.
Can be done like this:
function sum(a) {
return function(b) { // takes a from outer LexicalEnvironment
return a+b
}
}
alert( sum(1)(2) )
alert( sum(5)(-1) )
Create a function sum that will work like that: sum(a)(b) = a+b and accepts any number of brackets.
For instance:
sum(1)(2) == 3
sum(5)(-1)(2) == 6
sum(6)(-1)(-2)(-3) == 0
sum(0)(1)(2)(3)(4)(5) == 15
Hint 1
Solution
To make sum(1) callable as sum(1)(2), it must return a function.
The function can be either called or converted to a number with valueOf.
The solution is really self-explanatory:
function sum(a) {
var sum = a
function f(b) {
sum += b
return f
}
f.toString = function() { return sum }
return f
}
alert( sum(1)(2) ) // 3
alert( sum(5)(-1)(2) ) // 6
alert( sum(6)(-1)(-2)(-3) ) // 0
alert( sum(0)(1)(2)(3)(4)(5) ) // 15
Closures
Nested function may continue to live after the outer function has finished:
function User(name) {
this.say = function(phrase) {
alert(name + ' says: ' + phrase)
}
}
var user = new User('John')
Marking up LexicalEnvironments:
Note, the this context is not related to scopes and variables. It does not participate here.
As we see, this.say is a property in the user object, so it continues to live after User completed.
And if you remember, when this.say is created, it (as every function) gets an internal reference this.say.[[Scope]] to current LexicalEnvironment. So, the LexicalEnvironment of the current User execution stays in memory. All variables of User also are it’s properties, so they are also carefully kept, not junked as usually.
The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.
• The inner function keeps a reference to the outer LexicalEnvironment.
• The inner function may access variables from it any time even if the outer function is finished.
• The browser keeps the LexicalEnvironment and all it’s properties(variables) in memory until there is an inner function which references it.
This is called a closure.
Mutability of LexicalEnvironment
Several function may share same outer LexicalEnvironment. In this case they can modify it’s properties.
In the example below, this.fixName changes name, which is used by this.say:
function User(name) {
this.fixName = function() {
name = 'Mr.' + name.toUpperCase()
}
this.say = function(phrase) {
alert(name + ' says: ' + phrase)
}
}
var user = new User('John')
// (1)
user.fixName()
// (2)
user.say("I'm alive!") // Mr.JOHN says: I'm alive!
Here user.fixName.[[Scope]] and user.say.[[Scope]] reference same LexicalEnvironment, which corresponds to new User run.
From (1) to (2), the LexicalEnvironment.name is updated, so both functions see the variable change.
Variables in outer LexicalEnvironment may change.
Inner functions always see the last value.
The notorious closure loop
The task below contains interesting tricks, best demonstrated by an example. Please, glance at the solution, or, much better, try to solve it.
Here is a function to create an army of shooters:
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) {
var shooter = function() { // a shooter is a function
alert(i) // which should alert it's number
}
shooters.push(shooter)
}
return shooters
}
var army = makeArmy()
var shooter = army[0] // first shooter
shooter() // alerts 10, should be 0
shooter = army[5] // 5th shooter
shooter() // alerts 10, should be 5
// all shooters alert same: 10 instead of 1,2,3...10.
Why all shooters alert the same? How to make each shooter output it’s number?
Solution
Note that the shooter function. Does not have a variable named i.
So, when it is called, the interpreter takes i from the outer LexicalEnvironment.
The problem is that at the time when shooters are run, function makeArmy has already finished the execution.
The loop has finished and the variable i is now 10.
There are two possible solutions.
The first one is to put the correct value into the shooting function itself.
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) {
var shooter = function() {
alert( arguments.callee.i )
}
shooter.i = i
shooters.push(shooter)
}
return shooters
}
var army = makeArmy()
army[0]() // 0
army[1]() // 1
Another, more advanced solution is to use an extra function to trap the current value of i:
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) {
var shooter = function(i) {
return function() {
alert( i )
}
}(i)
shooters.push(shooter)
}
return shooters
}
var army = makeArmy()
army[0]() // 0
army[1]() // 1
Let’s consider the highlighted fragment more thoroughly.
var shooter = function(i) {
return function() {
alert( i )
}
}(i)
Here, the actual shooting function is created as the result of an anonymous function(i)which is created and executed in one place.
So, when it comes to executing alert(i), it will be taken from LexicalEnvironment of the anonymous function.
So, the anonymous function traps current i into it’s LexicalEnvironment and allows the shooter to access it.
The last way is to wrap the whole loop into temporary function. Sometimes that’s more readable:
function makeArmy() {
var shooters = []
for(var i=0; i<10; i++) (function(i) {
var shooter = function() {
alert( i )
}
shooters.push(shooter)
})(i)
return shooters
}
var army = makeArmy()
army[0]() // 0
army[1]() // 1
The (function(i) { ... }) definition is wrapped into brackets to make sure the interpreter treats that as expression.
[[Scope]] for new Function
There is an exception to general scope binding rule. When you create a function using new Function, it’s[[Scope]] points to window, not to current LexicalEnvironment.
The following example demonstrates how a function, created with new Function ignores local variable aand outputs the global variable.
The regular behavior:
window.a = 1;
function getFunc() {
var a = 2;
var func = function() { alert(a) }
return func;
}
getFunc()() // 2, from LexicalEnvironemnt of getFunc
And now the function, created by new Function:
window.a = 1
function getFunc() {
var a = 2
var func = new Function('', 'alert(a)')
return func
}
getFunc()() // 1, from window
Summary
We discussed the following topics:
• How variables are handled in JavaScript.
• How scopes work.
• What is a closure and how to use it.
• Possible pitfalls and subtles in working with closures.
Closures in JavaScript is like a salt. You can live without it, but not very long. Usually people put it everywhere…
Closures 2
What is a closure?
A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.
The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s arguments object, however, even though it can call the outer function’s parameters directly.
You create a closure by adding a function inside another function.
A Basic Example of Closures in JavaScript:
function showName (firstName, lastName) {
var nameIntro = "Your name is ";
// this inner function has access to the outer function's variables, including the parameter
function makeFullName () {
return nameIntro + firstName + " " + lastName;
}
return makeFullName ();
}
showName ("Michael", "Jackson"); // Your name is Michael Jackson
Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.
A Classic jQuery Example of Closures:
$(function() {
var selections = [];
$(".niners").click(function() { // this closure has access to the selections variable
selections.push (this.prop("name")); // update the selections variable in the outer function's scope
});
});
Closures’ Rules and Side Effects
Closures have access to the outer function’s variable even after the outer function returns:
One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:
function celebrityName (firstName) {
var nameIntro = "This celebrity is ";
// this inner function has access to the outer function's variables, including the parameter
function lastName (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}
var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.
// The closure (lastName) is called here after the outer function has returned above
// Yet, the closure still has access to the outer function's variables and parameter
mjName ("Jackson"); // This celebrity is Michael Jackson
Closures store references to the outer function’s variables;
they do not store the actual value. Closures get more interesting when the value of the outer function’s variable changes before the closure is called. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:
function celebrityID () {
var celebrityID = 999;
// We are returning an object with some inner functions
// All the inner functions have access to the outer function's variables
return {
getID: function () {
// This inner function will return the UPDATED celebrityID variable
// It will return the current value of celebrityID, even after the changeTheID function changes it
return celebrityID;
},
setID: function (theNewID) {
// This inner function will change the outer function's variable anytime
celebrityID = theNewID;
}
}
}
var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.
mjID.getID(); // 999
mjID.setID(567); // Changes the outer function's variable
mjID.getID(); // 567: It returns the updated celebrityId variable
Closures Gone Awry
Because closures have access to the updated values of the outer function’s variables, they can also lead to bugs when the outer function’s variable changes with a for loop. Thus:
// This example is explained in detail below (just after this code box).
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id()); // 103
In the preceding example, by the time the anonymous functions are called, the value of i is 3 (the length of the array and then it increments). The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID. So every position in the returned array get id = 103, instead of the intended 100, 101, 102.
The reason this happened was because, as we have discussed in the previous example, the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value. So just as the previous example showed that we can access the updated variable with the closure, this example similarly accessed the i variable when it was changed, since the outer function runs the entire for loop and returns the last value of i, which is 103.
To fix this side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE), such as the following:
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE
return function () {
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
} () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
} (i); // immediately invoke the function passing the i variable as a parameter
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs [1]; console.log(cruiseID.id); // 101
发表评论
-
Fiddler使用
2017-06-22 16:27 687Fiddler不能捕获chrome request 原因是,c ... -
Javascript跨域
2017-06-21 17:05 632在js中,我们直接用XMLHttpRequest请求不同域上 ... -
面向对象的JavaScript,ECMAScript6, ECMAScript2015
2017-02-11 21:11 566全面理解面向对象的 JavaScript http://www ... -
SASS用法指南
2016-03-03 14:18 707SASS用法指南 http://www.ruanyifeng. ... -
Angular JS与ExtJS比较
2016-01-04 13:54 1627ExtJS vs AngularJS http://www.t ... -
ExtJS POST请求客户端服务端案例
2015-11-10 15:29 1292客户端GUI端示例 var positionIDList = ... -
Javascript设计模式
2015-09-29 14:12 832书名:Learning JavaScript Design P ... -
JavaScript单例模式Singleton Pattern
2015-09-29 14:10 1373参考链接:http://www.dofactory.com/j ... -
Check scope of element in Angular JS
2015-06-16 15:36 772step 1) choose an element in de ... -
有意思的HTML5效果
2015-03-18 09:24 1254http://www.html5tricks.com/9-fu ... -
JavaScript内存溢出
2015-02-13 10:42 1106http://javascript.info/tutorial ... -
JavaScript电子相册
2015-02-05 09:36 1808http://www.webhek.com/misc/3d-a ... -
JavaScript学习笔记
2015-01-11 10:59 821Repeated and Omitted Declaratio ... -
JavaScript小技巧
2014-12-26 10:00 894关系javascript变量的内存 ... -
JavaScript TimeZone issue
2014-10-31 11:48 0Tue Jan 01 2008 23:45:00 GMT+03 ... -
转义字符处理,获取字符的ASCII码值
2014-10-24 13:58 2036Java 获取字符的ASCII码值 int asciiDec ... -
JavaScript技巧,最佳实践(Best Practice)
2014-10-20 10:03 68745个实用的JavaScript技巧、窍门和最佳实践 http ... -
如何跨域获取Cookie
2014-06-23 14:32 3141cookie可以跨子域访问,如果用户信息保存在qq.com的c ... -
JavaScript获取图片(Image)的大小(宽度,高度)
2014-05-13 17:46 1935如果只有图片的URL function getImageD ... -
JavaScript获取指定名字的样式规则
2014-05-13 17:39 1054function getCSSRule(ruleSelec ...
相关推荐
在Java中,你可以使用Lambda表达式或匿名内部类来创建闭包,而在JavaScript中,所有的函数都是闭包。在C#中,可以使用`Action`、`Func`等委托类型或`lambda`表达式来创建闭包。 总的来说,闭包在编程中扮演着至关...
在本Demo“Swift语言利用Closure闭包实现反向传值Demo”中,我们将深入探讨如何利用闭包在两个视图控制器之间实现数据的反向传递。 首先,理解闭包的基本概念。闭包本质上是一个函数,它可以访问并修改其外部作用域...
在这个"Swift语言采用Closure闭包实现反向传值Demo"中,我们将深入探讨如何使用闭包在两个视图控制器之间进行反向数据传递,以及它与Blocks的异同。 首先,让我们了解什么是闭包。闭包是能够捕获和存储其所在上下文...
在Swift中,Closure(闭包)是一种强大的功能,可以捕获和存储其所在上下文中的引用。本Demo通过闭包展示了在两个视图控制器之间如何实现反向传值,这与Objective-C(OC)中的Blocks概念有相似之处,但也有其独特之...
补充说明:闭包可以使用USE关键连接外部变量。 总结:PHP闭包的特性其实用CLASS就可以实现类似甚至强大得多的功能,更不能和js的闭包相提并论了吧,只能期待PHP以后对闭包支持的改进。不过匿名函数还是挺有用的,...
闭包(Closure)是一个可以包含自由变量的代码块,这个自由变量并不是在这个代码块或任何全局上下文中定义的,而是在定义代码块的环境中定义。简单来说,闭包就是在一个函数内部定义另一个函数,内层函数可以引用...
- 在Groovy中,闭包是 `groovy.lang.Closure` 类的实例,这意味着你可以将闭包赋值给变量,作为参数传递,或者作为其他对象的字段。例如: ```groovy def myClosure = { println 'Hello, World!' } ``` 3. **...
JavaScript中的闭包是一种高级特性,它是函数和其周围状态(词法作用域)的组合,即使函数在其定义的作用域之外被调用,它仍然能访问到这些状态。这个概念是JavaScript编程中的核心部分,尤其在处理异步操作、模块化...
Python闭包实例closure.py 简单示例闭包的使用 简单示例闭包的使用
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
本篇文章主要探讨JavaScript函数式编程中的一个重要概念——闭包(closure)。闭包是一种特殊的函数,它能记住其定义时的作用域,即使在函数执行完毕后,仍然可以访问到该作用域内的变量。在JavaScript中,每个函数...
闭包(Closure)是JavaScript中一个非常重要的概念,它涉及到函数及其相关的词法作用域。简单来说,闭包就是能够访问其自身作用域之外变量的函数。这些额外的词法作用域被绑定在函数的生命周期里,使得即使函数已经...
Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ...
### JavaScript闭包完整解释 #### 一、闭包的基本概念 **闭包**是一个非常重要的JavaScript概念,它指的是一个函数能够记住并访问其外部作用域中的变量的能力,即使该函数在其外部作用域之外被调用也是如此。具体...
在Python编程语言中,闭包(closure)是一种常见的概念,它涉及到函数和作用域,经常会在代码中扮演重要角色。理解闭包及其相关注意事项,对于编写高效、正确的Python代码来说至关重要。本文档详细解释了闭包的概念...
资源名称:javascript闭包详解 中文word版 内容简介: ... Javascript中有几个非常重要的语言特性——对象、原型...闭包是Closure, 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
### JavaScript闭包高级教程 #### 简介 在JavaScript编程中,“闭包”是一个非常重要的概念,尤其对于希望深入理解和高效使用JavaScript的开发者来说。简单地说,闭包是一种能够记住并访问其创建时周围环境的函数...
### JavaScript闭包详解 #### 一、闭包概念与工作机制 **闭包**是JavaScript中最强大的特性之一,它使得函数能够记住并访问其定义时所在的作用域中的变量。要理解和运用闭包,首先需要理解作用域、作用域链以及...
在计算机科学中,闭包(Closure)是一种函数,它在一个包含一个或多个绑定变量的环境中被评估。当这个函数被调用时,它可以访问那些绑定变量。换句话说,闭包可以记住在其创建时周围存在的任何变量的状态,即使在...