面试系列——变量提升

本文纯粹是无意间看到知乎一篇前端面试总结,作为面试官角度分析的一篇文章,深有感触。

附上链接:https://zhuanlan.zhihu.com/p/25855075


写给最初的我。

#一个不起眼的问题

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

如果对同步和异步,变量作用域,闭包等概念有正确的理解的话,应该知道正确答案:


如果约定用箭头表示前后两次输出之间有1秒的时间间隔,用逗号表示前后的两次输出时间间隔可忽略,那么实际运行的结果可以描述为:

5->5,5,5,5,5

#追问1:闭包

如果期望输出为5->0,1,2,3,4,如何改造代码?

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

console.log(new Date,i);

还有一个熟悉的陌生人:按值传递,也可实现。

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

最后,熟悉ES6的小伙伴们:

for(let i=0;i<5;i++){
	setTimeout(function(){console.log(new Date,i)},1000);
}
console.log(new Date,i);

上面只改了声明关键词var,用let代替后,会发现运行的结果是这样的(代码的最后一行i未定义):


#追问2:ES6

如果将问题升级,新的需求是期望输出代码为0->1->2->3->4->5,要求原有的循环和两处console.log不变。再精确点描述:代码执行时立即输出0,之后每隔一秒输出1,2,3,4,循环结束后大概在第5秒输出5。

如果不使用ES6的话,下面这个粗暴的方法也是有效的。

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

setTimeout(function(){
	console.log(new Date,i)
},i*1000);
如果了解ES6,那么就会知道其中有个对象是可以完美解决这样的异步调用问题,没错,就是Promise!
const task=[];
for(var i=0;i<5;i++){ //这里的var不能改为let
	(function(ind){
		task.push(new Promise(function(resolve){
			setTimeout(function(){
				console.log(new Date,ind);
				resolve(); //这里一定要有resolve(),
			},ind*1000);
		}));
	})(i)
}

Promise.all(task)
		.then(function(){
			setTimeout(function(){
				console.log(new Date,i)
			},1000);
		});
还是看看运行结果吧:


既然都用了ES6的Promise了,索性就用箭头函数改写一下吧!

const task=[];
for(var i=0;i<5;i++){
	((ind)=>{
		task.push(new Promise((resolve)=>{
			setTimeout(()=>{
				console.log(new Date,ind);
				resolve();
			},ind*1000);
		}))
	})(i);
}
Promise.all(task)
		.then(()=>{
			setTimeout(()=>{console.log(new Date,i)},1000);
		});

进一步用按值传递优化代码:

const task=[];
const input=(ind)=>{
	return new Promise((resolve)=>{
		setTimeout(()=>{
			console.log(new Date,ind);
			resolve();
		},ind*1000)
	})
}
for(var i=0;i<5;i++){
	task.push(input(i));
}

Promise.all(task)
		.then(()=>{
			setTimeout(()=>{
				console.log(new Date,i);
			},1000)
		});

#追问3:ES7

如果Promise已经掌握,想要更近一步操作异步的话(特别是针对重复的回调),ES7中的Async函数会表现得更好。ES7的Async函数可以被认为是异步编程的终极解决方案。其实,从表现形式上看,Async函数就是将Generator函数的星号*替换成async,将yield替换成await。

const sleep=(timeoutMs)=>{
	return new Promise((resolve)=>{
		setTimeout(resolve,timeoutMs);
	})
}
(async () =>{
	for(var i=0;i<5;i++){
		await sleep(1000);
		console.log(new Date,i);
	}

	await sleep(1000);
	console.log(new Date,i);
})();
有没有更简洁一些,且可读性更好一些呢?


猜你喜欢

转载自blog.csdn.net/u012194956/article/details/79878965