JS之作用域

本文深入探讨JavaScript的作用域概念,包括词法作用域、函数作用域、块作用域和变量提升。同时,详细阐述了闭包的原理,并通过实例展示了如何利用闭包解决实际问题。最后,提供了若干经典的面试题,帮助读者巩固理解。
摘要由CSDN通过智能技术生成

作用域是什么 ?

作用域是一套规则,用于确定在何处以及如何查找变量,如果是查找容器本身,会使用LHS查询;如果是查找变量的值,则使用RHS查询;在非严格模式下,LHS查询失败则会隐式创建全局变量,RHS查询失败则会抛出ReferenceError异常。

词法作用域

词法作用域就是定义词法解析阶段的作用域。词法作用域意味着作用域由书写代码时函数声明的位置决定。思考以下代码:

var a = 2;
function foo() {
	console.log(a);
}
function bar() {
	var a = 3;
	foo();
}
 
// 打印结果显而易见:2
// 印证了上面所说的作用域由函数声明的位置决定。

函数作用域与块作用域

函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用和复用(包括内嵌的作用域)。思考以下代码:

function foo() {
	var a = 1;

	function bar() {
		...
	}
}
// 在foo函数的作用域中任何地方都可以找到并使用a这个变量,即使在bar中也可以使用。
// a、bar属于foo函数的作用域气泡,因此如果在全局访问a活着bar则会抛出ReferenceError错误。

块作用域是指例如for、if、{}等内部形成了一个独立的作用域,在外部无法访问。
在ES6还没出来的时候,块级作用域的实现依赖立即执行函数(IIFE):

(function(){
	var a = 1;
})();

console.log(a);	//ReferenceError
// 这样写的好处可以隐藏和私有化变量,不会污染全局命名

ES6出来后,通过使用let关键字声明变量隐式劫持了所在块的作用域。

for(let i = 0;i < 10; i++) {}
console.log(i);	// ReferenceError
// 如果换成var 则打印出 10;

提升

所有的声明(变量和函数)都会被移动到各自作用域的最顶端(只有声明操作会被提升),这个过程被称为提升。let和var关键字声明提升的区别:var 声明和初始化都被提升,let只有声明提升。这里有一个细节,函数的提升是优先于变量的提升(这个细节可以出现在有多个重复声明的代码中)。思考以下代码:

foo();	// 1

var foo;

function foo() {
	console.log(1);
}

foo = function() {
	console.log(2);
}

// js引擎对于重复声明的标识符,会取第一个忽略后面的。
// 所以编译之后的执行代码如下

function foo() {
	console.log(1);
}

foo();

foo = function() {
	console.log(2);
}

作用域闭包

何谓闭包?当函数能记住并使用所在的词法作用域,就产生了闭包,即使函数是在当前词法作用域外执行。闭包是老生长谈的问题,也是及其重要的特点。且js代码中处处存在着闭包,我们要学习如何识别和拥抱它。
思考下面的代码:

function foo() {
	let a = 123;

	function bar() {
		console.log(a);
	}
	
	return bar;
}
let baz = foo();
baz();	// 123

这就是闭包了,在foo函数执行完毕后,我们期望foo函数内部的作用域已经全部销毁、回收(基于js中存在的垃圾回收机制),但实际上由于bar声明的位置涵盖了foo作用域的闭包,使得该作用域能够一直存活,以供bar在任何时候使用

当然,无论使用何种方式对函数类型的值进行传递,当函数在别处调用时都可以观察到闭包。

function foo() {
	let a = 123;
	
	function baz() {
		console.log(a);
	}

	bar(baz);
}

function bar(fn) {
	fn();
}

现在,我们来看一个非常经典的例子:

for(var i = 0; i < 6; i++) {
	setTimeout(() => {
		console.log(i);
	}, 1000);
}
// 有js开发经验的小伙伴一眼就能看出答案,会打印6个6
// 如何在不使用ES6中的let,让其依次打印0,1,2,3,4,5呢?
// 这里可以使用闭包解决,为每一个i创立一个词法作用域并且产生闭包

for(var i = 0; i < 6; i++) {
	(function (j) {
		setTiemout(() => {
			console.log(j);
		}, 1000)
	})(i);
}

练习题

// 做道经典的面试题,看看自己掌握的怎么样?
function fun(n,o) {
            console.log(o)
            return {
                fun:function(m){
                    return fun(m,n);
                }
            };
        }
        let a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?
        let b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
        let c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值