一、匿名函数
匿名函数就是没有名称的函数,定义一个普通函数和匿名函数的方法
// 普通函数声明
fn1()
function fn1(){
console.log("Hello")
}
// 以下是匿名函数的创建
// 1 函数表达式 等号右边就是一个匿名函数,把匿名函数赋值给变量
var fn2 = function(){
console.log("Hello")
}
fn2()
// 对应箭头函数的写法
let fn3 = () =>{
console.log("Hello")
}
fn3()
// 2 通过表达式自我执行
(function box() { //封装成表达式
alert('Lee');
})(); //()表示执行函数,并且传参
// 3 函数里的匿名函数
function box() {
return function(){ //函数里的匿名函数,产生闭包
return'Lee';
}
}
alert(box()()); //调用匿名函数
二、闭包
闭包使函数有权访问另一个函数作用域中的变量,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。函数与变量形成的代码块叫闭包
- 优点:就是可以把局部变量驻留在内存中,可以避免使用全局变量,全局变量污染导致应用程序不可预测性。
- 缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。
使用闭包解决setTimeout异步函数console内容
for (var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i)
},i*1000)
}
结果输出的是5个5
下面根据执行机制和作用域链解释这一输出结果
首先,JavaScript是单线程环境,代码从上到下依次执行。这种执行方这也被称作是“同步执行”。(同一时间JavaScript只能执行一段代码,如果这段代码要执行很长时间,那么之后的代码只能尽情地等待它执行完才能有机会执行)。
但JavaScript中引进了异步机制。于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”(task queue)的任务,只有主线程上的任务执行完了,才通知”任务队列”,任务队列中的任务才会进入主线程执行。
setTimeout是异步函数,所以会先把主线程的循环执行完毕再执行setTimeout函数,setTimeout的第二个参数则是把执行代码console.log(i)
添加到任务队列需等待的毫秒数
JavaScript引擎开始执行任务队列中的代码时,会开始在当前的作用域中开始找变量i,但是当前作用域中并没有对变量i进行定义。这个时候就会在创造该函数的作用域中寻找i。创建该函数的作用域就是全局作用域,这个时候就找到了for循环中的变量i,这时的i是全局变量,并且值已经确定:5,所以输出5个5
参考
用setTimeout实现for循环中的计时器
解决办法:把i设在函数的当前作用域
- 使用匿名函数闭包,在函数内部创建了一个函数,并将变量i以函数参数形式传递给内层函数中变量j,j就是这个函数中的局部变量,每次i传入的值不同,局部变量j的值也不同。
(闭包的意义就是对函数类型的值进行传递时,保留对它被声明的位置所处的作用域的值。闭包的本质就是不让变量被栈回收)
for (var i = 0; i < 5; i++){
(function(j){
setTimeout(function(){
console.log(j)
},j*1000)
})(i)
}
- 使用let定义i,当for循环中的i是通过var定义的变量时,作用域是一整个封闭函数,是全局作用域;当i是通过let定义的变量时,将变量固定在在当前循环的块级作用域
for (let i = 0; i < 5; i++){
setTimeout(function(){
console.log(i)
},i*1000)
}
- 使用setTimeout的第三个参数,
setTimeout(function, milliseconds, param1, param2, ...)
,param作为传给执行函数的其他参数
for (var i = 0; i < 5; i++){
setTimeout(function(j){
console.log(j)
},i*1000,i)
}
注意使用上述三种方法输出的是从0到4,而不是到5,因为到i加到5的时候,不满足i<5不进入函数。而原始的结果输出是5个5,因为函数使用的是最后的i值为5
- 间隔时间
delay的时间是1000 * i,等待的时间就会分别变成1000 * 1, 1000 * 2, 1000 * 3……且传入i的值就立即执行,所以每次打印都会有1000ms的时间差
例题
function mo(){
var x = 0;
return function(){
console.log(++x)
}
}
var a = mo();
var b = mo();
a();
a();
b();
输出1 2 1
函数mo()内部的闭包函数会把变量x存在内存中,再次调用同一个mo()时,会用回这个x
闭包实现private私有变量
通常私有变量约定为以下划线_
开头
var Person = function(name){
var _name = name
this.getName = function(){
return _name
}
}
var p1 = new Person("ls")
console.log(p1._name) // undefined
console.log(p1.getName()) // ls
这种方式的优点是实现了私有属性的隐藏,Person 的实例并不能直接访问_name属性,只能通过特权函数getName获取: