从零开始学_JavaScript_系列(19)——js系列<6>闭包

(18)闭包

①函数内部的子函数,用到了父函数的变量,就叫做闭包。

 

②可以为函数保存其执行状态,

 

③其原理大概是:

首先,假如B函数在A函数的内部,则A为外部函数,B为内部函数,B可以访问A的变量(变量的作用域规定的);

然后,假如在函数内部,如果有一个return,那么在return结束前,这个函数中的变量,其值是维持不变的;

另外需要注意,不能让A函数(外部函数)执行完毕,否则状态无法保留(因为闭包的前提是A函数没有执行完毕),因此不能直接调用A函数,而是需要生成A函数return部分的一个实例。

 

因为有了这两个前提,于是两个办法:

方法一:我们可以把B函数放在A函数的return之中。return的内容是一个对象{},然后通过new生成这样一个对象,B函数成为这个对象的一个方法;

如代码:

var func = function () {
    var i = 0;
    return {
        getI: function () {
            return i;
        },
        add: function () {
           i++;
        }
    }
}
var p = new func();
p.add();
console.log(p.getI());

此时,p.add()会导致i的值增加,此时i的值变为了1。

 

 

方法二:让A函数的return部分,成为一个函数,

但不能直接操纵A函数,因为调用A函数必然会导致A函数的return执行完毕,(因为函数会执行到return结束)

因此,需要将A函数的return部分赋值给另外一个变量C,此时C是A函数return部分(要记得,这个return部分是一个函数)的一个实例(相当于一个构造函数生成一个实例)。

而B函数是这个return部分的构造函数的一个方法,因此这个生成的实例也拥有了B函数这个方法。

如代码:

var func = function () {
    var i = 0;
    return function(){
        i++;
        return i;
    }
}
var p = new func();
console.log(p());
console.log(p());

这时,p的调用会导致i加一,因此调用p,输出的值分别为1和2。

 

ps:无论是哪种方法,假如我们再new一个实例

var q = new func();

其并不会导致i的值在p和q之间共享。

 

 

④优点:

按照网易云课堂的说法,闭包可以减少内存使用,提高效率。(个人经过实测,认为没有说服性)

但是经过我实测,大部分方法和闭包差不多,比闭包慢的一个方法我后面列出。

如:

var func = function () {
    var i = 0;
    return function () {
        i++;
        return i;
    }
}
var starttime = new Date();
var p = new func();
for(var i=0;i<1000000;i++){
    p();
}
console.log(p())
console.log(new Date()-starttime);

计算一百万次,消耗时间大概10毫秒左右。

 

这是我看网易云课堂给的一个示例(我自己略有修改);

function sum(i) {
    var add = function (i) {
        i++;
        return i;
    }
    return add(i);
}
var starttime = new Date();
for (var i = 0; i < 1000000; i++) {
    sum(1);
}
console.log(sum(1));
console.log(new Date() - starttime);

其时间耗时为30毫秒左右。

但这个例子事实上返回了2次,所以我觉得不能证明闭包更快。例如,常规写法:

function sum(i) {
    i++;
    return i;
}

其耗时就和闭包差不多。我把计算量增加10倍(到一千万次),其时间和上面那个所说的比较慢的方法是差不多的(30ms左右)。

因此个人认为,应该是双重return所导致了耗时增多。

 

 

⑤闭包的利用:

假设我们有N个按钮,想点击第x按钮时,输出x。

var m = function () {
    function cli(i){
        return function(){
            console.log(i);
        }
    }
    for (var i = 1; i < 3; i++) {
        document.getElementById("test" + i).onclick = cli(i);
    }
}
m();

初始时,不输出任何变量。点击id="test1",输出1,点击id="test2",输出2。

 

假如逐个绑定,那么是不能达成这种目的的。例如:

for (var i = 1; i < 3; i++) {
    $("#test" + i).click(function () {
        console.log(i)
    });
}

并不能如我们所愿那样,点击第一个按钮便输出1,事实上,无论点哪个,输出都是3。

 

时间: 2024-12-03 06:52:18

从零开始学_JavaScript_系列(19)——js系列&lt;6&gt;闭包的相关文章

从零开始学_JavaScript_系列(八)——js系列&amp;lt;2&amp;gt;(事件触发顺序、文本读取、js编写ajax、输入验证、下拉菜单)

(20)事件触发的顺序 假如文档中有多个脚本(例如自动执行的脚本),那么他们执行是有一定的顺序的(在HTML文档之中): ①先执行<head>内的<script>标签.因此,这里的js脚本通常是嵌入代码.指向JS文件,可以在这里定义其后要使用的函数: ②其次是执行<body>内的script标签: ③然后是执行触发的脚本内容,例如在button按钮上添加的onclick=""命令,就是在点击后触发的. (21)文档对象模型(DOM) 文档对象模型(D

从零开始学_JavaScript_系列(一)初步概念、工具选择、简单内容输出、加载js文件

(1)适用: HTML和web,可广泛用于服务器,PC,笔记本电脑,平板电脑和手机. 是所有现代浏览器和HTML5的默认脚本语言. 实际写的时候是跟HTML写一起,或者通过HTML来调用的.     (2)本质: 脚本语言. ①轻量级编程语言: ②可插入HTML页面的编程代码: ③插入HTML页面后,可由所有的现代浏览器执行: ④易学.       (3)<script>与</script>标签 HTML的脚本必须位于<script>和</script>之中

从零开始学_JavaScript_系列(15)——js系列&amp;lt;4&amp;gt;(数值、字符串、对象、数组、函数、日期的基本方法)

注:$("#a").text( 内容 ) 是jquery的方法,可以理解为在 <div id="a"></div>  添加内容变为: <div id="a">内容</div> (6)数值 注意,Math的M需要大写 ①绝对值:Math.abc(x)         获得x的绝对值(之后的意思都类似,非特殊不再说明)   ②求四舍五入后结果: Math.round(1.9) = 2; Math.rou

从零开始学_JavaScript_系列(20)——js系列&amp;lt;7&amp;gt;(函数原型的两种声明方式、函数的作用域)

  (20)函数的原型 ①Object.create(obj) 根据原型obj创建一个对象: 例如: var obj = {     a: 1 } var o1 = Object.create(obj); console.log(obj); console.log(o1); 输出为: Object {a: 1} Object {}   ②使用构造函数方法: 使用构造函数,通过new来创建一个实例(之前有,这里略)   ③ ①和②之间的区别: 通过②的继承: var obj = {     a:

从零开始学_JavaScript_系列(15)——js系列&amp;lt;3&amp;gt;(转为字符串,截取字符串)

(29)把json转化为字符串 JSON.stringify(store) 即可(注意JSON要大写),store是json对象     (30)对一个数组赋值 ①假如我们有一个不定长度的数组abc ②我们想要将该数组逐值赋给另一个数组def: ③因此,机智的我们想到了使用for循环: for(var i = 0; i < abc.length; i++) {        def[i] = abc[i]; } ④然而,这样是无法赋值的(原因在于def之前未定义,因此不能直接把他当做一个数组):

从零开始学_JavaScript_系列(16)——js系列&amp;lt;5&amp;gt;(正则表达式)

前注:参考网易云课堂前端的正则表达式内容 (12)正则表达式 ①描述字符串规则的表达式 使用  /规则/  来表示   ②正则表达式.test(字符串) 用于测试正则表达式与字符串是否匹配 返回值是true或者false 注:只需要字符串里包含正则表达式,即返回true,也就是说,假如正则表达式是/123/,字符串是a1234b,那么依然返回true   ③锚点: ^     起始位置(shift+6),/^http:/匹配以https开头的 $     结束为止,/\.jpg$/ 表示以.jp

从零开始学_JavaScript_系列(十)——dojo(3)(GRID表格创建、样式、列宽可变、排序、过滤器)

如果没有接触过dojo,建议阅读: http://blog.csdn.net/qq20004604/article/details/51028702 里面介绍了如何加载dojo.(当然,本篇也考虑了未使用过dojo的人,可以只阅读该链接关于dojo下载的部分,以获得dojo) 关于dojo的下载,请查看: https://dojotoolkit.org/download/ 建议下载FULL SOURCE版 如果需要讨论,请评论.或者站内信,我会尽快回复. (34)gridx gridx系列插件并

从零开始学_JavaScript_系列(二)——弹框及读取、条件判断、事件处理、注释、图片、超链和div

(11)导入js文件的潜在问题 假如有一个 <script src="111.js"> js脚本1 </script> 那么,由于导入了111.js的原因,"js脚本1"事实上是不会被执行的. 准确的说,包含导入js脚本功能的script标签,里面的脚本都不会执行(不能保证全部,但目前学到的都不会被执行,如果有可以执行的,其后会在这里修改说明).   (12)补充说明适应人群 至少要有一点的编程概念,毫无编程概念的话,恐怕是有一些难度的. 由

从零开始学_JavaScript_系列(七)——jquery(复选框及互斥、div块、修改css、标签数组、ajax连续加载)

(17)查看复选框是否被选中 复选框为: <input type="checkbox" name="yingmingliu"id="yingmingliu" value="yingmingliu"/> jq语法为: $("#yingmingliu").is(':checked') 如果选中,返回true,如果没有被选中,返回false.   查看复选框的值: $("#yingmingl