2018-12-20


闭包前言--异步编程

进程:进程是操作系统分配资源(时间片)的最小单位
线程:线程是进程中一个概念 (线程是程序执行的最小单元)
CPU:单核的CPU,同一时间只能运行一个线程,平常我们的电脑上多个软件看似并发的运行(但其实不是,只是各个线程的时间切换很快,我们无法察觉而已)。
浏览器是多进程多线程的,js引擎是单线程的(同一时间只能做一件事情),js引擎是浏览器的一个线程。


回调函数:

    一般回调函数都是一个匿名函数
    我们只想要定义回调函数  不需要调用
    最终函数会被js引擎自动调用(带条件)
        条件: 时间到了   用户触发了一些行为
同步回调函数
    立马执行   只有等同步回调函数执行完毕  后续代码才可以继续执行
    arr.map(function(item,index,arr){})
异步回调函数
    setTimeout(function(){},time)

生命周期

作用域的生命周期
---函数定义时被创建
---浏览器关闭时被销毁
执行上下的生命周期
---函数调用时被创建
---函数调用完毕闭包被释放,下一轮垃圾回收机制运行时被销毁
闭包的生命周期
---闭包被创建的前提条件:函数嵌套,内部函数使用了外部函数的变量
---外部函数被调用时,闭包被创建
---当内部函数设置为null时,闭包被释放,下一轮垃圾回收机制运行时被销毁

setTimeout(function(){
},time)

1.当js引擎遇到定时器时,会通知异步线程来管理定时器,js继续向下解析定时器以外的代码。
2.通知异步线程后,异步线程等待定时器设置的time时间后,将定时器中的函数放进异步队列中。
3.js引擎(主线程)执行完所有的同步操作,主线程空闲后就去异步队列中轮询,并将异步队列中的回调函数取出来执行。


异步编程面试题

<script>
   for(var i=0;i<5;i++){
       setTimeout(function () {
           console.log(i)
       },1000)
   }
</script>

解析:
1.js引擎第一次解析到setTimeout处,通知异步线程来管理当i=0时的function(回调函数),暂且称作f0,异步线程需等待1s钟将回调函数放到异步队列中
2.js引擎继续解析,发现又是一个setTimeout,于是执行第一步一样的步骤(f1~f4)
3.js执行代码的速度远远小于1s,当异步线程还没有将回调函数放入异步队列中时,js引擎已经将代码执行完毕,那么它就是队列中轮询,没有回调函数就空轮询,直到1s后,异步线程将回调函数放入队列中,js引擎就将回调函数取出来执行。(注意:队列是先进先出,每个回调函数进队列的时间间隔可忽略不计,因为时间间隔很短,差不多同时)。
4.还值得注意的是,回调函数中并没有对i进行声明,所以根据作用域链的规则,i的值使用的是全局的变量i(也就是for中定义的i)),当回调函数还没有进队列时,for中变量i已经变为了5
5.最终1s后输出5个5.
--
改造一:

<script>
   for(var i=0;i<5;i++){
       setTimeout(function () {
           console.log(i)
       },1000*i)
   }
</script>

解析:解析方法与前面一致,当这里的time变为了1000*i,于是每次的时间间隔不同了(0s,1s,2s,3s,4s),时间间隔为1s,第一个因为是等待0s,所以可以很快的输出,剩下的隔1s输出一个5.

--

IIFE(立即可执行函数表达式)

IIFE的组成部分:
1.第一个()中包含的是匿名函数function(){}
2.第二个()包含的是外部变量来传参(实参),传给匿名函数的形参,当js引擎遇到最后一个()时,那么IIFE就立即被执行。
--
IIFE的特点:
当函数变为立即可执行函数表达式(IIFE)时,外部是不能访问IIFE的,这样避免了外界对IIFE中变量的访问,也保证了IIFE中的变量不会污染全局。
--
改造二:

<script>
   for(var i=0;i<5;i++){
       (function(j){    //相当于 var j = i;
        setTimeout(function () {
           console.log(j)           //1s后输出0,1,2,3,4
       },1000)
       })(i)              //到最后的()处,就开始执行立即可执行函数的代码 
   }

解析:
1.js引擎首先解析到for循环,i=0,将i的值传递给IIFE的形参j,j的值为0,遇到setTimeout后,异步线程拿着回调函数等待1s,1s后将函数放到异步队列中。
2.因为IIFE的特性,IIFE中的变量是不会污染全局的,所以j的值每次都是i传给它的值,在异步队列中会产生5个回调函数,各个回调函数中的j值都不相同。
3.1s后,输出0,1,2,3,4


闭包

闭包的创建时间:
当外部函数的执行上下文被创建时(外部函数被调用时)
--
创建闭包的必要条件:
1.函数嵌套
2.内部函数有使用外部函数的变量
( //下面两句是装逼时使用的:)
1.函数产生了嵌套,内部函数使用了外部函数的变量 就会产生闭包
2.当函数可以记住并访问自己的作用域链时就会产生闭包
)
--
闭包存放的位置:
闭包放在内部函数的作用域中(在执行上下文还没有被创建时)
--
闭包的作用:
延长变量的生命周期
--
闭包的缺点:
闭包一般不会主动销毁,那么它所带来的问题:
1.内存泄露(被占用   无法释放)
2.内存溢出(使用的内存比分配的内存多)
销毁闭包的方法:
将内部函数置为null.
红色警报:下面这句话非常重要:
闭包和内部函数是一一对应的关系(外部函数每被调用一次就产生一个闭包,切记!!切记!!)


闭包面试题:

<script type="text/javascript">
  /*
   说说它们的输出情况
   */

  function fun(n, o) {    
    console.log(o)
    return {   
      fun: function (m) {
        return fun(m, n)
      }
    }
  }
  var a = fun(0)   //产生c0闭包,n=0
  a.fun(1)        
  a.fun(2)
  a.fun(3)   //undefined,0,0,0

  var b = fun(0).fun(1).fun(2).fun(3)   //undefined,0,1,2

  var c = fun(0).fun(1)
  c.fun(2)
  c.fun(3)   //undefined,0,1,1
</script>

解析:
1.fun(0)调用完后,返回一个对象:
{
fun: function (m) {
return fun(m, n)
}
}
并且将这个对象的引用地址赋值给a
2.这道题满足闭包的必要条件--函数嵌套,内部函数使用了外部函数的变量,所以当外部函数时(fun(0))被调用时,闭包n已经产生,保存在内部函数的作用域中。
3.这题的注意点:每次调用外部函数时,都会产生一个闭包,每个闭包都是不同的;每次调用内部函数时,都会产生一个执行上下文环境,每个执行上下文环境是不同的。
解析图:

image.png

第一个输出:
全部是对象a对内部函数fun的调用,所以永远都是使用的A0闭包,那么n的值永远都是0,所以答案是undefied,0,0,0
第二个输出:
相当于以下形式
/*
对象a = fun(0)
对象b = 对象a.fun(1)
对象c = 对象b.fun(2)
对象d = 对象c.fun(3)
/
所以答案是:undefuned,0,1,2
第三个输出:
相当于以下形式
/

对象a = fun(0)
对象b = 对象a.fun(1)
对象b = 对象b.fun(2)
对象b = 对象b.fun(3)
*/
所以答案是:undefined,0,1,1


鸡肋闭包

变量得到作用域链与闭包同时存在

<script>
   function wrap() {
         var a="a-val";
         function inner() {
             console.log(a);   //变量a通过作用域链与闭包都能找到
         }
         inner();    //return inner;
     }
     wrap();
</script>

原型&原型链

原型.png

经典的原型链图谱.jpg

规则:
---显示原型:所有的函数都有一个显示原型
---隐式原型:所有的对象都有一个隐式原型
---所有对象的隐式原型指向其构造函数的显示原型
---大写的Function构造函数的proto(隐式原型)指向本身的prototype(显示原型)
---所有原型对象看成一个{},所有的原型对象的proto指向object.prototype
---object.prototype__proto__(object.prototype的隐式原型)
恒等于(===)null,null(原型链的头)
---所有的原型对象都有一个constructor属性指向原函数


原型链(原型)的作用

原型   服务于属性的查找与设置

--
属性查找:
---先在属性的直接对象中找,如果找到直接返回
如果找不到,上原型链上面找(只有隐式原型链,没有显示原型链),如果找到就返回
如果在隐式原型链上没有找到对应的显示原型,就返回undefined


image.png

--
属性设置
---永远只影响对象的直接属性,跟原型链没关系
如果对象中有此属性则直接将此属性的属性值改掉
如果对象中没有此属性则将此属性添加至对象中

 <script>
        Object.prototype.name="siri";   //所有的对象都可以继承Object.prototype的属性和方法
        var obj = {

        }
         //obj对象中没有name属性,所以obj.name操作是将name属性及属性值添加至obj对象中,此过程并没有影响原型链
        obj.name = "tom";      
        console.log(obj);
        console.log(Object.prototype);
    </script>

image.png

方法重写

默认的数组的原型链:
arr.proto ——>Array.prototype
Array.prototype__proto__ ——>Object.prototype
Object.prototype__proto__ ——>null
Array.prototype ——>Object.prototype——>null就是原型链

 <script>
        var arr = [1,2,3]
        //方法的重写,原本的toString方法被改写掉了
        console.log(arr.toString());   
        console.log(Object.prototype.toString.call(arr));
    </script>

image.png

怎么判断一个对象是不是数组?(面试题)

--
使用isArray()API来判断

 //isArray()API兼容移动端不太友好
        var arr = [1,2,3];
        console.log(Array.isArray(arr));

--
使用Object.prototype.toString()方法来判断

//使用Object.prototype.toString()方法
        function isArray(obj){
            //调用toString()方法的返回值是[object type],type视传入的参数而定
           return Object.prototype.toString.call(obj).indexOf("Number") === 8;
        }
        console.log(isArray(12) );

--
使用instanceof来判断

 /*
        使用instanceof来判断
        a(对象) instanceof b(构造函数)
        */
        console.log([] instanceof Array);   //true

instanceof的深度解析
a(对象) instanceof b(构造函数):查找构造函数的显示原型(b.prototype)是否出现在a的隐式原型链上(a.proto)
注意点:instanceof的对象(也就是a)一般不写基本数据类型来判断
--
改写instanceof规则

 <script>
        function Person(){

        }
        var p = new Person()
        p.__proto__ = Array.prototype;      //将p的隐式原型(b.__proto__)指向Array的显示原型(Array.prototype)
        console.log(p instanceof Array);   //true
        console.log(p instanceof Person);   //false
    </script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 五十三:请解释 JavaScript 中 this 是如何工作的。1.方法调用模式当一个函数被保存为一个对象的属性...
    Arno_z阅读 577评论 0 2
  • Lur算法 关于Android的三级缓存,其中主要的就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LruC...
    Timper阅读 272评论 0 0
  • 爱美是每个女人的天性,美丽的头发,姣好的面容,苗条的身材,白皙的皮肤……每个女人都希望自己拥有最青春靓丽的外表,而...
    语文墨芯阅读 169评论 0 0
  • 今天站桩的时候感受到了胃里有一块很硬的东西,看到它的感受是委屈,焦虑,身体流来的时候有些对抗胃的不舒服,接着让意识...
    木子的芽阅读 205评论 0 0