JavaScript closure的一个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function f1() {    
var n = 999;    
nAdd = function() {
n += 1
}    
function f2() {      
alert(n);    
}    
return f2;  
}  
var result1 = f1();
var result2 = f1();
var result3 = f1();  
nAdd();
result1(); //999
result2(); //999
result3(); //1000

以上是困惑的问题?为什么nAdd()影响最近的闭包。
stackoverflow的两个答案,合起来能很好的理解这个问题。

1.进一步明白细节的回答

It has to do with when nAdd is assigned the function. Note that when you create a closure, a new “copy” (for lack of a better word) of the local variables are created. Thus, result1’s n is different from result2s n which is different from result3’s n. They are seperate, and each closure cannot access another closure’s n.

Look at this line:

1
2
3
nAdd = function() {
n += 1;
}

This assigns nAdd a new closure each time. Each time, this closure will only affect the most recent “copy” of n.

So when you are doing.

1
2
3
var result1 = f1(); // assign nAdd for the first time, referring to result1's n.
var result2 = f1(); // re-assign nAdd, now it affects result2's n.
var result3 = f1(); // re-assign nAdd, now it affect result3's n.

nAdd got assigned a new closure each time. The last time, nAdd got assigned a closure with result3’s copy of n.

Thus, when you do nAdd(), you only increment result3’s n.

Here’s an example that might clear things up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function f1() {    
var n = 999;    
nAdd = function() {
n += 1
}    
function f2() {      
alert(n);    
}    
return f2;  
}  
var result1 = f1();
var nAdd1 = nAdd;
var result2 = f1();
var nAdd2 = nAdd;
var result3 = f1();  
var nAdd3 = nAdd;
nAdd3();
result1(); //999
result2(); //999
result3(); //1000
nAdd1();
result1(); // 1000
nAdd1();
result1(); // 1001
nAdd2();
result2(); // 1000
nAdd();
result3(); // 1001 (the most recent nAdd result3's n).

To further elaborate, consider what would happen if you did this instead:

1
2
3
4
5
6
7
8
var result1 = f1();
nAdd();
var result2 = f1();
var result3 = f1(); 
result1(); // 1000
result2(); // 999
result3(); //999

Or this:

1
2
3
4
5
6
7
8
var result1 = f1();
var result2 = f1();
nAdd();
var result3 = f1(); 
result1(); // 999
result2(); // 1000
result3(); // 999

It becomes obvious that nAdd updates only the most recent invocation’s n.!

2.调用本质的回答

First, you need to be clear about the following thing:

nAdd is a global scoped function. But it is using a local scoped variable n.

Every time you re-create f1(), the pointer of nAdd function will change.

Finally it will changed to the closest f1() closure and the n variable inside nAdd function will also point to the closest f1() closure. So it can only change the value of the closest one.

JavaScript中this关键字

译文

原文链接:http://davidshariff.com/blog/javascript-this-keyword/#first-article
this 关键词是 JavaScript 一个常用的特性,但是它同时也是这门语言中最容易让人困惑或者误解的一个点。this 实际上是什么意思?如何去确定呢?
这篇文章尽力用一种清晰的方式来解决大家的困惑。
‘this’ 关键字对于其它程序化编程语言并不新奇,它经常指向一个类的构造函创建的新对象。举个例子:如果我有一个 Boat() 类,它含有一个 moveBoat() 方法,当在 moveBoat() 方法内部引用 this 的时候,我们实际上访问的是新创建的对象 Boat().
在JavaScript中,当使用new关键字的时候,我们也有构造函数这么个概念。但是,这并不是唯一的规则, this 经常会根据不同的execution context 指向不同的对象。如果你对execution context不是很清楚的话,我推荐你阅读我的另一篇文章。说的够多了,让我们来看一些JavaScript的例子:

1
2
3
4
5
6
7
// global scope
foo = 'abc';
alert(foo); // abc
this.foo = 'def';
alert(foo); // def

当我们在全局上下文中(不再函数内部)使用 this 关键字时,this 总是指向全局对象。现在让我们看看 this 在函数内部的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var boat = {
size: 'normal',
boatInfo: function() {
alert(this === boat);
alert(this.size);
}
};
boat.boatInfo(); // true, 'normal'
var bigBoat = {
size: 'big'
};
bigBoat.boatInfo = boat.boatInfo;
bigBoat.boatInfo(); // false, 'big'

上面这个例子中 this 如何确定?
以上是文章摘要 阅读更多请点击——>右下角的more

标签解析和JavaScript的闭包(Identifier Resolution and Closures in the JavaScript Scope Chain)

原文地址:http://davidshariff.com/blog/javascript-scope-chain-and-closures/#more-271

从我的上篇文章中,我们知道每个函数都会有一个关联的execution contextexecution context包含一个变量对象variable object[VO]。变量对象由函数的局部变量、内部函数和参数声明组成。
每个execution context的作用域链(scope chain)属性简单来说就是当前context的[VO]和所有父级的词法作用域的[VO]。
Scope = VO + All Parent VOs
Eg: scopeChain = [[VO] + [VO1] + [VO2] + [VO n+1]];

定义作用域链的变量对象

[VO]s

现在我们知道作用域链的第一个[VO]属于当前的execution context,通过查找父级context的作用域链,我们能够找到其余的父级[VO]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function one() {
two();
function two() {
three();
function three() {
alert('I am at function three');
}
}
}
one();

image

这个例子直截了当的表明了这种情况。从global context 开始,我们调用了 one(), one()调用了two(), 然后又调用了three()。在函数 tree()内,执行了提示输出。上面的图片展示出了函数 three() 在执行 alert('I am at function three') 时的调用栈。此时,我们可以将 scope chain 理解为下面这样:
three() Scope Chain = [[three() VO] + [two() VO] + [one() VO] + [Global VO]]

词法作用域

JavaScript的一个重要特征就是解释器使用了词法作用域,而不是动态作用域。复杂点来说,就是所有内部函数都会根据代码位置,静态地绑定到父级context上。
以上是文章摘要 阅读更多请点击——>右下角的more

深入探讨JavaScript的执行环境和栈(What is the Execution Context & Stack in JavaScript)

原文网址:http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/

这篇文章我将会深入地讨论JavaScript中最根本的一部分——Execution Context(执行上下文)。在文章结束的时候,你应该对解释器的工作原理有一个比较清晰的理解,对于为什么会存在‘变量提升’,它们的值又是如何被真正确定的这些问题有一个正确的答案。

什么是Executin Context(执行上下文)

当JavaScript代码执行的时候,执行环境是很重要的,它可能是下面三种情况中的一种:

  • 全局 code(Global code)——代码第一次执行的默认环境
  • 函数 code(Function code)——执行流进入函数体
  • Eval code(Eval code)——代码在eval函数内部执行

在网上你能够读到许多关于作用域的资料,这篇文章的目的是让事情变得简单些。让我们来思考下execution context这个词,它与当前代码的环境 / 作用域是等价的。好了,说的够多了,让我们来看一个包含global和function / local context的例子吧。

image
这里没有什么特别的地方,我们有一个global context被紫色的框框着,还有三个不同的function context,分别被绿色、蓝色、橘色的框框着。在你的程序中,有且仅能有一个global context,并且它能够被任何其他的context访问到。
你能够拥有任意多个function context,并且每个函数被调用的时候都会生成一个新的context。它会生成一个私有作用域,并且在它内部声明的任何东西都不能直接在它的外部访问。就像上面的例子,一个函数可以直接访问它外面的变量,但是外部的context就不能直接访问在内部声明的变量或者函数。为什么会这样?如何准确的理解这段代码的执行?
以上是文章摘要 阅读更多请点击——>右下角的more

JS函数式编程

一、概念相关

  1. “函数式编程”是这种编程典范,它将电脑运算视为函数的计算。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要;和过程化编程相比,函数式编程里,函数的计算可随时调用。
  2. 函数式编程的重点是函数的定义而不是状态机(state machine)的实现
  3. 函数式概念的运用,例如:扩展数组排序、动态HTML生成的优美代码、系列函数的应用。

二、应用概念

1、高阶函数

可以操作其它函数的函数,被称为高阶函数。利用高阶函数可以简化操作。

三、应用代码

  1. 扩展数组排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var a = {
    value: 1,
    date: 2001
    }
    var b = {
    value: 2,
    date:2002
    }
    var c = {
    value: 3,
    date:2003
    }
    var str = [a,c,b];
    var str2 = ['zx','z','yz'];
    str2.sort();
    console.log(str2);
    str.sort(function(x,y){
    return x.date - y.date;
    });
    console.log(str);
  2. 系列函数应用

1
2
3
4
5
6
7
8
9
Function.prototype.sequence=function(g) {
var f=this;
return function() {
f();g();
}
};
function alertFrst() { alert('First!'); }
function alertSec() { alert('Second!'); }2
setTimeout( alertFrst.sequence(alertSec), 5000);

一组函数作为回调函数
以上是文章摘要 阅读更多请点击——>右下角的more

闭包深入理解

闭包的深入理解

一:引子

1
2
3
4
5
6
7
8
9
10
11
12
function makePowerFn(power){
function powerFn(base){
console.log(power);
return Math.pow(base,power);
}
return powerFn;
}
var square = makePowerFn(2);
var cube = makePowerFn(3);
console.log(square(3));
console.log(cube(3));
console.log(square(4));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function makeIncrementer(){
var n = 0;
function increment(){
console.log(++n);
}
return increment;
}
var inc1 = makeIncrementer();
var inc2 = makeIncrementer();
inc1();
inc1();
inc1();
inc2();
inc2();
inc2();
1
2
3
4
5
6
7
8
9
10
11
12
13
function out(){
return function (a){
var b = a;
console.log(b);
}
}
var a1 = out();
var a2 = out();
a1(1);
a2(2);
a1();

乍一看这些本地变量已经不再受限于基本的域限制并拥有了无限的生命周期了。可以推出来的结论是:它们已经不是存在栈(stack)上,而是在堆(heap)上了

(Ps:对于heap和stack的区别有这么几点:1. stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配;2. stack空间有限,heap的空间是很大的自由区。例如:在Java中,若只是声明一个对象,则先在栈内存中为其分配地址空间。若再new一下,实例化它,则在堆内存中为其分配地址;3. 举例:数据类型 变量名;这样定义的东西在栈区。如:Object a = null;这样定义的东西就在堆区:如:Object b = new Object();在堆内存中分配空间)

以上是文章摘要 阅读更多请点击——>右下角的more