你准备好了吗?我们现在要开始了。
每个题目都有一个代码片段,你需要说出这段代码的输出是什么。
在说闭包之前,我们必须了解作用域的概念,它是理解闭包的基石。
此代码段的输出是什么?
var a = 10
function foo(){
console.log(a)
}
foo()
这很简单,相信所有人都知道输出结果是10。
当执行 console.log(a) 时,JavaScript 引擎将首先在函数 foo 创建的本地范围内查找 a。当 JavaScript 引擎找不到 a 时,它会尝试在其外部作用域(即全局作用域)中查找 a。然后事实证明a的值为10。
var a = 10
function foo(){
var a = 20
console.log(a)
}
a = 30
foo()
在这段代码中,变量 a 也存在于 foo 的范围内。所以当执行 console.log(a) 时,JavaScript 引擎可以直接从本地作用域获取 a 的值。
所以输出是 20 。
记住:当 JavaScript 引擎需要查询一个变量的值时,它会首先在本地范围内查找,如果没有找到该变量,它会继续在上层范围内查找。
var a = 10
function foo(){
console.log(a)
}
function bar() {
var a = 20
foo()
}
bar()
这个问题容易出错,也是面试中经常出现的问题,你可以考虑一下。
简单地说,JavaScript 实现了一种名为词法作用域(或静态作用域)的作用域机制。它被称为词法(或静态),因为引擎仅通过查看 JavaScript 源代码来确定范围的嵌套,无论它在哪里调用。
所以输出是 10 :
如果我们将代码片段更改为:
var a = 10
function bar() {
var a = 20
function foo(){
console.log(a)
}
foo()
}
bar()
输出是什么?
foo 范围成为 bar 范围的子范围:
当 JavaScript 引擎在 Foo 作用域中没有找到 a 时,它会首先从 Foo 作用域的父作用域,也就是 Bar 作用域中寻找 a,它确实找到了 a。
所以输出是 20:
好了,以上就是关于范围的一些基本挑战,相信你能顺利通过。现在我们开始进入闭包的部分。
function outerFunc() {
let a = 10;
function innerFunc() {
console.log(a);
}
return innerFunc;
}
let innerFunc = outerFunc();
innerFunc()
输出是什么?这段代码会抛出异常吗?
在词法范围内,innerFunc 仍然可以访问 a,即使在其词法范围之外执行。
换句话说,innerFunc 从其词法范围中记住(或关闭)变量 a。
换句话说,innerFunc 是一个闭包,因为它在变量 a 的词法范围内关闭。
因此,这段代码不会抛出异常,而是输出 10。
(function(a) {
return (function(b) {
console.log(a);
})(1);
})(0);
此代码片段使用 JavaScript 立即调用函数表达式 (IIFE)。
我们可以简单地将这段代码翻译成这样:
function foo(a){
function bar(b){
console.log(a)
}
return bar(1)
}
foo(0)
所以输出是 0 。
闭包的一个经典应用是隐藏变量。
比如现在要写一个计数器,基本的写法是这样的:
let i = 0
function increase(){
i++
console.log(`courrent counter is ${i}`)
return i
}
increase()
increase()
increase()
可以这样写,但是在全局范围内会多出一个变量i,这样就不好了。
这时候,我们可以使用闭包来隐藏这个变量。
let increase = (function(){
let i = 0
return function(){
i++
console.log(`courrent counter is ${i}`)
return i
}
})()
increase()
increase()
increase()
这样,变量 i 就隐藏在局部范围内,不会污染全局环境。
let count = 0;
(function() {
if (count === 0) {
let count = 1;
console.log(count);
}
console.log(count);
})();
在这个代码片段中,有两个 count 的声明和三个 count 的用法。这是一个难题,你应该仔细考虑。
首先,我们要知道if代码块也创建了一个局部作用域,上面的作用域大致是这样的。
或在此图中:
所以输出是 1 , 0 :
function cr
eateCounter(){
let i = 0
return function(){
i++
return i
}
}
let increase1 = createCounter()
let increase2 = createCounter()
console.log(increase1())
console.log(increase1())
console.log(increase2())
console.log(increase2())
这里需要注意的是,increase1和increase2是通过不同的函数调用createCounter创建的,它们不共享内存,它们的i是独立的,不同的。
所以输出是 1 , 2 , 1 , 2 。
function createCounter() {
let count = 0;
function increase() {
count++;
}
let message = `Count is ${count}`;
function log() {
console.log(message);
}
return [increase, log];
}
const [increase, log] = createCounter();
increase();
increase();
increase();
log();
这段代码很容易理解,但是有个陷阱:message其实是一个静态字符串,它的值固定为Count为0,当我们调用increase或者log时不会改变。
所以每次调用 log 函数,输出结果总是 Count is 0 。
如果您希望 log 函数及时检查 count 的值,请将 message 移入 log :
function createCounter() {
let count = 0;
function increase() {
count++;
}
- let message = `Count is ${count}`;
function log() {
+ let message = `Count is ${count}`;
console.log(message);
}
return [increase, log];
}
const [increase, log] = createCounter();
increase();
increase();
increase();
log();
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 0)
}
输出是什么?
上面的代码等价于:
var i = 0;
setTimeout(function(){
console.log(i);
},0)
i = 1;
setTimeout(function(){
console.log(i);
},0)
i = 2;
setTimeout(function(){
console.log(i);
},0)
i = 3;
setTimeout(function(){
console.log(i);
},0)
i = 4;
setTimeout(function(){
console.log(i);
},0)
i = 5
而且我们知道JavaScript会先执行同步代码,然后再执行异步代码。所以每次执行console.log(i)时,i的值已经变成了5。
所以输出是 5 , 5 , 5 , 5 , 5 。
如果我们想要代码输出 0 , 1 , 2 , 3 , 4 ,需要怎么操作?
使用闭包的解决方案是:
for ( var i = 0 ; i < 5 ; ++i ) {
(function(cacheI){
setTimeout(function(){
console.log(cacheI);
},0)
})(i)
} ;
上面的代码等价于:
var i = 0;
(function(cacheI){setTimeout(function(){
console.log(cacheI);
},0)})(i)
i = 1;
(function(cacheI){setTimeout(function(){
console.log(cacheI);
},0)})(i)
i = 2;
(function(cacheI){setTimeout(function(){
console.log(cacheI);
},0)})(i)
i = 3;
(function(cacheI){setTimeout(function(){
console.log(cacheI);
},0)})(i)
i = 4;
(function(cacheI){setTimeout(function(){
console.log(cacheI);
},0)})(i)
我们通过 JavaScript 立即调用的函数表达式创建函数范围。i 的值是通过闭包保存的。
恭喜你,到这里,你已经学会了这些面试挑战题。
希望在开发面试中,闭包相关的问题不会再困扰你了。
来源: web前端开发
近年来,从事JavaScript的程序员越来越多,JavaScript的曝光率也越来越高,如果你想转行试试JavaScript,不妨收下这份面试题及答案,没准用得上。当然,如果针对这些问题,你有更棒的答案,欢迎移步至评论区。
面试的公司分别是:阿里、网易、滴滴、今日头条、有赞、挖财、沪江、饿了么、携程、喜马拉雅、兑吧、微医、寺库、宝宝树、海康威视、蘑菇街、酷家乐、百分点和海风教育。以下是面试题汇总
web前端常见的面试题:包括:HTML 常见题目、CSS类的题目、JavaScript类的题目、面试官爱问的问题。原来公司工作流程是怎么样的,如何与其他人协作的?如何夸部门合作的?你遇到过比较难的技术问题是?你是如何解决的?
毕业之后就在一直合肥小公司工作,没有老司机、没有技术氛围,在技术的道路上我只能独自摸索。老板也只会画饼充饥,前途一片迷茫看不到任何希望。于是乎,我果断辞职,在新年开工之际来到杭州,这里的互联网公司应该是合肥的几十倍吧。。。。
javascript的typeof返回哪些数据类型;例举3种强制类型转换和2种隐式类型转换?split() join() 的区别; 数组方法pop() push() unshift() shift();IE和标准下有哪些兼容性的写法
解析 URL Params 为对象;模板引擎实现;转化为驼峰命名;查找字符串中出现最多的字符和个数;字符串查找请使用最基本的遍历来实现判断字符串 a 是否被包含在字符串 b 中
虚拟 DOM (VDOM)是真实 DOM 在内存中的表示。UI 的表示形式保存在内存中,并与实际的 DOM 同步。这是一个发生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为调和。函数组件和类组件当然是有区别的
使用渐进式框架的代价很小,从而使现有项目(使用其他技术构建的项目)更容易采用并迁移到新框架。 Vue.js 是一个渐进式框架,因为你可以逐步将其引入现有应用,而不必从头开始重写整个程序。
AJAX 即 Asynchronous Javascript And XML(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。AJAX 是一种用于创建快速动态网页的技术。它可以令开发者只向服务器获取数据(而不是图片,HTML文档等资源)
本文分享 12 道 vue 高频原理面试题,覆盖了 vue 核心实现原理,其实一个框架的实现原理一篇文章是不可能说完的,希望通过这 12 道问题,让读者对自己的 Vue 掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握 Vue
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!