每天积累一丢丢(Fri, 6 May 2016)

再谈闭包,闭包为什么可以保存变量以及参数的原理。

首先复习一下execution context这个object创建的时候都做了啥:

executionContextObj = {

   variableObject: { /* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */ },
   scopeChain: { /* variableObject 以及所有父执行上下文中的variableObject */ },
   this: {}
 }

而variableObject中包括活动对象activation object(http://programmers.stackexchange.com/questions/189967/what-is-an-activation-object-in-javascript)以及全局对象global object。比较重要的一点是:全局变量对象始终存在,局部环境的变量对象函数中用完即销毁除非这个变量对象还在被引用或者说它的引用计数不为0. 

还拿下面这个小闭包的例子做分析

for (var i = 0; i<5; i++) {
  (function () {
    var a = i;
    console.log(i)
  setTimeout(() => {
    console.log(i);
  })
  })()
}

做了闭包的情况下,这个IIFE里的setTimeout的callback在执行的时候会创建execution context:(不考虑console.log本身的情况)

variableObject 没啥关系,不分析                  | 2:-----指向----->全局变量对象: i (最外层)

scopeChain ---------会指向------>  作用域链:  |1:-----指向------> IIFE的活动对象:arguments(没有), a(指向i).

this 没啥关系,不分析                                   | 0:-----指向-----> 没有啥东西.

这么看的话,callback function需要用到a, 而a又需要i, 所以每个i的值都需要被引用(所以引用计数不为0)所以就这么被保存下来。

不过研究到这里似乎又出现了一个大大的问题,不是说赋值基本类型值的时候不是指针吗啊啊啊?为什么书上这里写的是箭头???

于是我又写了两个小例子:

var a = 2;
function foo() {
  var b = a;
  a++;
  (function foo1() {
    console.log(a);
    console.log(b);
  })()
}
foo(); // 输出结果为:3和2</span></span>

如果这么写:

var a = [1,3];
function foo() {
  var b = a;
  a[0]++;
  (function foo1() {
    console.log(a);
    console.log(b);
  })()
}
foo(); // 输出结果为 [2,3] 和 [2,3]</span></span>

所以其实书里用箭头有那么一点点误导,所以我的理解是:如果采用闭包,activation object中的基本类型值(number string boolean)仍旧采用复制一块新内存的方式来保存,例如上面第一个小程序,每次IIFE都在新的memory中复制出了一个当前循环中i的值来供里面的setTimeout的callback来用,这几个memory对应的引用计数都为1,所以不会被当做垃圾被回收掉。第二个例子中使用的是array,是assign一个引用类型值(object),这个时候,我们复制的其实是复制了一个指针存在memory中并指向那个array,所以a的第一项变了,那么b也跟着一起变了。

下面的这个例子呢?

var arr = [0, 5];
for (arr[0]; arr[0]<arr[1]; arr[0]++) {
  (function () {
    var a = arr;
    console.log(arr)
  setTimeout(() => {
    console.log(a[0]);
  })
  })()
}

这一次的输出结果呢?5次5!!!!!利用闭包存储的这个a指针仍然存在,只不过它指向的这个本来是[0,5]的数组变成了[5,5]; (然后... 什么? 怎么是5? 为什么不是4?思考一下还是挺有意思的)

关于运算优先级的问题:

突然想起一个特别有意思的问题,

var foo = {a:1}
var bar = foo;
foo.b = foo = {c:2}
console.log(foo.b);
console.log(bar.b);

结果是undefined和object{c:2}

感觉如果不了解运算机制可能很难推导出正确的结果。

通常计算过程是从右向左的,但是如果有 ‘.‘ 的话,会优先 ‘.’ 的部分,因此在

foo.b = foo = {c:2}

的时候,会优先生成b这个foo所指向的object(即{a:1})的property,因此这个b的出现导致 {a:1}变成了{a:1, b:undefined}    ----》然后开始进行连续赋值的过程:第一步就是让a指向{n:2}, 然后再让a.x指向{n:2}, 可是这个时候由于上一步的时候已经将foo.b解析为{a:1, b:undefined}这个object的b, 所以第二个赋值过程(也就是第一个等号)实际上是把{c:2}赋值给{a:1,
b:undefined}的b然后变成了{a:1, b:{c:2}}

因此 console.log(foo.b)的时候解析foo.b发现并不存在, 所以是undefined,

而console.log(bar.b),由于b始终指向{a:1}(后来变成了{a:1, b:{c:2}}),因此输出为 object {c: 2}

总而言之,当给一个‘指向object的variable来增加/改写这个object的property’的时候,始终要记得增加改写的其实没有变过,都是那个object本身。

另外很重要的是,遇到连等这种赋值,如果有‘.‘ 这种操作符,会优先并且只进行一次解析,就像小程序中foo.b被解析为指向{a:1}这个object,就不会更改了(应该是? 但如果以后看到黑魔法我回来做update)。

时间: 2024-11-08 21:48:53

每天积累一丢丢(Fri, 6 May 2016)的相关文章

关于第二类斯特林数的一丢丢东西

关于第二类斯特林数的一丢丢东西 第二类斯特林数 S(n,m)表示有\(n\)个有区别小球,要放进\(m\)个相同盒子里,且每个盒子非空的方案数 考虑一个很容易的递推: \[S(n,m)=S(n-1,m-1)+m*S(n-1,m)\] 考虑组合意义: 假设前面的\(n-1\)个球丢进了\(m-1\)个组,因为每个组非空,所以这个球只有一种选择--自己一组 如果前面的球已经分成了\(m\)组,那么,这个球就有\(m\)种放法 所以这个递推式就是这样来的 那么,只考虑组合意义可不可以算? 当然是可以的

每天都有那么一丢丢成就感,生活就多了很多幸福感

扇贝单词 我从2016年6月开始使用扇贝单词背单词,但那时只是想起来就背,应该从来就没有满勤打卡的时候.真正把背单词这件事坚持下来,应该是从2017年6月开始,逐渐打卡记录多了起来,第一个满勤打卡发生在2017年8月.我每天要求自己背50个单词,大概10分钟左右的时间.我将背单词的时间固定在每天早上上厕所的时候同时进行,别人上厕所刷朋友圈,看电视剧,我就背单词.如果上厕所的时间背不完,我就上班坐轻轨的路上把剩下的单词背完.从上轻轨到下车,15分钟左右,一定可以背完本天的任务.一开始自己督促自己背

关于线性基的一丢丢理解

线性基 有趣的东西 在某次考试时人人都切了一道题时才发现我没学过线性基... 是什么 我感觉它就是一个类似于向量基底的东西 线性基中的元素任选几个异或起来是可以表达出原数组中的所有的值的,并且不能搞出其它的数 性质 线性基无论怎么选集合,只要是非空的,异或起来一定不是\(0\) 线性基二进制最高位互不相同 线性基中元素互相异或,异或集合不变 线性基异或出原数组的异或方案唯一 满的线性基可以表示出所有正整数,准确来说是\(2\)的长度次方减\(1\) 求法 首先线性基中的一个元素\(a[i]\)的

影院售票系统-----一个让你有成就感的小项目,只有一丢丢哦

先来点废话,刚拿到这个项目的时候你是怎么想的,上来就闷头写,还是构想思路......   项目需求:     影院售票系统可以销售每天不同时段的电影票,不同时段的电影票有不同的优惠政策,还有使用兑换券的免费票.选择某个时段的一场电影,     单击放映厅未售出的座位并选择一个种类的电影票,创建电影票,计算价格并打印,座位设置为红色表示已经售出.   思想:    使用面向对象思想编写影院售票系统   看完这个你有没有点思路呢,我想你是有的 ...... 下面先来点图片,先刺激一下视觉:  窗体搭

Day 22 生成器yield表达式及内置函数(一丢丢)

本日知识点: ################################### #一.上节课复习:在for循环式,调用对象内部的__iter__方法, # 把他们变成了可迭代对象然后for循环调用可迭代对象的__next__方法去取值, # 而且for循环会捕捉StopIteration异常,以终止迭代 ################################### # def func(n): # while n > 0: # yield n # print("*"

面向对象中一丢丢小知识点

1.使用 Function 创建函数与原来的方式创建函数: Function 是使用字符串构建函数, 那么就可以在程序运行过程中构建函数 将数组形式的字符串, 转换成数组对象 var arr = ( new Function( 'return ' + str + ';' ) )(); 2.eval();不常用,易受恶意攻击.用()将其转换成表达式,{}不再是语句块,转换成代码块而是对象了 3.变量名提升:预解析(标记声明).代码执行.调用 4.函数声明与函数表达式有区别:函数声明是单独写在一个结

Hive(2)-Hive的安装,使用Mysql替换derby,以及一丢丢基本的HQL

一. Hive下载 1. Hive官网地址 http://hive.apache.org/ 2. 文档查看地址 https://cwiki.apache.org/confluence/display/Hive/GettingStarted 3. 下载地址 http://archive.apache.org/dist/hive/ 4. github地址 https://github.com/apache/hive 5. 也可以使用我准备好的安装包 https://pan.baidu.com/s/1

关于java线程池的一丢丢

线程池应用达到的目的 1.降低资源消耗:可以重复利用已创建的线程从而降低线程创建和销毁所带来的消耗. 2.提高响应速度:当任务到达时,不需要等线程创建就可以立即执行. 3.提高线程的可管理性:使用线程池统一分配.调优和监控. 线程池实现原理          1. 最核心的ThreadPoolExecutor类,ThreadPoolExecutor.AbstractExecutorService.ExecutorService和Executor几个之间的关系 public class Threa

懒妹子,第一次开始想写博客,有那么一丢丢小激动呢 ememem--进入正题,分享关于个别iphoneX输入框失去焦点的bug

近来iphone新机铺面而来,作为一个自己没有iphone最新机的小前端,也只有羡慕的份啦,不过呢代码还是要码的,新机的bug还是要改滴,iphone x系列包括xs xr,当input获取焦点后键盘弹起,页面随着键盘向上滑动,一切正常,然鹅,当输入完,点击手机键盘自带的完成按钮,或者是你要进入下一步操作,例如登录的时候,输入框自然就失去了焦点,问题来了,页面并没有随着键盘滑动下来,且页面的点击事件也触发不了,个人想到的解决办法,直接贴代码 //原生写法 document.documentEle