所有的 for if switch while do 等等,都属于块级作用域,里面声明的对象,外面也能访问。但function 函数里的作用域,在函数外是访问不到的。
但函数作用域里面可以访问外面。
内层永远可以访问外层的。
这就叫闭包,同时是一个立刻执行的匿名函数。
你给他加个名字也可以,不过这里可以省略名字,闭包中的函数可以匿名。
把一个函数的声明,用 () 括起来表示调用这个函数,然后再跟一个(),表示执行这个函数。等于把一个函数的声明和调用一句代码完成了。
所以这里,这个函数执行的时候,b 还是会报错, done不会出来。
我们改写一下,用一个setTimeout 把刚才的函数传进去,不需要设延迟,也就是说立刻执行它。
虽然报错,但是done打印出来了,也就是说,带有错误的代码执行了,后文的代码也执行了。
这就叫异步,setTimeout中的代码,等于另开了一个线程去执行它的上下文,而后面的代码不会等待setTimeout里的代码执行完,而是立刻继续往后执行。
本来是同步顺序执行,遇到setTimeout后就分叉了,两条路同时开始执行,这就叫异步。
改成上图这样,timer done 同样是打印不出来的。因为在另一条路setTimeout的函数中,代码依然按照同步顺序来执行。遇到错误就退出了。但仅仅只是setTimeout里的程序退出了,原本的程序继续运行。
试想一下,我如果想在timeout的异步程序执行结束之后,做一点什么操作,怎么判断它执行完了呢?
比如,现在有一个程序可以帮我运算出sum,但是它可能需要点时间才能算好,我怎么知道在什么时候才能得到sum的值?从而进行下一步操作?(因为异步执行,如果不等setTimeout执行完就打印sum,它还并没有赋值)
我们让这个程序运算的久一点,模仿一个复杂的程序。
此时,我们可以用一个回调函数。
我们加一个参数,这个参数是一个函数,这个函数在异步程序执行到最后会运行。我们在调用这个程序时,传入一个函数,这个就叫回调函数。它是在异步程序执行完后,能反馈给你结果的函数。
这个函数是在下面调用程序时声明的,然后它会在异步程序执行完时调用运行。所以当这个函数运行的时候,前文的sum是已经算完了。
我们再改造一下,程序功能要完成,不需要在外面额外声明一个sum。我希望只要传给程序两个值,它就能反给我结果。于是可以把结果,作为参数传给回调函数,这样我们调用时就可以直接拿来用。
记住 js 中有几个原生的异步方法:setTimeout、setInterval、ajax,以后还有更多,目前只用记住这三个。
jq的$.get post ajax 等等都是异步程序,异步程序必须要在回调中执行等待结果的代码。当用一个异步的程序时其实就是下图这个意思。
要相信 异步程序 这个小伙伴,它处理完后一定会帮你做你交代的事情的。
这比起setInterval来判断异步程序执行完毕的优势在于不用开另一个异步程序,并且很及时,不用考虑轮询的频率,和时间误差。不用考虑是否要关闭轮询的异步程序。
我们不用回调,还有一种写法,就是使用事件。事件的机制比喻一下就是:在异步程序执行完以后,大声广播,我执行完了。再给某些对象或者dom绑定一个事件监听,它听到“执行完了”以后就执行自己的程序。
回调和事件,各有好处,不分高低。不同的情形下用不同的机制。回调简单易用,但增加了耦合性,事件的好处是,监听者和广播者可以互不干涩,只管做自己的事。你做完了自己的事只用喊一声,“我做完了”,就不用管别人,其他人听到你做完了以后,有需要触发自己的程序的,都是他们自己的事了,他们自己会知道怎么做。
回调:主程序需要告诉异步程序,你完成以后需要做什么。
事件:监听者只用告诉触发者,你完成以后只用大声喊,不用做什么,我会听到。
事件的机制使每个程序只用关心自己的事,在听到不同的广播时做不同的事就行。事件主要要做两个操作,给谁绑事件,及谁来监听事件,再就是谁来触发事件,即谁来广播事件。
简单来说,回调和事件的区别,就是线程结束之后,交代的事情由哪个线程做,是不同的。回调是把事情交代给别的线程做,而事件是等别的线程结束之后自己做。
最后,感谢大神CX的讲解。