首页 > 其他分享 >前端面试题

前端面试题

时间:2024-07-22 15:58:10浏览次数:12  
标签:面试题 const 变量 作用域 前端 arr let 函数

不足之处评论我进行修改,或者有更好的面试题评论告诉我,我添加上

1、什么是闭包

  1. 必须有一个内嵌函数。
  2. 内嵌函数必须引用外部函数中的变量。
  3. 外部函数的返回值必须是内嵌函数。

1.闭包的概念

闭包是这样的一种机制:函数嵌套函数,内部函数可以引用外部函数的参数和变量。参数和变量不会被垃圾回收机制收回。

2.闭包的作用

  • 数据封装:闭包可以用于封装私有数据,只暴露有限的接口供外界访问。
  • 保持变量状态:闭包允许函数记住和访问其词法作用域中的变量,即使函数在其作用域之外执行。
  • 延迟计算:通过闭包可以推迟计算的执行,直到真正需要结果时。
  • 相比全局变量和局部变量,闭包有两大特点:

    1.闭包拥有全局变量的不被释放的特点

    2.闭包拥有局部变量的无法被外部访问的特点

    闭包的好处:

    1.可以让一个变量长期在内存中不被释放

    2.避免全局变量的污染,和全局变量不同,闭包中的变量无法被外部使用

    3.私有成员的存在,无法被外部调用,只能直接内部调用

3.闭包的使用场景

  • 装饰器:在不修改原有函数代码的情况下,增加额外的功能。
  • 回调函数:封装了状态的函数可以作为回调函数传递给某些操作。
  • 函数工厂:根据输入参数的不同返回不同行为的函数。

4.闭包语法规范

  1. 定义外部函数。
  2. 在外部函数内定义内部函数。
  3. 内部函数引用外部函数的变量。
  4. 外部函数返回内部函数。

5.闭包的注意事项

1.内存泄漏

由于闭包可以访问外部函数的作用域,如果不小心,可能导致内存泄漏。在不再需要使用闭包时,及时释放对它的引用是很重要的。

2.陷阱:异步操作

当在循环或迭代中创建闭包时,可能会遇到与预期不符的问题,尤其是在涉及异步操作时。

for (var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// 输出: 5 5 5 5 5

在上面的例子中,由于 JavaScript 中的事件循环机制,setTimeout 中的闭包在循环结束后执行,此时 i 的值已经变成了 5。为了解决这个问题,可以使用块级作用域或立即执行函数。

for (var i = 0; i < 5; i++) {
  (function(index) {
    setTimeout(function() {
      console.log(index);
    }, 1000);
  })(i);
}
// 输出: 0 1 2 3 4

2.垃圾回收机制有两种:1.标记清除, 2.引用计数

1.标记清除:js会对变量做一个标记yes or no的标签以供js引擎来处理,当变量在某个环境下被使用则标记为yes,当超出改环境(可以理解为超出作用域)则标记为no,然后对有no的标签进行释放。

2.引用计数:对于js中引用类型的变量, 采用引用计数的内存回收机制,当一个引用类型的变量赋值给另一个变量时, 引用计数会+1, 而当其中有一个变量不再等于值时,引用计数会-1, 如果引用计数为0, 则js引擎会将其释放掉。

3.防抖节流的概念

防抖 是指在一定时间内,在动作被连续频繁触发的情况下,动作只会被执行一次,也就是说当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间,所以短时间内的连续动作永远只会触发一次,比如说用手指一直按住一个弹簧,它将不会弹起直到你松手为止。

节流 是指一定时间内执行的操作只执行一次,也就是说即预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期,一个比较形象的例子是人的眨眼睛,就是一定时间内眨一次。 

1.作用

节流(throttle)与 防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。

2. 区别

防抖: 任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行;

节流: 指定时间间隔内只会执行一次任务;

3.原理

函数节流与函数防抖巧妙地使用 setTimeout 来存放待执行的函数,这样可以很方便的利用 clearTimeout 在合适的时机来清除待执行的函数。

4.应用场景

函数节流应用的实际场景,多数在监听页面元素滚动事件的时候会用到。因为滚动事件,是一个高频触发的事件。

函数防抖的应用场景,最常见的就是用户注册时候的手机号码验证邮箱验证了。只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。

4.重绘与回流的区别

1、 重绘:元素样式的改变(但宽高、大小、位置等不变)
如:outline、visibility、color、background-color等

只改变自身样式,不会影响到其他元素

2、 回流:元素的大小或者位置发生改变(当页面布局和几何信息发生改变的时候),触发了重新布局导致渲染树重新计算布局和渲染
​ 如添加或删除可见的DOM元素;元素的位置发生变化;元素的尺寸发生变化、内容发生变化(如文本变化或图片被另一个不同尺寸的图片所代替);页面一开始渲染的时候(无法避免);

​ 因为回流是根据视口大小来计算元素的位置和大小的,所以浏览器窗口尺寸变化也会引起回流

注意:回流一定会触发重绘,而重绘不一定会回流 

3.如何避免(减少)回流
css
避免设置多层内联样式。
如果需要设置动画效果,最好将元素脱离正常的文档流。
避免使用CSS表达式(例如:calc())。
JavaScript
避免频繁操作样式,最好将样式列表定义为class并一次性更改class属性。
避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
可以先为元素设置为不可见:display: none,操作结束后再把它显示出来。 

5.什么是原型和原型链

原型就是个对象
每个对象都有他自己对应的原型对象,每个对象都可以使用他对象对应的原型对象上面的所有的属性和方法

原型链:查找对象实例的方法和属性时,先在自身找,找不到则沿着__proto__向上查找,我们把__proto__形成的链条关系称原型链

6.数组去重的方式

1、new Set() + Array.from


Set对象:是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即Set中的元素是唯一的。Set本身是一个构造函数,用来生成 Set 数据结构。类似于数组,不是真正的数组,不能使用 length 方法
Array.from() 方法:对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
注意:以上去方式对NaN和undefined类型去重也是有效的,是因为NaN和undefined都可以被存储在Set中, NaN之间被视为相同的值(尽管在js中:NaN !== NaN)
对 {} 无效 

const newArr = Array.from(new Set(arr))
console.log(newArr)   //  [9, 2, '123', true, NaN, false, undefined, {…}, {…}]

2、双层循环,外层循环元素,内层循环时比较值如果有相同的值则跳过,不相同则push进数组

function getDisArray2(arr) {
    let newArr = [];
    let len = arr.length
    for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            if (arr[i] === arr[j]) {
                j++
            }
        }
        newArr.push(arr[i]);
    }
    return newArr
}
 
getDisArray2(arr)   //  [9, 2, '123', true, NaN, false, undefined, NaN, {…}, {…}]

3、利用两层循环+数组的splice方法

function getDisArray3(arr) {
    let len = arr.length
    for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            if (arr[i] === arr[j]) {
                arr.splice(j, 1)
                len-- // 减少循环次数提高性能
                j-- // 保证j的值自加后不变
            }
        }
    }
    return arr
}
 
getDisArray3(arr)    //  [9, 2, '123', true, NaN, false, undefined, NaN, {…}, {…}]

4、利用数组的indexOf方法 + forEach

function getDisArray4(arr) {
    const newArr = []
    arr.forEach(item => {
        if (newArr.indexOf(item) === -1) {
            newArr.push(item)
        }
    })
    return newArr // 返回一个新数组
}
 
console.log(getDisArray4(arr))   
//[9, 2, '123', true, NaN, false, undefined, NaN, {…}, {…}]

5、利用filter

function getDisArray7(arr) {
    return arr.filter(function (item, index, arr) {
        return arr.indexOf(item, 0) === index;
    });
}
 
console.log(getDisArray7(arr))   // [9, 2, '123', true, false, undefined, {…}, {…}]

 当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素

6、利用Map()

 
function getDisArray4(arr) {
    const map = new Map()
    const newArr = []
 
    arr.forEach(item => {
        if (!map.has(item)) { // has()用于判断map是否包为item的属性值
            map.set(item, true) // 使用set()将item设置到map中,并设置其属性值为true
            newArr.push(item)
        }
    })
 
    return newArr
}
 
getDisArray4(arr)   // [9, 2, '123', true, NaN, false, undefined, {…}, {…}]

7.Let、const、var的区别

1.作用域

var声明变量的作用域

var声明的变量不存在块级作用域,属于全局作用域。但是存在函数作用域,在函数中用var声明的变量在函数外不能使用,也不能跨函数使用(在其他函数里使用)。

let声明变量的作用域

let声明的变量存在块级作用域,由{ }包裹,不能跨块访问。也存在存在函数作用域,在函数中用let声明的变量在函数外不能使用,也不能跨函数使用(在其他函数里使用)。

const声明变量的作用域

const声明的变量存在块级作用域,由{ }包裹,不能跨块访问。也存在存在函数作用域,在函数中用const声明的变量在函数外不能使用,也不能跨函数使用(在其他函数里使用)。

2.变量提升

let和const声明的变量和函数不存在变量提升,var声明的变量和函数存在变量提升

3.暂时性死区

暂时性死区是指在代码块内,使用let或const命令声明变量之前的区域,该变量在此区域内是不可用的,即属于该变量的“死区”。

因为let,const不存在变量提升所以导致死区的形成,但是var不存在暂时性死区的情况。

在ES6中,暂时性死区的概念是为了防止在变量声明前就使用这个变量,从而导致意料之外的行为。具体来说,当在代码块内尝试访问一个使用let或const声明的变量,但在该声明之前就已经引用了这个变量时,会触发一个ReferenceError错误。可以看如下代码方便理解:

	let a = 1
    {
      //死区开始
      
      console.log(a) //死区:因为存在块级作用域无法访问外部的a,也无法访问到内部的a
      
	  //死区结束
      let a = 2 //此区域块内创建了a并绑定了值
      console.log(a)//此时获取到内部的a
    }

4.重复声明

var在同一作用域内,相同的变量可以重复声明变量,let和const不可以

5、全局属性

var关键字声明的变量会挂载到window全局属性上,但是let和const声明的则不会。

6.初始值

1、使用const定义变量时必须赋予其初始值,而var和let则不用

2、const声明的常量必须进行初始化,不允许对常量重新赋值

3、如果const定义的是一个复杂数据类型,可以添加、删除、修改值,但不能改变原有类型

8.

标签:面试题,const,变量,作用域,前端,arr,let,函数
From: https://blog.csdn.net/2301_81274510/article/details/140609216

相关文章

  • iOS面试题-load 和 initlize的区别
    +load和+initialize是两个与类的加载和初始化相关的特殊方法。它们在类的生命周期中的作用和调用时机有明显的区别。+load方法调用时机:+load在类初始加载进内存时调用,这通常发生在程序启动的时候,所有类和分类(Category)的+load方法在应用程序的生命周期中只会被调用一次。调用......
  • Day07 MySQL 面试题 (三)
    MySQL面试题22、MySQL里记录货币用什么字段类型好NUMERIC和DECIMAL类型被MySQL实现为同样的类型,这在SQL92标准允许。他们被用于保存值,该值的准确精度是极其重要的值,例如与金钱有关的数据。当声明一个类是这些类型之一时,精度和规模的能被(并且通常是)指定。例......
  • Android笔试面试题AI答之控件Views(3)
    答案仅供参考,来自文心一言目录1.如何在ListView间添加分割线?方法1:在XML布局文件中设置方法2:在Java代码中设置注意事项2.如何实现ListView的逐行显示?1.使用`Handler`和`postDelayed()`方法2.监听滚动事件3.自定义Adapter4.使用`RecyclerView`代替`ListVie......
  • 【前端】JavaScript入门及实战71-75
    文章目录71数组72数组的方法73数组的遍历74数组练习75forEach71数组<!DOCTYPEhtml><html><head><title></title><metacharset="utf-8"><scripttype="text/javascript"> //使用字面量来创建数组 //语法:[] vararr=[......
  • 前端体验优化(5)——后台
    从0开始搭建一套后台管理系统,成本巨大,所以都会选择一套成熟的组件库,基于此,再堆叠业务逻辑。我们公司的组件库基于AntDesign。AntDesign包含一套完整的后台解决方案,不仅提供了75个组件,还开源了整套设计方案,配色、字体、图标、布局等,还分享了众多的用户体验案例。官方基......
  • MySQL InnoDB事务隔离和并发控制面试题详解
    1.为什么MySQL使用B+树作为索引而不是B树?MySQL选择使用B+树作为索引主要有以下几个原因:减少IO次数,提高效率:B+树的所有数据都存储在叶子节点,非叶子节点只存储索引,树的高度较低,因此查找路径较短,减少了磁盘IO次数。查询效率更加稳定:由于数据仅存储在叶子节......
  • Java基础面试题大全 -001
    1、Java语言有哪些特点1、简单易学、有丰富的类库2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高)3、与平台无关性(JVM是Java跨平台使用的根本)4、可靠安全5、支持多线程6、java生态完善2、面向对象和面向过程的区别面向过程:是分析解决问题的步骤,然后用函数......
  • Web前端WebRTC攻略-媒体协商与SDP简析(转载)
    1.媒体协商在音视频通讯场景中,由于两端之间所支持的音视频编解码、传输协议、传输的速率,都需要进行彼此通知对方。我们把一个1对1的音视频通讯,比喻成双方互送快递包裹的过程。首先这里有很多问题,双方要彼此告知对方后,才能寄送包裹。比如:*我不知道包裹要寄给谁?(我要和谁建立通......
  • 简单的球场管理系统(仅仅是连接IDEA与数据库不涉及前端,展示主要是在控制台输入输出)
    项目内层分为七层我这里分为(beans,controller,dao,lib,service,test,util)这七层仅展示(controller层)(1)QiuChangControllerimportcom.xszx.beans.Balance;importcom.xszx.beans.Ball;importcom.xszx.beans.JBall;importcom.xszx.beans.User;importcom.xszx.dao......
  • 解决spring后端传前端数值为空的问题
    问题:在开发当中,由于我的数据传输从DTO在某些场景下,其中的部分字段并不需求进行值的传递,但在其他功能当中需要;(比如开发题目模块时,查询题目采用同一接口,根据题目id不同,后台判断其为多选还是单选进行回传给dto给前端)。导致出现了如下情况的诸多null值,而这些是没有作用但又不可删除的......