函数作用域的含义:属于此函数的全部变量都可以在整个函数的范围内使用及复用,实际上在嵌套的作用域内也可以使用。

 

1、隐藏内部实现:

对函数的传统认知:先声明一个函数,再向里面添加代码。试想,若从所写的代码中挑选出一个任意的片段,再用函数声明对其进行包装,可以实现将这段代码隐藏起来——这段代码中的任意声明(变量或函数)都会绑定到新的函数作用域中,而不是在先前所在的作用域中。也就是说,可以把变量和函数包裹在一个函数的作用域中,然后用这个作用域来隐藏它们。

1)好处一:将私有的内容隐藏在函数作用域中,防止外部程序有意或无意地篡改。

function calculate(a) {
	b = a + add(a * 2);
	console.log(b * 3);
}
function add(a) {
	return a + 1;
}
var b;
calculate(2); // 21

将变量b和函数add()隐藏在calculate()内部:

function calculate(a) {
	function add(a) {
		return a + 1;
	}
	var b;
	b = a + add(a * 2);
	console.log(b * 3);
}
calculate(2); // 21

2)好处二:避免同名标识符之间的冲突,用名的标识符可能用途不一样,无意间可能造成命名冲突,冲突会导致变量的值被意外覆盖。

function outFunc() {
	function inFunc() {
		i = 10;
		console.log(a + i);
	}
	for (var i = 0; i < 10; i++) {
		intFunc(i * 2);
	}
}
outFunc();

问题:无限循环,i = 10意外地覆盖了for循环中的i。

解决:i = 10这一赋值操作需要声明一个本地变量来使用,如改成var i = 10;或var j = 10;,但是往往在软件设计过程中需要使用同名的标识符,这就需要使用作用域来隐藏同名标识符。

由此引出全局命名空间的概念:

变量冲突的一个典型例子存在于全局作用域中,当程序中加载了多个第三方库时,若没有妥善将内部私有函数或变量隐藏起来,很容易发生冲突。因此,往往在全局作用域中声明一个名字独特的变量,通常是一个对象,这个对象被用作命名空间,所有需要暴露给外界的功能都会成为这个对象(命名空间)的属性。

var globalNameSpace = {
	property1: ...,
	property2: ...,
	func1: function() {
		...
	},
	func2: function() {
		...
	}
};

 

2、函数作用域

var i = 10;
// 添加包裹从此行开始
function func() {
	var i = 5;
	console.log(i); // 5
}
func();
// 添加包裹到此行结束
console.log(i); // 10

用函数声明包裹代码片段的问题:

1) 必须声明一个具名函数func,这个名称污染了其所在作用域(全局作用域);

2) 必须显式地通过函数名func调用这个函数才能运行其中的代码。

理想的情况:

1) 函数不需要函数名;

2) 函数能够自动运行。

解决方案:

采用立即执行的匿名函数表达式:

var i = 10;
// 添加包裹从此行开始
(function() {
	var i = 5;
	console.log(i); // 5
})();
// 添加包裹到此行结束
console.log(i); // 10

函数表达式可以是匿名的,但函数声明不能匿名。

匿名的函数表达式的缺点:

匿名函数在栈追踪中不会显示函数名使得调试很困难;

函数需要引用自身时只能使用arguments..callee引用,如在递归中和事件触发后事件监听器需要解绑自身的情况。

具名的函数表达式与函数声明的区别:

最重要的区别是名称标识符会被绑定到何处。函数声明的名称标识符会被绑定到所在作用域中,可以直接通过名称标识符来调用;函数表达式的名称标识符会被绑定在函数表达式自身的函数中而不是所在的作用域中。 


本文转载:CSDN博客