JavaScript必知必会(九)function 说起 闭包问题


Posted in Javascript onJune 08, 2016

function

函数格式

function getPrototyNames(o,/*optional*/ a)
{
a = a || [];
for(var p in o)
{
a.push(p);
}
return a;
}

caller

func.caller 返回函数调用者

function callfunc()
{
if(callfunc.caller)
{
alert(callfunc.caller.toString());
}else
{
alert("没有函数调用");
}
}
function handleCaller()
{
callfunc();
}
handleCaller();//返回 handler
callfunc();//没有函数调用,返回null,执行了《没有函数调用》

callee

匿名方法递归调用

alert( (function (x) {
if (x <= ) return ;
return x * arguments.callee(x - );
}()));//

scope

作用域大家都不陌生,今天就来说说闭包问题,深刻吃透闭包问题。

<script>
var global = "global scope";//这个是全局的作用域
function scope()
{
var scope = "local scope";//这个是局部的作用域
return scope;//返回的scope,是局部的变量
}
</script>

  1、:定义的全局变量也能在函数内部访问。当定义的局部变量和全局变量名字相同时,局部变量的就会隐藏全局变量,不会破坏全局变量的值。

var scope = "global scope";
function f()
{
var scope = "local scope";
return scope;
}
alert(f());//local scope
alert(scope);//global scope;

上面确实是很容易理解,对吧。

2、 全局变量可以不用var声明,但是局部变量必须使用var声明,如果局部变量不使用var声明,编译器会默认这个是个全局变量。

<span style="line-height: .; font-family: verdana, Arial, Helvetica, sans-serif; font-size: px; background-color: rgb(, , );"></span> 
scope = "global scope";
function f()
{
scope = "local scope";
return scope;
}
alert(f());//local scope
alert(scope);//local scope

但是全局变量不使用var声明,也仅限非严格模式,如果使用严格模式的话,会报错误

<script>
"use strict";
scope = "global scope";
function f()
{
scope = "local scope";
return scope;
}
alert(f());//local scope
alert(scope);//local scope
</script>

JavaScript必知必会(九)function 说起 闭包问题

所以建议大家声明变量时,千万不要省略var,可以避免不必要的麻烦。

3、 声明提前,也是可以滴。什么叫什么提前。

JavaScript必知必会(九)function 说起 闭包问题

<script>
"use strict";
scope;
console.log(scope);
var scope = "global scope";
console.log(scope);
</script>

这个可能大家看出第一个打印undefined ,是呀还没有给他赋值, 下面赋值可定打印global scope了。

这样理解并没有错,但是为什么会这样的呢,一个变量不是应该先定义才可以使用的吗?

这里给大家说下作用域链,JavaScript是基于词法作用域的语言。

1、作用域链是一个对象或者链表,这组代码中定义了这段代码"作用域中“的变量。当JavaScript需要查找变量scope时,就会从链中的第一个对象开发查找,如果第一个对象为scope,则会直接返回这个对象的值,如果不存在继续第二对象开始查找,直到找到。如果在作用域链上未查到该变量,则会抛出一个错误。

我们可以这个作用链可以这样表示:查找 scope->window(全局对象)很显然后面是有定义scope的。但是并没有做赋值操作,后面才做赋值操作,所以此时值为undefined.

4、这个比较具有迷惑性了,大家猜想下打印的值是什么?

<script>
"use strict";
var scope = "global scope";
function f() {
console.log(scope);
var scope = "local scope";
console.log(scope);
}
f();
</script>

看到这段代码:如果你粗心大意的话,很有可能会写出错误的答案:

1、gobal scope

2、local scope

分析: 声明了全局变量,在函数体中时,第一个表示全局变量,所以打印global,第二定义了局部的变量,覆盖掉了全局的scope,所以打印local scope。

这样的分析在C# java ,完全正确。但是这里分析确实错误的。

这说明这个问题前,我们先看一个问题。

这句话很重要:全局变量在程序中始终都是有定义的。局部变量在声明它的函数体以及其所嵌套的函数内始终是定义的。

如果你是从事高级语言工作开始接触JavaScript 有点不适应其作用域的定义。我也是这样的。我们来看一个例子:

<script>
var g = "global scope";
function f()
{
for(var i=;i<;i++)
{
for(var j=;j<;j++)
{
;
}
console.log(j);
}
console.log(i);
}
console.log(g);
f();
</script>

  打印的结果是什么?

大家看到{} 表示语句块,语句块在一块作用域,所以大家可能猜想,j、i值已经在内存释放掉了,所以结果肯定是undefined.

真实的结果可能令你失望,

JavaScript必知必会(九)function 说起 闭包问题

结果为什么会是这样,我开始的表情和你一样。

JavaScript必知必会(九)function 说起 闭包问题

这时查看我让你记住的那句话。。。全局变量在程序中始终都是有定义的。局部变量在声明它的函数体以及其所嵌套的函数内始终是定义的。

确切的说函数的参数 也是属于局部变量的范畴。这句话也很重要!!!

那句话大概意思说,只要是在函数内部定义的变量,在整个函数内都是有效的。所以结果也就不难理解了。在回过头看我们的那个问题,你理解了吗?

作用链还有以下定义:

1、作用链是有一个全局对象组成。

2、在不包含嵌套的函数体内,作用链上有两个对象,第一个定义了函数参数和局部变量的对象,第二个是全局对象。

3、在一个嵌套的函数体内,作用链上至少包含三个对象。

当定以一个函数的时候,就会保存一个作用域链。

当调用这个函数时,它就会创建一个新的对象来存储它的局部变量,并将这个对象添加到保存的作用链上。同时创建一个新的更长的表示函数调用的作用链。

对于嵌套的函数,当调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用链都是不同的。内部函数在每次定义的时候都有微妙的差别,每次调用的外部函数时,内部函数的代码都是相同的,而且关联的代码的作用域也是不同的。

闭包

搞了这么久终于要讲了,但是再将之前我们在来分析下作用域。

<script>
var nameg="global"
var g = function f() {
console.log(name);
function demo()
{
console.log("demo="+name);
}
var name = "";
function demo() {
var name = "";
console.log("demo=" + name);
}
function demo() {
console.log("demo=" + nameg);
}
demo();
demo();
demo();
};
g();
</script>

我们按照作用链来分析:

调用demo0, demo0()->查找name,未找到->f()查找,返回

调用demo1,demo1()->查找name,找到,返回

调用demo2,demo2()->查找nameg,未找到->f()查找nameg,未找到->window查找nameg找到,返回。

看这个例子:

<script>
function f()
{
var count = ;
return { counter:function() {
return count++;
},reset:
function()
{
return count = ;
}
}
}
var d = f();
var c = f();
console.log("d第一次调用:"+ d.counter());//
console.log("c第一次调用:"+ c.counter());// 互不影响
console.log("d第一次调用:"+ d.reset());//
console.log("c第二次调调用"+ c.counter());//
</script>

这个例子上大家可以看到,我做了一个计数和置零的操作。

创建了两个f的对象实例d c,各有自己的作用域链,所以其值互不影响。当c第二次调用时,count值被保存了,因为c对象并未被销毁。明白这个例子后,后面的例子才比较好懂。

这个过程,大家应该十分明了了。那么现在我们来看闭包问题。我设置四个按钮,点击每个按钮就返回响应的名字。

<body>
<script>
function btnInit()
{
for(var i=;i<;i++)
{
var btn = document.getElementById("btn" + i);
btn.addEventListener("click",function() {
alert("btn" + i);
});
}
}
window.onload= btnInit;
</script>
<div>
<button id="btn">Btn</button>
<button id="btn">Btn</button>
<button id="btn">Btn</button>
<button id="btn">Btn</button>
</div>
</body>

点击运行,结果却是都是btn5;

我们用刚才的分析在坐下,首先要调用匿名函数->查找i,未找到->btnInit(),找到i在for循环中。找到。我们知道只有函数调用结束才释放,for中的i总是可见的,所以保留了最后的i值。那么如何解决呢。

解决i值在函数中不是总是可见的,那么我们就要使用函数的嵌套了,然后把i值传进去。

function btnInit()
{
for(var i=;i<;i++)
{
(function (data_i) {
var btn = document.getElementById("btn" + data_i);
btn.addEventListener("click", function () {
alert("btn" + data_i);
});
}(i));
}
}

看修改后的代码,首先执行第一次for,创建了一个对象,我们首先是执行匿名函数->data_i,未找到->function(data_i)找到,然后再次执行for有创建一个对象,闭包规则说的互不影响。所以能得出正确的结果。

以上所述是小编给大家介绍的 JavaScript必知必会(九)function 说起 闭包问题的相关知识,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
YUI 读码日记之 YAHOO.lang.is*
Mar 22 Javascript
比较搞笑的js陷阱题
Feb 07 Javascript
html组件不可输入(只读)同时任何组件都有效
Apr 01 Javascript
jQuery插件制作之参数用法实例分析
Jun 01 Javascript
JQUERY的AJAX请求缓存里的数据问题处理
Feb 23 Javascript
JS中Safari浏览器中的Date
Jul 17 Javascript
微信小程序自定义toast实现方法详解【附demo源码下载】
Nov 28 Javascript
vue的一个分页组件的示例代码
Dec 25 Javascript
jQuery插件实现弹性运动完整示例
Jul 07 jQuery
解决使用layui对select append元素无效或者未及时更新的问题
Sep 18 Javascript
webpack中的模式(mode)使用详解
Feb 20 Javascript
ElementUI实现el-form表单重置功能按钮
Jul 21 Javascript
jQuery+ajax+asp.net获取Json值的方法
Jun 08 #Javascript
jQuery焦点图轮播插件KinSlideshow用法分析
Jun 08 #Javascript
JavaScript必知必会(十) call apply bind的用法说明
Jun 08 #Javascript
AngularJs学习第八篇 过滤器filter创建
Jun 08 #Javascript
jQuery数据检索中根据关键字快速定位GridView指定行的实现方法
Jun 08 #Javascript
jquery模拟多级复选框效果的简单实例
Jun 08 #Javascript
Jquery揭秘系列:ajax原生js实现详解(推荐)
Jun 08 #Javascript
You might like
使用TinyButStrong模板引擎来做WEB开发
2007/03/16 PHP
详解WordPress中调用评论模板和循环输出评论的PHP函数
2016/01/05 PHP
PHP设计模式之观察者模式定义与用法分析
2019/04/04 PHP
jquery.simple.tree插件 更简单,兼容性更好的无限树插件
2010/09/03 Javascript
javascript之Partial Application学习
2013/01/10 Javascript
Node.js(安装,启动,测试)
2014/06/09 Javascript
使用js实现数据格式化
2014/12/03 Javascript
谈一谈javascript闭包
2016/01/28 Javascript
Bootstrap每天必学之响应式导航、轮播图
2016/04/25 Javascript
jQuery复制节点用法示例(clone方法)
2016/09/08 Javascript
原生JavaScript制作计算器
2016/10/16 Javascript
Express框架之connect-flash详解
2017/05/31 Javascript
VUE使用vuex解决模块间传值问题的方法
2017/06/01 Javascript
详解vue.js 开发环境搭建最简单攻略
2017/06/12 Javascript
AngularJs每天学习之总体介绍
2017/08/07 Javascript
Express本地测试HTTPS的示例代码
2018/06/06 Javascript
Node.js系列之发起get/post请求(2)
2019/08/30 Javascript
Weex开发之WEEX-EROS开发踩坑(小结)
2019/10/16 Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
2020/08/22 Javascript
ESLint 是如何检查 .vue 文件的
2020/11/30 Vue.js
Python牛刀小试密码爆破
2011/02/03 Python
Python中用Descriptor实现类级属性(Property)详解
2014/09/18 Python
OpenCV+python手势识别框架和实例讲解
2018/08/03 Python
python模拟预测一下新型冠状病毒肺炎的数据
2020/02/01 Python
python之生成多层json结构的实现
2020/02/27 Python
Python装饰器的应用场景代码总结
2020/04/10 Python
使用sklearn对多分类的每个类别进行指标评价操作
2020/06/11 Python
解决Python中导入自己写的类,被划红线,但不影响执行的问题
2020/07/13 Python
JBL澳大利亚官方商店:扬声器、耳机和音响系统
2018/05/24 全球购物
一份报关员的职业规划范文
2014/01/08 职场文书
开工庆典邀请函范文
2014/01/16 职场文书
群众路线教育实践活动学习笔记
2014/11/05 职场文书
2014年保险公司工作总结
2014/11/22 职场文书
幼儿园亲子活动通知
2015/04/24 职场文书
大学团日活动总结书
2015/05/11 职场文书
python高温预警数据获取实例
2022/07/23 Python