前端面试题(js篇)

2023-10-26

1.解释一下什么是闭包

什么是闭包:函数使用了不属于自己的局部变量(函数套函数,里面函数使用了外面函数定义的变量)
闭包的作用:避免全局污染
闭包的缺点:使用过多会造成内存泄漏(占用的内存释放不掉)

2.js中的本地存储有哪些,区别是什么

(1).sessionStorage

仅在当前会话下生效,当你关闭页面或浏览器后你存储的sessionStorage数据会被清除。
可存储的数据大小一般在5mb。
不参与和服务器的通信

(2).localStorage

永久有效,关闭浏览器也不会消失的,除非自己主动清除localStorage信息。
可存储的数据大小一般在5mb。
不参与和服务器的通信

(3).cookie

cookie是在设置的过期时间之前一直有效,哪怕关闭浏览器。
有个数限制(各浏览器不同),一般不能超过20个。
与服务器端通信:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题

3.原型与继承、原型链

只有函数才有原型,原型是用来存放共有属性的,原型是一个属性,它的值是个对象
每一个对象都有一个__proto__(隐式原型)的属性,这个属性指向创建该对象的构造函数的prototype(如果不能确定他是谁的实例,都是Object的实例)
原型链:对象实例的隐士原型指向创建改对象的构造函数的原型对象,这样一层一层的指向关系形成的链叫原型链。如下。
在这里插入图片描述

4.js中的this指向

es5this指向函数运行时所在的对象(谁调用我我指谁,会随着调用者不同而改变)
es6箭头函数中this指向函数定义时所在的对象(我在哪出生我指谁,永远不会改变)

5.跨域问题

由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。存在跨域的情况:
网络协议不同,如http协议访问https协议。
端口不同,如80端口访问8080端口。
域名不同,如qianduanblog.com访问baidu.com。
子域名不同,如abc.qianduanblog.com访问def.qianduanblog.com。
域名和域名对应ip,如www.a.com访问20.205.28.90.
解决方案
1.proxy代理(vue中常用)
2.jsonp
3.生产环境上用nginx反向代理

6.call和apply的用法

call和appley的作用都是改变this指向,让函数中的this可以自定义为我们传入的对象。
call和apply作用是一样的,只不过call接受的是参数序列当参数,apply接受的是数组当参数
其实call和apply的意思就是让一个对象去调用别人的方法,之前我们大多接触的是对象调用自己的方法。
就比如你朋友买了台车,正常我们接触的都是自己开自己的车,但是现在你想开你朋友的车,就是你这个对象去调用别人的方法

fn.call(obj,arg1,arg2,arg3)
fn.apply(obj,[arg1,arg2,arg3])

语法可以概括为:

开车这个函数.call(开车的这个对象,参数1,参数2) **
开车这个函数.apply(开车的这个对象,[参数1,参数2]) **

call前面是你要调用的方法,括号里是要调用这个方法的对象和参与运算的参数

例如:

function fn(){
   console.log(this.name+"在开车")
 } 
var obj={name:"小明"}
fn.call(obj)

运行结果:
在这里插入图片描述
上面的例子我们是用小明对象调用了window对象中定义的方法。下面换个对象是一样的道理,然后我们把参数传进去

var xh={
 	name:"小红",
    fn:function(car){
       	console.log(this.name+"在开"+car+"车")
      } 
}
 var obj={name:"小明"}
 xh.fn.call(obj,"宝马")

结果如下
在这里插入图片描述
换成apply,用法一样,只是传参换成了数组,注意call方法也支持多个参数,只不过不能写成数组,要写成以逗号分隔

var xh={
    name:"小红",
    fn:function(car1,car2){
       	console.log(this.name+"在开"+car1+car2+"车")
      } 
   }
   var obj={name:"小明"}
   xh.fn.apply(obj,["宝马","奔驰"])

在这里插入图片描述
总结:面试一般问作用和区别,作用是都是用来改变this指向的,区别是call接受参数序列,apply接受的是数组

7.for in 循环和for of循环的区别

for-in是ES5标准,遍历的是key(可遍历对象、数组或字符串的key);
for-of是ES6标准,遍历的是value(可遍历对象、数组或字符串的value)。

8.js中的深拷贝和浅拷贝

深拷贝递归地复制新对象中的所有值或属性,而浅拷贝只复制引用关系。
在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。
slice方法和concat方法可以实现普通数组的深赋值,但是如果是二维数组就无法深复制
下面这个例子证明slice无法真正实现深复制

var arr1=[1,2,3,['1','2','3']];
var arr2=arr1.slice(0);
 arr1[3][0]=0;
 console.log(arr1);//[1,2,3,['0','2','3']]
 console.log(arr2);//[1,2,3,['0','2','3']]

如何实现深拷贝:

1.JSON.stringfy JSON.parse

var arr1 = ['red','green'];
var arr2 = JSON.parse(JSON.stringify(arr1));//复制
console.log(arr2)//['red','green'];
arr1.push('black') ;//改变color1的值
console.log(arr2)//["red", "green"]
console.log(arr1)//["red", "green", "black"]

2.递归

function deepClone(obj){
    //判断参数是不是一个对象
    let objClone = obj instanceof Object?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
var a ={
    x:1,
    y:2
};
b=deepClone(a);
a.x=3
console.log(a);
console.log(b);

9.从打开浏览器输入网址,到页面显示出来经历了什么

1- 输入网址:那肯定是输入你要访问的网站网址了,俗称url;
2- 缓存解析:它先去缓存当中看看有没有,从 浏览器缓存-系统缓存-路由器缓存 当中查看,如果有从 缓存当中显示页面,然后没有那就进行步骤三;
缓存就是把你之前访问的web资源,比如一些js,css,图片什么的保存在你本机的内存或者磁盘当中
3- 域名解析:域名到IP地址的转换过程。域名的解析工作由DNS服务器完成。解析后可以获取域名相应的IP地址
4- tcp连接:在域名解析之后,浏览器向服务器发起了http请求,tcp连接,三次握手建立tcp连接。TCP协议是面向连接的,所以在传输数据前必须建立连接
5- 服务器收到请求:服务器收到浏览器发送的请求信息,返回一个响应头和一个响应体。
6-页面渲染:浏览器收到服务器发送的响应头和响应体,进行客户端渲染,生成Dom树、解析css样式、js交互。

10.事件委托/事件代理

利用事件的冒泡传播机制(触发当前元素的某一个行为,它父级所有元素的相关行为都会被触发),如果一个容器中有很多元素都要绑定点击事件,我们没有必要一个个的绑定了,只需要给最外层容器绑定一个点击事件即可

11.线程和进程

  1. 线程是最小的执行单元,进程是最小的资源管理单元
  2. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(一般情况)
  3. 一个进程对应多个线程最为常见,Linux、Windows等是这么实现的.其实理论上这种关系并不是一定的,可以存在多个进程对应一个线程,例如一些分布式操作系统的研究使用过这种方式,让线程能从一个地址空间转移到另一个地址空间,甚至跨机器调用不同的进程入口

12.如何实现数组扁平化

什么是数组扁平化?
[‘a’,‘b’,‘c’] //这是一个拥有3个元素的数组,是一个一维数组(不存在数组嵌套)。[[‘a’,‘b’],[‘c’,‘d’],[‘e’,‘f’]] 从整体上看是一个数组,但是其中的元素又是数组,即数组中嵌套数组,这就是二维数组
以此类推·····
[‘a’,[‘b’,[‘c’]]]//3维数组 [‘a’,[‘b’,[‘c’,[…]]]]//n维数组 数组扁平化就是把多维数组转化成一维数组。

1.通过es5递归实现

let result = [];
 function fn(ary) {
    for(let i = 0; i < ary.length; i++) }{
    let item = ary[i];
    if (Array.isArray(ary[i])){
    fn(item);
    } else {
        result.push(item);
        }
    }
}

2.es6 +reduce+递归实现

let arr=[[1,2,3],4,5,[6,7,[8,9]]];
function bianping(arr){
    return arr.reduce((res,item) =>{
        return res.concat(Array.isArray(item)?bianping(item):item)
    },[])
}
console.log(bianping(arr));

13.防抖和节流

见我的这篇博客
防抖和节流

14.js中的event loop(事件循环)

js作为单线程语言。在执行过程中,会产生执行环境。这些执行环境中的代码被顺序的加入到执行栈中,如果遇到异步代码,会被挂起并加入到任务队列当中,等到主线程任务执行完毕,event loop就会从任务队列取出需要执行的代码放入到执行栈中执行。

异步任务分类为宏任务(macro-task)和微任务(micro-task)。

宏任务:整体的Script setTimeout setInterval
微任务:Promise process.nextTick

例子

// 这是一个同步任务
console.log('1')            --------> 直接被执行
                                      目前打印结果为:1

// 这是一个宏任务
setTimeout(function () {    --------> 整体的setTimeout被放进宏任务列表
  console.log('2')                    目前宏任务列表记为【s2】
});

new Promise(function (resolve) {
  // 这里是同步任务
  console.log('3');         --------> 直接被执行
  resolve();                          目前打印结果为:1、3 
  // then是一个微任务
}).then(function () {       --------> 整体的then[包含里面的setTimeout]被放进微任务列表
  console.log('4')                    目前微任务列表记为【t45】
  setTimeout(function () {
    console.log('5')
  });
});

有微则微,无微则宏
如果微任务列表里面有任务 会执行完毕后在执行宏任务。

浏览器瞅了一眼微任务列表 发现里面有微任务 就开始全部执行
then(function () {
  console.log('4')            --------> 直接被执行
                                        目前打印结果为:1、3、4
  setTimeout(function () {    --------> 被放进宏任务列表了
    console.log('5')                    目前宏任务列表记为【s2、s5】
  });
});


浏览器发现微任务执行完毕了

开始执行宏任务列表

setTimeout(function () {
  console.log('2')   --------> 直接被执行
                               目前打印结果为:1、3、4、2

});

setTimeout(function () {
  console.log('5')   --------> 直接被执行
                               目前打印顺序为: 1、3、4、2、5、5
});

最终结果为: 1、3、4、2、5

15.原型链污染

先看一个案例

> var a = {admin:1}
undefined
> a.admin
1
> a.__proto__.admin = 2
2
> var b = {}
undefined
> b.admin
2

这里看到,b为空的,但是却访问到存在admin属性,并且修改了它的值。这是因为我们通过a.proto.admin = 2修改了a的原型,即Object,而这里b的原型也是Object,所以自然会有一个admin属性。
这些不会的同学建议去学一下原型链的知识。任何一个对象,都有隐式原型也就是__proto__,它的值指向的是原型对象。而a和b都是属于 Object的子类实例,所以他们的原型是一样的。

所以,什么是原型链污染(定义)
那么,在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。

16.async await函数

作用:跟promise的作用一样,都是为了解决异步编程问题,但是写法比promise更优雅
async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于声明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

async函数会返回一个promise对象,如果在函数中return一个值,那么该值会通过Promise.resolve()传递出去。
任何一个函数都可以直接改为async函数,只需要在function 前面加上async关键字,如下

 async function fn(){
         alert(1)
 }
console.log(fn()) 
//

输出结果如下,
在这里插入图片描述
跟普通函数默认没有结果不同,async函数默认是有结果的,默认返回一个promise对象,如果函数本身有return,那么return的值就是promise对象中resolve的值,这也就意味着我们可以继续在函数的调用之后写then方法如下:

 async function fn(){
    alert(1)
 }
 fn().then((res)=>{
 //因为函数没有return,所以默认resolve的是undefined
     console.log(res)  //undefined
  })


//如果加了return return的值就是该函数返回的promise对象resolve出来的值
 async function fn(){
   return "ok"
}
fn().then((res)=>{
    console.log(res) //ok
})

一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。
因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行
在这里插入图片描述
await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?我不得不先说,
await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。
如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
案例

function fn() {
	var a=new Promise(function(resolve,reject){
	setTimeout(function(){
		console.log(666);
		resolve("haha")
		},2000)	
	})
	return a
}
async function fn2(){
	let b=await fn();
	console.log("执行",b)		
}
	fn2()
//执行结果为 
/*
666
执行 haha
*/
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

前端面试题(js篇) 的相关文章

随机推荐

  • 芯片的ATE测试简介

    ATE Automatic Test Equipment 即自动测试设备 它用于芯片大规模生产测试 保障稳健 质量 成本和进度 的供应 ATE测试基本的覆盖理念 主要是结构性测试 即Structure Test 再辅以一定的功能和性能测试
  • ch4 报错修正 & Sophus使用

    ch4 报错 修正 1 添加Eigen头文件 include directories usr include eigen3 2 include sophus so3 hpp include sophus se3 hpp 3 大量报错但都与S
  • CentOS 7安装OpenMPI

    文章目录 一 下载OpenMPI源码 二 解压缩OpenMPI源码 三 安装OpenMPI 四 配置环境变量 五 验证安装 参考资料 一 下载OpenMPI源码 wget https download open mpi org releas
  • 微前端框架 之 qiankun

    文章目录 一 介绍 1 1 qiankun的优点 特点 二 源码解读 2 1 框架目录结构 2 2 有料的 package json 2 3 示例项目中的主应用 2 4 启动示例项目 三 示例项目 3 1 主应用 3 1 1 webpack
  • video.js 报错:your browser did not support

    video js 报错 The media playback was aborted due to a corruption problem or because the media used features your browser d
  • 记vue___旧版vue项目中配置可选链和双问号语法

    新项目vue cli搭建可以直接使用可选链和双问号 旧项目的话需要借助插件解析可选链和双问号写法 babel plugin proposal optional chaining 插件可以帮助我们将可选链式的代码转化 旧项目通过babel配置
  • MySQL 关键字及保留字

    在SQL语句中出现的关键字和保留字 如果要使用人他们的字符意思而不是作为关键字 保留字使用 关键字可以正常使用 但是保留字必须使用 键盘tab键上面 数字1左边的那个按键 来分割 这个在SQLServer里面是使用 中括号实现的 所以我们要
  • typeScript--[类的实例方法与静态方法]

    这里所谓的静态方法 其实就是将方法直接定义在了 构造函数对象上 只有构造函数本身才能去使用它 任何其他都无法使用 包括它的 派生类 一 js中的实例方法和静态方法 1 实例方法 定义在构造函数内的方法就是实例方法 function A na
  • java 作业1:鸡兔同笼问题

    作业 1 鸡兔同笼问题 现在若干只鸡和若干只兔 丢在一个笼子里 从上面看有35个头 从下面看有 94只脚 问有多少只鸡和多少只兔子 package Lx public class ForTest01 public static void m
  • 建模前数据去噪方法总结

    数据在建模之前 有的时候会存在大量噪声 这个时候就需要去噪算法对原始数据进行去噪处理 目前了解到的去噪方法有 3标准差去噪 分箱去噪 dbscan去噪 孤立森林等 其中 3标准差去噪 对于正态分布的数据具有较好的去噪性能 而对于数据属于偏态
  • 【算法】零基础KMP、Trie、AC自动机

    文章目录 KMP 前后缀是什么 KMP题 AcWing 831 KMP字符串 Trie AcWing 835 Trie字符串统计 AC自动机 KMP 字符串匹配的KMP算法 前缀和后缀的详解 看这个可以理解KMP 字符串匹配的KMP算法 前
  • STM32F1系列PB3,PB4,PA13,PA14,PA15用作普通IO口的特殊配置

    最近博主用STM32F103C8T6做了一个温度测控模块 用到PB3 PB4 PA15等引脚控制外设 发现不管怎么配置 这三个引脚都不能置零 后来发现是包括这三个引脚在内的PB3 PB4 PA13 PA14 PA15是特殊的IO口 用作JT
  • CleanShot X for mac安装下载,mac系统录屏、截图、标注软件

    您是否经常需要截图 录屏或者标注图片 如果是 那么您一定会喜欢CleanShot X for mac 这是一款专为Mac用户设计的强大而简洁的工具 CleanShot X for mac可以让您轻松地截取任何区域的屏幕 无论是整个屏幕 窗口
  • [586]使用requests来爬取大众点评

    看完本文 你可以 1 了解大众点评的CSS反爬虫机制 2 破解反爬虫机制 3 使用requests即可正确获取到评论数 平均价格 服务 味道 环境数据 评论文本数据 1 前言 在工作生活中 发现越来越多的人对大众点评的数据感兴趣 而大众点评
  • spring 解析swagger.json

    微服务开发 经常会用到swagger 开发过程中也可以直接验证 测试接口是否可用 但是由于swagger不是正式的对接文档 我们提供给前端或者外部来进行联调时还是要正式的文档 为了解决这一痛点 发现swagger是通过swagger jso
  • 软件测试面试HR常问问题及回答技巧,看完丝毫不需要慌,稳过

    前言 当你去一家公司面试 可能会经历好几轮的面试 然后到HR面试 很多公司的HR都有一票否决权 可以直接决定录不录用你 就算你的技术很硬 实例很强 HR不满意还是可以不录用你 尤其对于技术一般的人 HR的这关面试就显得极为重要了 我这个里准
  • memtool使用指南

    最近在用Infineon Memtool 的过程中遇到的一点问题 看了英文help解决了 现总结一下memtool的使用指南 1 target选择change 选择芯片型号 添加配置文件 打开电源 2 对右边的各个rom进行remove a
  • GNU Linux核心命令和工具的源代码路径

    GNU Linux核心命令和工具的源代码路径 2015 08 20 09 44 57 分类 LINUX 原文地址 GNU Linux核心命令和工具的源代码路径 作者 ubuntuer Commands and Utilities The t
  • Linux系统下安装Redis-7.0.0

    一 准备工作 1 下载安装新版的gcc编译器 redis的安装需要gcc环境的支持 所以首先要检查下服务器上时候已经安装了gcc环境 离线安装gcc包 执行安装命令 rpm ivh rpm nodeps force 1 1 下载Redis客
  • 前端面试题(js篇)

    1 解释一下什么是闭包 什么是闭包 函数使用了不属于自己的局部变量 函数套函数 里面函数使用了外面函数定义的变量 闭包的作用 避免全局污染 闭包的缺点 使用过多会造成内存泄漏 占用的内存释放不掉 2 js中的本地存储有哪些 区别是什么 1