天天看点

JS高程 -- chapter( 函数表达式 )

递归

var factorial = (function f(n){
	if(n<=1){
		return 1;
	}else{
		return n*f(n-1);
	}
})
           

上面这段代码使用具名函数表达式来创建递归函数,保证

factorial

变量即使指向别的对象也能正确执行递归。

( )

中是什么,它就返回什么,若是函数,则其命名在全局是无法访问的,只有赋值给一个变量才能访问( 参考立即执行函数 )

闭包

闭包 是指有权访问另一个函数作用域中变量的函数

作用域链细节:

  1. 函数 创建 的时候,会创建一个执行环境( 执行上下文 ),以及确定作用域链,创建 变量 对象
  2. 函数 执行 的时候, 变量 对象转换为 活动 对象,

    arguments

    对象和函数内声明的 变量 的赋值,初始化 活动 对象
  3. 作用域链中,当前函数的 活动 对象 处于链上的最前端,然后依次是上一级函数的 活动对象,… 最后直到全局执行环境

当函数执行完毕时,函数中的变量都会被销毁,只保留全局作用域的 活动对象,但是由于闭包函数的作用域链上拥有对其链上一系列函数的 活动 对象的引用,所以这些函数的

活动 对象

不会被销毁,会被保留在内存中

闭包与变量

闭包保存的是整个 活动 对象,而不是某个变量

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

这道经典的面试题,其会每秒打印一个

5

,原因就是 其实 定时器里的函数就是一个闭包,它会在同步代码执行完毕之后执行,其作用域链中保存了当前函数环境的 活动对象,其中

i

的值在当前函数执行结束之后变为了

5

,然后定时器闭包函数开始执行,然后就会每秒打印一个

5

想要获得

0, 1, 2, 3, 4

这种打印结果,就得要定时器函数能够记住当前

i

的 值,可以使用闭包解决( 函数的参数都是值传递的 )

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

关于

this

this

指向是在函数执行的时候才确定的,所以使用闭包的时候 可能出现问题,只要没有显示的调用者,或者使用

apply, call, bind

指定调用者,则

this

都会指向全局对象

模仿块级作用域

ES6 之前,js 是没有块级作用域的,这意味着在

if..else、for、while

等语句中声明的变量会成为当前函数或者全局的变量,即在这些代码块之外的地方,也能访问到在块中定义的变量

变量提升

JS 会在函数执行的时候将函数声明,变量声明提升到函数的最顶部先执行( 使用

var

声明 ),函数优先,如果遇到相同的名字,则会忽略后一个,但是如果在声明的地方有赋值操作,赋值操作还是会进行,只不过声明被忽略

可以使用匿名函数来模仿块级作用域

(function(){
	// 这里的 a 不会被外界访问到
	var a = 1
})()
           

函数表达式后面可以跟圆括号,可以使用圆括号将匿名函数体包裹起来转换成函数表达式

(function(){ }) // => 函数表达式