匿名函数 闭包 JavaScript线程

预解析

所谓预解析,就是在当前作用域之下,在js代码运行之前,会把带有var和function关键字的事先声明,但是不会赋值。

// 一道面试题
alert(a)
a();
var a=3;
function a(){
    alert(10)
}   
alert(a)
a=6;
a();

所考到的知识点有两个:

  • 事先进行变量声明(即预解析,将变量提升到顶部)
  • 函数声明优先于变量声明

所以运行结果就是

  1. 刚开始,a就是function a(){alert(10)},就会看到这个函数

  2. a(),执行函数,alert(10)

  3. 执行了var a=3,所以alert(a)显示3

  4. 然后a就不是一个函数,再往下执行a()就会报错

    //类似相关
    alert(a)
    a();
    var a=3;
    var a=function(){
    alert(10)
    }
    alert(a)
    a=6;
    a();

这里有一个函数表达式,将一个匿名函数(没有名字的函数)赋值给一个变量,此时变量a的值就是指向函数对象的指针,从本质上来说和使用函数声明的方式定义一个有名字的函数是一样的,与第一个的区别就在这里,将函数的声明转化为了这种var型变量的声明,由于预解析只是进行事先声明,但并不进行赋值操作,所以一开始第一个alert(a)显示的是underfind,然后执行a()的时候会报错的原因和刚开始的一样,转化为var型之后就没有函数的声明优先于变量的声明,两个都是变量(即匿名函数声明等同于变量声明),这个时候a就并不是一个函数。

预解析和作用域

//收录一些面试题
var a=0;
function aa(){
    alert(a)
    a=3
}
//结果是什么都没发生,因为要执行aa函数才会执行alert(0)

------------分割线1------------------

var a=0;
function aa(){
    alert(a)
    var a=3
}
aa();
//underfind  在aa函数里面,有var a=3,那么在aa作用域里面,就是把a这个变量声明提前,但是不会赋值,所以是underfind

------------分割线2------------------

var a=0;
function aa(a){
    alert(a)
    var a=3
}
aa(5)
alert(a)
//5,0   在函数体内,参数a的优先级高于变量a

------------分割线3------------------

var a=0;
function aa(a){
    alert(a)
    a=3
}
aa(5)
alert(a)
//5,0   在函数体内,执行alert(a)和a=3,修改的的并不是全局变量a,而是参数a

------------分割线4------------------

var a=0;
function aa(a){
    alert(a)
    var a=3
    alert(a)
}
aa(5)
//5,3
//这个我也有点不理解,请教网上的说法,有两个答案(小伙伴如果知道怎么理解,欢迎在评论上指点)
//1.参数优先级高于变量声明,所以 变量n的声明其实被忽略了,此时相当于
//var a=0;
//function aa(a){
//  var a=5;
//    alert(a)
//    a=3
//    alert(a)
//}
//aa(5)

//2.形参和局部变量优先级一样,此时相当于
//var a=0;
//function aa(a){
//  var a;    先声明
//  a=5      由于形参和变量名称一样,覆盖了!
//    alert(a)
//    a=3
//    alert(a)
//}
//aa(5)

------------分割线5------------------

var a=0;
function aa(a){
    alert(a)
    a=3
    alert(a)
}
aa()
alert(a)
//underfind  3  0 
//首先,参数优先级高于全局变量,由于没传参数,所以是underfind
//a=3,实际上修改的时形参a的值,并不是全局变量a,往下alert(a)也是形参a
//最后的alert(a),你懂的

匿名函数 闭包 JavaScript线程

for (var i = 0; i < 5; i++) {
 setTimeout(function() {
  console.log(i);
 }, 1000);
}
console.log(i);
//这个大家就要小心一点了,答案是5    55555
//在setTimeout执行之前,for循环早就执行完了,i的值早已经是5了,所以一开始是执行,最后面的console.log(i);
//在for循环的时候一下子自定义5个setTimeout,大概一秒后,就是输出55555

首先要得到这个结果就必须对JavaScript线程有一定了解

这道题的考察点:JavaScript的单线程以及setTimeout的异步特性

JavaScript引擎是单线程运行的,浏览器运行期间只有一个线程在运行js程序。浏览器的内核是多线程的,他们在内核控制下相互配合,以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。

  • JavaScript引擎是基于事件驱动单线程执行的,js引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个js线程在运行js程序。
  • GUI渲染线程负责浏览器界面的渲染,当界面需要重绘的时候或者由于某种操作引发回流时,该线程就会执行,需要注意GUI渲染线程与js引擎是互斥的,当js引擎执行的时候GUI线程会被挂起,GUI更新会被保存在一个队列中,等到js引擎空闲时立即被执行
  • 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理的队列末尾,等待js引擎的处理。这些事件可来自js引擎当前执行代码块,如setTimeout,也可来自浏览器内核的其他线程如鼠标点击、ajax异步请求等,但由于js的单线程关系所有这些事件都得排队等待js引擎处理。
  • 当线程中没有任何同步代码的前提下才会执行异步代码。

所以在上题之中setTimeout是异步的代码,即使是setTimeout中设置的等待时间为0也不会立即执行,而for循环代码是同步的,所以要等待for循环执行完以后才会执行setTimeout。

关于为什么都是5:因为setTimeout中的i是对外层i的引用,当setTimeout的代码被解释的时候,运行时只是记录了i的引用,而不是i的值。这样当setTimeout被触发的时候,setTimeout被同时取值,由于他们都是指向了外层的同一个i,而此时i的值是5,所以打印的都是5.

这个问题更深层次的原因:

var s = document.getElementsByTagName('input'); //获取整个网页的标签
    window.onload = function(){
        for(var i = 0;i < s.length;i++){
            s[i].onclick = show(i);
        }
    };
    function show(num){
        alert("你好,请"+num+"号嘉宾领奖")
    }

这个例子实际运行点击无论哪个按钮,都会alert相同的值(s.length)

JavaScript没有块级作用域,用循环赋值给每一个 s[i] 的方法的参数都是一个i的引用,它们都指向同一个也是唯一一个i,所以最终无论点哪个按钮,alert 的都是同一个值,在这个循环中,i的值就等于 s.length。

解决方案:

window.onload=function(){
 for(var i=0;i< s.length;i++){
  s[i].onclick=(function(num){
  return function(){show(num)}
  })(i)//采用的是直接调用函数表达式(免去了赋值再调用)
 }     //需要再函数体外围加上括号,然后再以括号调用
};
function show(num){
 alert("你好,请"+num+"号嘉宾领奖")
}

采用的写法是利用IIFE(立即执行函数)模拟了一个块级作用域(函数有自己的作用域),实际上就是声明一个匿名函数,同时立即执行他自己。

匿名函数可以用来做闭包

finciton F(){
    var name = "a";
    return function(){
        alert(name);
    };
}
var closure = F();
closure();

F() 的返回值是一个内部函数,这个函数可以访问外部函数的作用域,也就是可以访问到外部函数的变量。

注意:因为把返回的函数赋给了一个变量,虽然函数在执行完一瞬间会销毁其执行环境, 但是如果有闭包的话,闭包会保存外部函数的活动对象(变量),所以如果不 closure = null; 把对闭包的引用消除掉,闭包会一直存在内存中,垃圾收集器不会销毁闭包占用的内存。

这个时候可以采用匿名函数 闭包的概念解决这个问题

for (var i = 0; i < 5; i++) {
 (function(j) { // j = i
  setTimeout(function() {
   console.log(j);
  }, 1000);
 })(i);
}
console.log(i); 
//这里的解析和上面基本一样,只是用闭包来记录每一次循环的i,
//所以答案是5     01234



var output = function (i) {
 setTimeout(function() {
  console.log(i);
 }, 1000);
};
 
for (var i = 0; i < 5; i++) {
 output(i); // 这里传过去的 i 值被复制了
}
console.log(i);

//这里的解析和上面基本一样,把i当参数传进output,记录每一次循环的i,
//所以答案是5     01234
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,176评论 5 469
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,190评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,232评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,953评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,879评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,177评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,626评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,295评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,436评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,365评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,414评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,096评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,685评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,771评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,987评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,438评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,032评论 2 341

推荐阅读更多精彩内容