Web前端最新JavaScript进阶(十一):闭包学习进阶,护士15个经典面试问题

框架相关

原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。现阶段较热门是React、Vue两大框架,两者工作原理上存在共通点,也存在一些不同点,对于校招来说,不需要两个框架都学得特别熟,一般面试官会针对你简历中写的框架进行提问。

在框架方面,生命周期、钩子函数、虚拟DOM这些基本知识是必须要掌握的,在学习的过程可以结合框架的官方文档

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

Vue框架

知识要点:
1. vue-cli工程
2. vue核心知识点
3. vue-router
4. vuex
5. http请求
6. UI样式
7. 常用功能
8. MVVM设计模式

React框架

知识要点:
1. 基本知识
2. React 组件
3. React Redux
4. React 路由

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

一、变量的作用域

要理解闭包,首先必须理解Javascript特殊的变量作用域。

变量的作用域无非就是两种:全局变量局部变量

Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

   var n=999;
  function f1(){
    alert(n);
  }
  f1(); // 999

另一方面,在函数外部自然无法读取函数内的局部变量。

function f1(){
 var n=999;
}
alert(n); // error

这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

function f1(){
 n=999;
}
f1();
alert(n); // 999

二、如何从外部读取局部变量?

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

function f1(){
 var n=999;
 function f2(){
  alert(n); // 999
 }
}

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

function f1(){
 var n=999;
 function f2(){
  alert(n); 
 }
  return f2;
}
var result=f1();
result(); // 999

三、闭包的概念

上一节代码中的f2函数,就是闭包。闭包就是能够读取其他函数内部变量的函数。

由于在Javascript中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

四、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

 function f1(){
  var n=999;
  nAdd=function(){n+=1}
  function f2(){
   alert(n);
  }
   return f2;
 }
   var result=f1();
   result(); // 999
   nAdd();
   result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

五、使用闭包注意点

  1. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  2. 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

六、思考题

如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

代码片段一

   var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

代码片段二

   var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());

6.1 闭包面试题
6.1.1 输出内容

下面的代码将会输出什么内容?

let count = 0;
(function immediate() {
  if (count === 0) {
    let count = 1;
    console.log(count); // 输出什么?
  }
  console.log(count); // 输出什么?
})();

答案
输出 1 和 0。

第一个语句 let count = 0 声明了一个变量 count。

immediate() 是一个闭包,它从外部作用域得到 count 变量。在 immediate() 函数作用域内, count 是 0。

但是,在条件内,另一个 let count = 1 声明了局部变量 count,该变量覆盖了作用域之外的 count。第一个 console.log(count) 输出 1。

第二个 console.log(count) 输出为 0 ,因为这里的 count 变量是从外部作用域访问的。

6.1.2 输出内容

下面的代码将会输出什么内容?

for (var i = 0; i < 3; i++) {
  setTimeout(function log() {
    console.log(i); // => ?
  }, 1000);
}

答案
输出:3, 3, 3。

代码分为两个阶段执行。

阶段1
for() 重复 3 次。在每次循环都会创建一个新函数 log(),该函数将捕获变量 i。 setTimout() 安排log() 在 1000 毫秒后执行。
当 for() 循环完成时,变量 i 的值为 3。
阶段2
第二阶段发生在 1000ms 之后:
setTimeout() 执行预定的 log() 函数。 log() 读取变量 i 当前的值 3,并输出 3
所以输出 3, 3, 3。

6.1.3 错误信息

下面的代码将会输出什么:

function createIncrement() {
  let count = 0;
  function increment() { 
    count++;
  }
  let message = `Count is ${count}`;
  function log() {
    console.log(message);
  }
  return [increment, log];
}
const [increment, log] = createIncrement();
increment(); 
increment(); 
increment(); 
log(); // => ?

答案
输出:‘Count is 0’

increment() 函数被调用 3 次,将 count 增加到 3。

message 变量存在于 createIncrement() 函数的作用域内。其初始值为 ‘Count is 0’。但即使 count 变量已经增加了几次,message 变量的值也始终为 ‘Count is 0’。

log() 函数是一个闭包,它从 createIncrement() 作用域中获取 message 变量。 console.log(message) 输出录’Count is 0’到控制台。

6.1.4 重新封装

下面的函数 createStack() 用于创建栈结构:

function createStack() {
  return {
    items: [],


### 文末

如果30岁以前,可以还不知道自己想去做什么的话,那30岁之后,真的觉得时间非常的宝贵,不能再浪费时间在一些碎片化的事情上,比如说看综艺,电视剧。一个人的黄金时间也就二,三十年,不能过得浑浑噩噩。所以花了基本上休息的时间,去不断的完善自己的知识体系,希望可以成为一个领域内的TOP。



同样是干到30岁,普通人写业务代码划水,榜样们深度学习拓宽视野晋升管理。

这也是为什么大家都说30岁是程序员的门槛,很多人迈不过去,其实各行各业都是这样都会有个坎,公司永远都缺的高级人才,只用这样才能在大风大浪过后,依然闪耀不被公司淘汰不被社会淘汰。



**269页《前端大厂面试宝典》**

包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

![](https://img-blog.csdnimg.cn/img_convert/b98713ee557d94286de8afe404cb51d1.png)



**前端面试题汇总**

![](https://img-blog.csdnimg.cn/img_convert/1d691ca297c9016828aff783a701e065.png)



**JavaScript**

![](https://img-blog.csdnimg.cn/img_convert/a489904576da281d07092f043566f6dd.png)



【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

![](https://img-blog.csdnimg.cn/img_convert/b98713ee557d94286de8afe404cb51d1.png)



**前端面试题汇总**

![](https://img-blog.csdnimg.cn/img_convert/1d691ca297c9016828aff783a701e065.png)



**JavaScript**

![](https://img-blog.csdnimg.cn/img_convert/a489904576da281d07092f043566f6dd.png)



  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript面试题的可以包括以下几个方面: 1. 作用域和变量提升:在JavaScript中,函数作用域和块级作用域的概念是很重要的。了解变量提升和作用域链的工作原理对于理解JavaScript的作用域规则至关重要。在题目中给出的代码中,变量name由于使用了var关键字声明,会被提升到函数作用域的顶部,但是在打印输出时还未被赋值,所以会输出undefined。而变量age使用let关键字声明,不会被提升,所以在console.log(age)时会报错。因此,上述代码的输出是undefined和ReferenceError。 2. 数据类型和类型转换:JavaScript有多种基本数据类型,包括数字、字符串、布尔值等。了解不同数据类型的特点以及类型转换的规则对于正确处理数据至关重要。在JavaScript中,使用双等号(==)行比较时会发生隐式类型转换,这可能导致一些令人惊讶的结果。因此,在行类型转换时需要格外小心。 3. 闭包和作用域:闭包是指函数能够访问并操作其定义时所在的词法环境中的变量。了解闭包的概念以及如何正确使用闭包可以帮助开发人员编写更高效和安全的代码。 4. 面向对象编程:JavaScript是一种面向对象的语言,它支持封装、继承和多态等面向对象的特性。了解如何使用构造函数、原型链和对象创建模式等概念可以帮助开发人员设计和组织复杂的代码结构。 5. 异步编程:JavaScript是一种单线程的语言,但是通过使用回调函数、Promise和async/await等机制,可以实现非阻塞的异步编程。理解JavaScript中的事件循环和异步编程模型对于编写高效的异步代码至关重要。 总结:JavaScript面试题的包括作用域和变量提升、数据类型和类型转换、闭包和作用域、面向对象编程以及异步编程等方面的知识。熟练掌握这些概念和技术,可以帮助您在JavaScript编码面试中取得优异的表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值