`
luozhonghua2014
  • 浏览: 62398 次
文章分类
社区版块
存档分类
最新评论

javascript 作用域 闭包 对象 原理和示例分析(上)

 
阅读更多

阅读、理解、思考、实践,再实践、再思考.... 深圳小地瓜献上

javascript高级特性包含:作用域、闭包、对象

-----------------------------------------------作用域----------------------------------------------------------------------------------------

作用域(scope)是结构化编程语言中的重要概念,它决定了变量的可见范围和生命周
期,正确使用作用域可以使代码更清晰、易懂。作用域可以减少命名冲突,而且是垃圾回收
的基本单元。和 C、C++、Java 等常见语言不同,JavaScript 的作用域不是以花括号包围的块
级作用域(block scope) ,这个特性经常被大多数人忽视,因而导致莫名其妙的错误。例如
下面代码, 在大多数类 C 的语言中会出现变量未定义的错误, 而在 JavaScript 中却完全合法:
if (true) {
var somevar = 'value';
}
console.log(somevar); // 输出 value
这是因为 JavaScript 的作用域完全是由函数来决定的, if 、 for 语句中的花括号不是独
立的作用域。


作用域(scope)包含函数作用域和全局作用域

函数作用域:不同于大多数类 C 的语言,由一对花括号封闭的代码块就是一个作用域,JavaScript 的作用域是通过函数来定义的,在一个函数中定义的变量只对这个函数内部可见,我们称为函
数作用域。在函数中引用一个变量时,JavaScript 会先搜索当前函数作用域,或者称为“局
部作用域” ,如果没有找到则搜索其上层作用域,一直到全局作用域。我们看一个简单的
例子:
var v1 = 'v1';
var f1 = function() {
console.log(v1); // 输出 v1
};
f1();
var f2 = function() {
var v1 = 'local';
console.log(v1); // 输出 local
};
f2();
以上示例十分明了,JavaScript 的函数定义是可以嵌套的,每一层是一个作用域,变量
搜索顺序是从内到外
。下面这个例子可能就有些令人困惑:
var scope = 'global';
var f = function() {
console.log(scope); // 输出 undefined
var scope = 'f';
}
f();
上面代码可能和你预想的不一样, 没有输出 global , 而是 undefined , 这是为什么呢?
这是 JavaScript 的一个特性,按照作用域搜索顺序,在 console.log 函数访问 scope 变
量时,JavaScript 会先搜索函数 f 的作用域,恰巧在 f 作用域里面搜索到 scope 变量,
所以上层作用域中定义的 scope 就被屏蔽了,但执行到 console.log 语句时, scope 还
没被定义,或者说初始化,所以得到的就是 undefined 值了。
我们还可以从另一个角度来理解:对于开发者来说,在访问未定义的变量或定义了但没
有初始化的变量时,获得的值都是 undefined 。
于是我们可以认为,无论在函数内什么地
方定义的变量,在一进入函数时就被定义了,但直到 var 所在的那一行它才被初始化,所
以在这之前引用到的都是 undefined 值。 (事实上,JavaScript 的内部实现并不是这样,未
定义变量和值为 undefined 的变量还是有区别的。 )
函数作用域的嵌套
接下来看一个稍微复杂的例子:

var f = function() {
var scope = 'f0';
(function() {
var scope = 'f1';
(function() {
console.log(scope); // 输出 f1
})();

})();
};
f();
上面是一个函数作用域嵌套的例子,我们在最内层函数引用了 scope 变量,通过作用
域搜索,找到了其父作用域中定义的 scope 变量。
有一点需要注意:函数作用域的嵌套关系是定义时决定的,而不是调用时决定的,也就
是说,JavaScript 的作用域是静态作用域,又叫词法作用域,这是因为作用域的嵌套关系可
以在语法分析时确定,而不必等到运行时确定
。下面的例子说明了这一切:
var scope = 'top';
var f1 = function() {
console.log(scope);
};
f1(); // 输出 top
var f2 = function() {
var scope = 'f2';
f1();
};
f2(); // 输出 top
这个例子中,通过 f2 调用的 f1 在查找 scope 定义时,找到的是父作用域中定义
的 scope 变量,而不是 f2 中定义的 scope 变量。这说明了作用域的嵌套关系不是在调用
时确定的,而是在定义时确定的。


全局作用域:在 JavaScript 中有一种特殊的对象称为 全局对象。这个对象在Node.js 对应的是 global
对象, 在浏览器中对应的是 window 对象。 由于全局对象的所有属性在任何地方都是可见的,
所以这个对象又称为 全局作用域。全局作用域中的变量不论在什么函数中都可以被直接引
用,而不必通过全局对象。


满足以下条件的变量属于全局作用域:

在最外层定义的变量;

全局对象的属性;

任何地方隐式定义的变量(未定义直接赋值的变量) 。


需要格外注意的是第三点,在任何地方隐式定义的变量都会定义在全局作用域中,即不
通过 var 声明直接赋值的变量
。这一点经常被人遗忘,而模块化编程的一个重要原则就是
避免使用全局变量,所以我们在任何地方都不应该隐式定义变量。

-----------------------------------------------------闭包--------------------------------------------------------------------------

闭包(closure)是函数式编程中的概念,出现于 20 世纪 60 年代,最早实现闭包的语言
是 Scheme,它是 LISP 的一种方言。之后闭包特性被其他语言广泛吸纳。
闭包的严格定义是“由函数(环境)及其封闭的自由变量组成的集合体。 ”这个定义对
于大家来说有些晦涩难懂,所以让我们先通过例子和不那么严格的解释来说明什么是闭包,
然后再举例说明一些闭包的经典用途。

什么是闭包?

通俗地讲,JavaScript 中每个的函数都是一个闭包,但通常意义上嵌套的函数更能够体
现出闭包的特性,请看下面这个例子:
var generateClosure = function() {
var count = 0;
var get = function() {
count ++;
return count;
};
return get;
};
var counter = generateClosure();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3
这段代码中, generateClosure() 函数中有一个局部变量 count ,初值为 0。还有一
个叫做 get 的函数, get 将其父作用域,也就是 generateClosure() 函数中的 count 变
量增加 1,并返回 count 的值。 generateClosure() 的返回值是 get 函数。在外部我们
通过 counter 变量调用了 generateClosure() 函数并获取了它的返回值,也就是 get 函
数,接下来反复调用几次 counter() ,我们发现每次返回的值都递增了 1。
让我们看看上面的例子有什么特点,按照通常命令式编程思维的理解, count 是
generateClosure 函数内部的变量,它的生命周期就是 generateClosure 被调用的时
期,当 generateClosure 从调用栈中返回时, count 变量申请的空间也就被释放。问题

是,在 generateClosure() 调用结束后, counter() 却引用了“已经释放了的” count

变量,而且非但没有出错,反而每次调用 counter() 时还修改并返回了 count 。这是怎
么回事呢?

这正是所谓闭包的特性。 当一个函数返回它内部定义的一个函数时, 就产生了一个闭包
闭包不但包括被返回的函数,还包括这个函数的定义环境。上面例子中,当函数
generateClosure() 的内部函数 get 被一个外部变量 counter 引用时, counter 和
generateClosure() 的局部变量就是一个闭包
。如果还不够清晰,下面这个例子可以帮助
你理解:
var generateClosure = function() {
var count = 0;
var get = function() {
count ++;
return count;
};
return get;
};
var counter1 = generateClosure();
var counter2 = generateClosure();
console.log(counter1()); // 输出 1
console.log(counter2()); // 输出 1
console.log(counter1()); // 输出 2
console.log(counter1()); // 输出 3
console.log(counter2()); // 输出 2
上面这个例子解释了闭包是如何产生的: counter1 和 counter2 分别调用了 generate-
Closure() 函数,生成了两个闭包的实例,它们内部引用的 count 变量分别属于各自的
运行环境。我们可以理解为,在 generateClosure() 返回 get 函数时,私下将 get 可
能引用到的 generateClosure() 函数的内部变量(也就是 count 变量)也返回了,并
在内存中生成了一个副本,之后 generateClosure() 返回的函数的两个实例 counter1
和 counter2 就是相互独立的了。

闭包的用途:

1. 嵌套的回调函数
闭包有两个主要用途,一是实现嵌套的回调函数,二是隐藏对象的细节。让我们先看下
面这段代码示例,了解嵌套的回调函数。如下代码是在 Node.js 中使用 MongoDB 实现一个
简单的增加用户的功能:

exports.add_user = function(user_info, callback) {
var uid = parseInt(user_info['uid']);
mongodb.open(function(err, db) {
if (err) {
callback(err);
return
}
db.collection('users',
function(err, collection) {
if (err) {
callback(err);
return
}
collection.ensureIndex("uid",
function(err) {
if (err) {
callback(err);
return
}
collection.ensureIndex("username",
function(err) {
if (err) {
callback(err);
return
}
collection.findOne({
uid: uid
},
function(err) {
if (err) {
callback(err);
return
}
if (doc) {
callback('occupied')
} else {
var user = {
uid: uid,
user: user_info,


};
collection.insert(user,
function(err) {
callback(err)
})
}
})
})
})
})
})
};

如果你对 Node.js 或 MongoDB 不熟悉,没关系,不需要去理解细节,只要看清楚大概
的逻辑即可。这段代码中用到了闭包的层层嵌套,每一层的嵌套都是一个回调函数。回调函
数不会立即执行,而是等待相应请求处理完后由请求的函数回调。我们可以看到,在嵌套的
每一层中都有对 callback 的引用,而且最里层还用到了外层定义的 uid 变量。由于闭包
机制的存在,即使外层函数已经执行完毕,其作用域内申请的变量也不会释放,因为里层的
函数还有可能引用到这些变量,这样就完美地实现了嵌套的异步回调。


2. 实现私有成员

我们知道,JavaScript 的对象没有私有属性,也就是说对象的每一个属性都是曝露给外部
的。这样可能会有安全隐患,譬如对象的使用者直接修改了某个属性,导致对象内部数据的一
致性受到破坏等。JavaScript通过约定在所有私有属性前加上下划线(例如 _myPrivateProp ) ,

表示这个属性是私有的,外部对象不应该直接读写它。但这只是个非正式的约定,假设对象
的使用者不这么做,有没有更严格的机制呢?答案是有的,通过闭包可以实现。让我们再看
看前面那个例子:
var generateClosure = function() {
var count = 0;
var get = function() {
count++;
return count;
};
return get;
};
var counter = generateClosure();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3
我们可以看到,只有调用 counter() 才能访问到闭包内的 count 变量,并按照规则
对其增加1,除此之外决无可能用其他方式找到 count 变量。受到这个简单例子的启发,
我们可以把一个对象用闭包封装起来, 只返回一个 “访问器” 的对象, 即可实现对细节隐藏。
关于实现JavaScript对象私有成员的更多信息,请参考http://javascript.crockford.com/private.html。

-------------------------------------------------------------------对象-------------------------------------------------------------

提起面向对象的程序设计语言,立刻让人想起的是 C++、Java 等这类静态强类型语言,
以及 Python、Ruby 等脚本语言,它们共有的特点是基于类的面向对象。而说到 JavaScript,
很少能让人想到它面向对象的特性,甚至有人说它不是面向对象的语言,因为它没有类。没
错,JavaScript 真的没有类,但 JavaScript 是面向对象的语言。JavaScript 只有对象,对象就
是对象,不是类的实例。
因为绝大多数面向对象语言中的对象都是基于类的, 所以经常有人混淆类的实例与对象
的概念。对象就是类的实例,这在大多数语言中都没错,但在 JavaScript 中却不适用。
JavaScript 中的对象是基于原型的,因此很多人在初学 JavaScript 对象时感到无比困惑。通过
这一节,我们将重新认识 JavaScript 中对象,充分理解基于原型的面向对象的实质。

创建和访问

javaScript 中的对象实际上就是一个由属性组成的关联数组,属性由名称和值组成,值
的类型可以是任何数据类型,或者函数和其他对象。注意 JavaScript 具有函数式编程的特性,
所以函数也是一种变量,大多数时候不用与一般的数据类型区分。

在 JavaScript 中,你可以用以下方法创建一个简单的对象:
var foo = {};
foo.prop_1 = 'bar';
foo.prop_2 = false;
foo.prop_3 = function() {
return 'hello world';
}
console.log(foo.prop_3());
以上代码中,我们通过 var foo = {}; 创建了一个对象,并将其引用赋值给 foo ,
通过 foo.prop1 来获取它的成员并赋值, 其中 {} 是对象字面量的表示方法, 也可以用 var
foo = new Object() 来显式地创建一个对象。
1. 使用关联数组访问对象成员
我们还可以用关联数组的模式来创建对象,以上代码修改为:
var foo = {};
foo['prop1'] = 'bar';
foo['prop2'] = false;
foo['prop3'] = function() {
return 'hello world';
}
在 JavaScript 中,使用句点运算符和关联数组引用是等价的,也就是说任何对象(包括
this 指针)都可以使用这两种模式。使用关联数组的好处是,在我们不知道对象的属性名
称的时候,可以用变量来作为关联数组的索引
。例如:
var some_prop = 'prop2';
foo[some_prop] = false;

2. 使用对象初始化器创建对象
上述的方法只是让你对JavaScript对象的定义有个了解, 真正在使用的时候, 我们会采用
下面这种更加紧凑明了的方法:
var foo = {
'prop1': 'bar',
prop2: 'false',
prop3: function (){
return 'hello world';
}
};
这种定义的方法称为对象的初始化器。注意,使用初始化器时,对象属性名称是否加引
号是可选的, 除非属性名称中有空格或者其他可能造成歧义的字符, 否则没有必要使用引号。


构造函数
前一小节讲述的对象创建方法都有一个弱点,就是创建对象的代码是一次性的。如果我
们想创建多个规划好的对象,有若干个固定的属性、方法,并能够初始化,就像 C++ 语言
中的对象一样,该如何做呢?别担心,JavaScript 提供了构造函数,让我们来看看应该如何
创建复杂的对象。
function User(name, uri) {
this.name = name;
this.uri = uri;
this.display = function() {
console.log(this.name);
}
}
以上是一个简单的构造函数,接下来用 new 语句来创建对象:
var someuser = new User('byvoid', 'http://www.byvoid.com');
然后就可以通过 someuser 来访问这个对象的属性和方法了。

上下文对象

在 JavaScript 中,上下文对象就是 this 指针,即被调用函数所处的环境。上下文对象
的作用是在一个函数内部引用调用它的对象本身,JavaScript 的任何函数都是被某个对象调
用的,包括全局对象,所以 this 指针是一个非常重要的东西。

在前面使用构造函数的代码中我们已经看到了 this 的使用方法,下面代码可以更佳清
楚地说明上下文对象的使用方式:

var someuser = {
name: 'byvoid',
display: function() {
console.log(this.name);
}
};
someuser.display(); // 输出 byvoid
var foo = {
bar: someuser.display,
name: 'foobar'
};
foo.bar(); // 输出 foobar

JavaScript 的函数式编程特性使得函数可以像一般的变量一样赋值、传递和计算,我们
看到在上面代码中, foo 对象的 bar 属性是 someuser.display 函数,使用 foo.bar()
调用时, bar 和 foo 对象的函数看起来没有区别,其中的 this 指针不属于某个函数,而
是函数调用时所属的对象。

在 JavaScript 中,本质上,函数类型的变量是指向这个函数实体的一个引用,在引用之
间赋值不会对对象产生复制行为
。我们可以通过函数的任何一个引用调用这个函数,不同之
处仅仅在于上下文
。下面例子可以帮助我们理解:
var someuser = {
name: 'byvoid',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
someuser.func(); // 输出 byvoid
foo.func = someuser.func;
foo.func(); // 输出 foobar
name = 'global';
func = someuser.func;
func(); // 输出 global

为防止func歧义,再复制一份,将得到同样的结果。

var someuser = {
name: 'byvoid',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
someuser.func(); // 输出 byvoid
foo.func = someuser.func;
foo.func(); // 输出 foobar
name = 'global';
var func1 = someuser.func;
func1(); // 输出 global

仔细观察上面的例子,使用不同的引用来调用同一个函数时, this 指针永远是这个引
用所属的对象。在前面的章节中我们提到了 JavaScript 的函数作用域是静态的,也就是说一
个函数的可见范围是在预编译的语法分析中就可以确定的, 而上下文对象则可以看作是静态
作用域的补充。

1. call 和 apply
在 JavaScript 中, call 和 apply 是两个神奇的方法,但同时也是容易令人迷惑的两个
方法,乃至许多对 JavaScript 有经验的人也不太清楚它们的用法。 call 和 apply 的功能是
以不同的对象作为上下文来调用某个函数
。简而言之,就是允许一个对象去调用另一个对象
的成员函数。乍一看似乎很不可思议,而且容易引起混乱,但其实 JavaScript 并没有严格的

所谓“成员函数”的概念,函数与对象的所属关系在调用时才展现出来。灵活使用 call 和
apply 可以节省不少时间,在后面我们可以看到, call 可以用于实现对象的继承
call 和 apply 的功能是一致的,两者细微的差别在于 call 以参数表来接受被调用函
数的参数,而 apply 以数组来接受被调用函数的参数

call 和 apply 的语法分别是:
func.call(thisArg[, arg1[, arg2[, ...]]])
func.apply(thisArg[, argsArray])
其中, func 是函数的引用, thisArg 是 func 被调用时的上下文对象, arg1 、 arg2 或
argsArray 是传入 func 的参数。我们以下面一段代码为例介绍 call 的工作机制:
var someuser = {
name: 'byvoid',
display: function(words) {
console.log(this.name + ' says ' + words);
}
};
var foo = {
name: 'foobar'
};
someuser.display.call(foo, 'hello'); // 输出 foobar says hello
用 Node.js 运行这段代码, 我们可以看到控制台输出了 foobar 。 someuser.display 是
被调用的函数,它通过 call 将上下文改变为 foo 对象,因此在函数体内访问 this.name
时,实际上访问的是 foo.name, 因而输出了 foobar 。
2. bind
如何改变被调用函数的上下文呢?前面说过,可以用 call 或 apply 方法,但如果重复
使用会不方便,因为每次都要把上下文对象作为参数传递,而且还会使代码变得不直观。针
对这种情况,我们可以使用 bind 方法来永久地绑定函数的上下文,使其无论被谁调用,上
下文都是固定的。 bind 语法如下:
func.bind(thisArg[, arg1[, arg2[, ...]]])
其中 func 是待绑定函数, thisArg 是改变的上下文对象, arg1 、 arg2 是绑定的参
数表。 bind 方法返回值是上下文为 thisArg 的 func
。通过下面例子可以帮你理解 bind
的使用方法:

var someuser = {
name: 'byvoid',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
foo.func = someuser.func;
foo.func(); // 输出 foobar
foo.func1 = someuser.func.bind(someuser);
foo.func1(); // 输出 byvoid
func = someuser.func.bind(foo);
func(); // 输出 foobar
func2 = func;
func2(); // 输出 foobar

上面代码直接将 foo.func 赋值为 someuser.func ,调用 foo.func() 时, this 指
针为 foo ,所以输出结果是 foobar 。 foo.func1 使用了 bind 方法,将 someuser 作
为 this 指针绑定到 someuser.func ,调用 foo.func1() 时, this 指针为 someuser ,
所以输出结果是 byvoid 。(未定义直接赋值的变量)全局函数 func 同样使用了 bind 方法,将 foo 作为 this 指
针绑定到 someuser.func ,调用 func() 时, this 指针为 foo ,所以输出结果是 foobar 。
而 func2 直接将绑定过的 func 赋值过来,与 func 行为完全相同。
3. 使用 bind 绑定参数表
bind 方法还有一个重要的功能:绑定参数表,如下例所示。
var person = {
name: 'byvoid',
says: function(act, obj) {
console.log(this.name + ' ' + act + ' ' + obj);
}
};
person.says('loves', 'diovyb'); // 输出 byvoid loves diovyb
byvoidLoves = person.says.bind(person, 'loves');
byvoidLoves('you'); // 输出 byvoid loves you
可以看到, byvoidLoves 将 this 指针绑定到了 person ,并将第一个参数绑定到
loves ,之后在调用 byvoidLoves 的时候,只需传入第三个参数。这个特性可以用于创建
一个函数的“捷径” ,之后我们可以通过这个“捷径”调用,以便在代码多处调用时省略重
复输入相同的参数。

4. 理解 bind
尽管 bind 很优美,还是有一些令人迷惑的地方,例如下面的代码:
var someuser = {
name: 'byvoid',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
func = someuser.func.bind(foo);
func(); // 输出 foobar
func2 = func.bind(someuser);
func2(); // 输出 foobar


全局函数 func 通过 someuser.func.bind 将 this 指针绑定到了 foo ,调用 func() 输
出了 foobar 。我们试图将 func2 赋值为已绑定的 func 重新通过 bind 将 this 指针绑定到
someuser 的结果, 而调用 func2 时却发现输出值仍为 foobar , 即 this 指针还是停留在 foo
对象上,这是为什么呢?要想解释这个现象,我们必须了解 bind 方法的原理。
让我们看一个 bind 方法的简化版本(不支持绑定参数表) :
someuser.func.bind = function(self) {
return this.call(self);
};
假设上面函数是 someuser.func 的 bind 方法的实现,函数体内 this 指向的是
someuser.func ,因为函数也是对象,所以 this.call(self) 的作用就是以 self 作为
this 指针调用 someuser.func 。
//将func = someuser.func.bind(foo)展开:
func = function() {
return someuser.func.call(foo);
};
//再将func2 = func.bind(someuser)展开:
func2 = function() {
return func.call(someuser);
};
从上面展开过程我们可以看出, func2 实际上是以 someuser 作为 func 的 this 指
针调用了 func ,而 func 根本没有使用 this 指针,所以两次 bind 是没有效果的


分享到:
评论

相关推荐

    javascript 闭包、匿名函数、作用域链

    JavaScript中的闭包、匿名函数和作用域链是编程中至关重要的概念,它们是理解JavaScript运行机制的关键。在本文中,我们将深入探讨这三个概念,并通过实际示例来展示它们的运用。 首先,我们来讨论“闭包”。闭包是...

    现代JavaScript高级教程:深入作用域、闭包及DOM操作

    内容概要:本书《现代JavaScript高级教程》全面涵盖了JavaScript的高级特性和技术,从基础到进阶逐步讲解,特别强调了作用域、闭包、DOM操作等核心概念。书中提供了丰富的示例代码和实际应用场景,帮助读者深入理解...

    细品javascript 寻址,闭包,对象模型和相关问题.docx

    闭包是JavaScript的一个重要特性,它允许一个函数访问并操作其外部作用域中的变量。这种特性使得函数可以记住并访问在其外部作用域定义的变量,即使这些变量已经不在其直接可访问的作用域内。 **1. 闭包的工作原理*...

    JavaScript作用域示例详解_.docx

    JavaScript作用域是编程中至关重要的概念,它规定了变量和函数的可见性和生命周期。了解JavaScript作用域对于编写高效、安全的代码至关重要。本篇将详细解释JavaScript作用域的几个核心特性,包括无块级作用域、函数...

    第四章示例代码__对象的作用域

    在示例代码中,可能会展示如何在不同作用域内创建和使用对象,包括如何在函数内部创建局部对象,如何在类中定义成员对象,以及如何利用闭包保持对变量的访问。通过这些例子,开发者将能更直观地看到作用域规则的实际...

    闭包作用域

    #### 三、闭包示例分析 以下面的例子来说明闭包的工作原理: ```javascript function a() { var i = 0; function b() { alert(++i); } return b; } var c = a(); c(); ``` 在这个例子中,`a` 函数返回了一...

    闭包概念原理

    当函数执行完毕后,执行上下文会被销毁,但闭包却能够保持对外部作用域中变量的引用,即使外部函数已经执行完毕。 #### 四、函数式参数与闭包的关系 函数式参数是指值为函数的参数。例如,在下面的例子中,`...

    JavaScript 中的闭包是指内部函数可以访问外部函数作用域中的变量

    1. **函数作用域**:在JavaScript中,变量的作用域由其声明的位置决定。如果一个变量是在函数内部声明的,则该变量仅在该函数内可见;如果是在全局作用域中声明的,则该变量在整个程序中都可见。这种作用域规则确保...

    JavaScript使用闭包模仿块级作用域操作示例

    JavaScript中的闭包是一种强大的特性,它允许函数访问和修改外部函数的作用域中的变量,即使外部函数已经执行完毕。由于JavaScript不支持块级作用域(如C++或Java中的`{}`),开发者通常利用闭包来模拟块级作用域的...

    JavaScript闭包的定义和理解,含代码示例

    闭包是JavaScript中一个非常重要的概念,它涉及到函数作用域以及变量的作用域链。简单来说,闭包就是一个能够访问其外部作用域中变量的函数。具体而言,当一个函数能够记住并访问其外部作用域中的变量时,即使这个...

    JavaScript: 函数与作用域深入解析及应用场景

    接着讨论了 JavaScript 中的全局作用域、局部作用域、块级作用域和函数作用域,特别是闭包的概念。随后,文章探讨了函数的高级用法,如递归函数、高阶函数和立即执行函数表达式(IIFE)。最后,通过实际应用示例,如...

    javascript闭包

    要理解和运用闭包,首先需要理解作用域、作用域链以及垃圾回收机制。 ##### 1.1 闭包的基本定义 闭包是一种表达式,它包含了许多变量及其绑定的环境。具体到JavaScript中,闭包通常指的是一种特殊类型的函数,这种...

    夯实基础中篇-图解作用域链和闭包.doc

    在JavaScript编程中,作用域链和闭包是两个至关重要的概念,它们对于理解代码执行机制以及函数内部如何访问和管理变量至关重要。让我们深入探讨这两个概念。 首先,**作用域链**是JavaScript中的一种机制,它定义了...

    javaScript闭包技术资料

    1. **函数的执行环境**:每当函数被调用时,都会创建一个新的执行上下文,其中包含变量对象、作用域链等信息。 2. **活动对象**:在函数执行时创建,用于存储函数参数、局部变量等信息。它是一个内部对象,不可直接...

    JavaScript闭包与活动.pdf

    JavaScript中的闭包是一种重要的编程概念,它涉及到函数、作用域和变量持久化等多个方面。闭包的本质是一个函数,它可以访问并操作其外部作用域的变量,即使在其外部作用域已经结束之后仍然能保持对这些变量的引用。...

    深入理解变量作用域

    ### 深入理解变量作用域 ...此外,理解作用域链和闭包的概念对于编写高质量、可维护的JavaScript代码至关重要。掌握这些知识点有助于开发者更好地管理变量的作用范围,避免潜在的问题,提高代码的质量和性能。

    【JavaScript源代码】js闭包和垃圾回收机制示例详解.docx

    3. **私有化**:闭包可以用来创建私有变量和方法,这些变量和方法只能被闭包本身访问,而不能被全局作用域或其他函数访问。 4. **内存管理**:闭包中的变量会持续存在于内存中,直到不再被任何闭包引用为止,这可能...

    深入理解javascript作用域和闭包

    javascript中函数内声明的所有变量在函数体内始终是可见的,在javascript中有全局作用域和局部作用域,但是没有块级作用域,局部变量的优先级高于全局变量,通过几个示例来了解下javascript中作用域的那些“潜规则”...

Global site tag (gtag.js) - Google Analytics