2023前端面试题汇总(最新)-持续更新中

目录

1. 防抖和节流

防抖(debounce):单位时间内,频繁触发事件,只执行最后一次。
比如点击按钮,2秒后调用函数,结果在1.5秒的时候又点了,则会重新计算2秒后在调用函数。

应用场景:搜索框、输入框、手机邮箱验证等

思路:利用定时器,每次触发先清除定时器()
底层代码实现:

const box = document.querySeleter(".box")
let i = 1
function mouseMove() {
	box.innerHTML = i++
}
function debounce(fn, t) {
	let timer
	// return 返回一个匿名函数
	return function() {
		if(timer) clearTimeout(timer)
		timer = setTimeOut(function() {
			fn()
		}, t)
	}
}
box.addEventListener('mousemove', debounce(mouseMove, 500))

节流(throttle):单位时间内,频繁触发事件,只执行一次

应用场景比较多的是:鼠标经过、页面缩放、滚动条滚动scroll事件、下拉刷新等高频事件
思路:利用定时器,判断是否有定时器,如果有等定时器结束再开启新的定时器
代码实现:

const box = document.querySeleter(".box")
let i = 1
function mouseMove() {
	box.innerHTML = i++
}
function throttle(fn, t) {
	let timer
	// return 返回一个匿名函数
	return function() {
		if(!timer) {
			timer = setTimeOut(function() {
				fn()
				// 清空定时器 此处使用timer=null清除定时器是因为写在了定时器里面,setTimeout中是无法清除定时器的,因为定时器还在运作
				timer = null
			}, t)	
		}
	}
}
box.addEventListener('mousemove', throttle(mouseMove, 500))

2. js闭包

什么是闭包:闭包就是能够读取其他函数内部变量的函数

function fn0 () {
	const aaa = 0
	return function() {
		return aaa
		console.log('打印', aaa)
	}
}

闭包存在意义:

可以延长变量的生命周期4可以创建私有的环境

闭包好处:

可以读取其他函数的内部变量

将变量始终保存在内存中

可以封装对象的私有属性和方法

坏处:

消耗内存、使用不当会造成内存溢出问题

3. Vue相关总结

3.1 vue中的data为什么是一个函数?(面试常问)

Vue 中的 data 必须是个函数,因为当 data 是函数时,
组件实例化的时候这个函数将会被调用,返回一个对象,
计算机会给这个对象分配一个内存地址,实例化几次就分配几个内存地址,
他们的地址都不一样,所以每个组件中的数据不会相互干扰,改变其中一个组件的状态,其它组件不变。

简单来说,就是为了保证组件的独立性和可复用性,如果 data 是个函数的话,
每复用一次组件就会返回新的 data,类似于给每个组件实例创	建一个私有的数据空间,
保护各自的数据互不影响

3.2 MVC 和 MVVM的区别

MVC:M(model数据)、V(view视图),C(controlle控制器)
缺点是前后端无法独立开发,必须等后端接口做好了才可以往下走;
前端没有自己的数据中心,太过依赖后台

MVVM:M(model数据)、V(view视图)、VM(viewModel控制数据的改变和控制视图)
html部分相当于View层,可以看到这里的View通过通过模板语法来声明式的将数据渲染进DOM元素,
当ViewModel对Model进行更新时,通过数据绑定更新到View。 
Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,
即Model变化时VIew可以实时更新,View变化也能让Model发生变化

MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,
也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,
而是改变属性后该属性对应View层显示会自动改变

3.3 v-model 原理

是采用数据劫持结合发布者-订阅者模式的方式,
通过Object.defineProperty()来劫持各个属性的setter,getter,
在数据变动时发布消息给订阅者,触发相应的监听回调从而达到数据和视图同步。

3.4 vue中的data为什么是一个函数?(面试常问)

实际上就是一个闭包,因为vue是单页面应用,是由很多组件构成,每一个组件中都有一个data,
所以通过闭包给每一个组件创建了一个私有的作用域,这样就不会相互影响。

3.5 v-if 和 v-show的区别

v-if是通过添加和删除元素来进行显示或者隐藏
v-show是通过操作DOM修改display样式来修改元素的显示和隐藏
如果需要频繁的进行元素的显示和隐藏使用v-show性能更好

3.6 v-for中为什么要有key

key 可以提高虚拟DOM的更新效率。

在vue中,默认“就地复用”的策略,在DOM操作的时候,如果没有key 就会造成选项错乱

key 只能是字符串或者number,其他类型不可以

1. 虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,
Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新的虚拟DOM】与【旧的虚拟DOM】差异比较比较规则如下:

2. 比较规则:

1)旧虚拟DOM找到了与新虚拟DOM相同的key:

若虚拟DOM中内容没变,直接使用之前的真实DOM

若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM

3. 用index作为key可能会引发的问题:

1)若对数据进行:逆序添加、逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新==>界面效果没问题,但效率低

2)如果结构中还包含输入类的DOM,会产生错误的DOM更新 ==> 界面有问题

3.7 打包后 dist 目录过大,解决办法?

  1. dist打包生成的文件中有 .map 文件,可以删除。在 vue.config.js文件中配置:productionSourceMap: false
  2. 组价和路由使用懒加载、按需引入等
  3. 对于文件和图片进行压缩。 安装压缩组件: compression-webpack-plugin
    安装后进行导入配置:
    最小化代码 minisize: true
    分割代码: splitChunksl
    超过限定值的文件进行压缩,threshold: 文件大小(字节为单位)

3.8 computed 和 watch 的区别

  1. computed是计算属性,watch是监听器,用来监听某一个值的变化进而触发相应的回调
  2. computed中的函数必须要有return返回、watch没有必须的要求返回return
  3. computed是第一次加载就触发一次,watch首次加载不会触发,如果需要首次加载需要设置immediate属性
  4. computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中获取;而watch在每次监听值发生变化的时候都会执行回调。

3.9 vue组件之间的数据传递

3.9.1 父 传 子

通过在子组件身上绑定自定义属性,然后再子组件里使用props属性来接收即可
​​在这里插入图片描述

3.9.2 子 传 父

1)第一种方式:通过父组件给子组件传递函数类型的props实现:子组件给父组件传递数据
父组件:
在这里插入图片描述
在这里插入图片描述

子组件:
在这里插入图片描述

2)第二种方式:通过父组件给子组件绑定一个自定义事件实现:子组件给父组件传递数据
父组件
在这里插入图片描述
在这里插入图片描述

子组件
在这里插入图片描述

3)第三种方式:通过父组件给子组件绑定一个自定义事件实现:使用ref实现
父组件
在这里插入图片描述
在这里插入图片描述

子组件
在这里插入图片描述

3.9.3 全局事件总线:可以实现任意组件间的数据传递

main.js:将全局事件bus,挂载到Vue的原型上,这样所有的组件都可以使用
在这里插入图片描述
兄弟组件A:
在这里插入图片描述
兄弟组件B:
在这里插入图片描述
4. 消息订阅与发布
一种组件间的通信方式,适用于任意组件间通信。

使用步骤:

  1. 安装pubsub: npm i pubsub-js
  2. 引入: import pubsub from ‘pubsub-js’
  3. 接收数据: A组件想要接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
mounted() {
  this.pid = punsub.subscribe('xxx', (data)=>{
          ......
  })
}
  1. 提供数据: pubsub.publish(‘xxx’, 数据)
  2. 最好在beforeDestory钩子中,用pubsub.unsubscribe(pid)取消订阅

4.0 vue原型 和 原型链

不管是普通函数还是构造函数,都有一个属性prototype,用来指向这个函数的原型对象。

同时原型对象也有一个constructor属性,用来指向这个原型对象的构造函数。

原型对象的作用:用来存储实例化对象的公有属性和方法

例如

function Person (name, age) {
	this.name = name
	this.age = age
	this.say = function () {
		console.log('我会说话')
	}
}

let person1 = new Person('小明', 20)

console.log(person1.name) // 小明
console.log(person1.age) // 20
console.log(person1.say()) // 我会说话


如上:实例化出来的person1,就可以使用构造函数Person里面的属性和方法

总结

  1. 访问对象的一个属性,先在自身查找,如果没有,会访问对象的__proto__,沿着原型链查找,一直找到Object.prototype.proto
  2. 每个函数都有prototype属性,会指向函数的原型对象。
  3. 所有函数的原型对象的__proto__,会指向Object.prototype。
  4. 原型链的尽头是Object.prototype.proto,为null。

具体可参考下方文章链接地址(写的非常详细易懂):https://blog.csdn.net/weixin_56505845/article/details/119683904

4.1 深拷贝 与 浅拷贝

首先浅拷贝和深拷贝只针对引用类型
浅拷贝:拷贝的是地址,适合拷贝单层的数据
浅拷贝
创建一个对象,这个对象有着原始对象的值的精确拷贝。
这里要引入基本数据类型引用数据类型 ,浅拷贝的如果是基本数据类型,则拷贝的直接是这个数据的值;如果是引用类型,则是创建了一个新的指针,用来指向原类型的内存地址(即拷贝了这个内存地址),如果一个对象改变了,则会影响另一个对象

常见方法:
1、拷贝对象:Object.assign() / 展开运算符{…obj} 拷贝对象
2、拷贝数组:Array.prototype.concat() / […arr]

弊端:如果拷贝的数据复杂,此时修改拷贝对象,会影响原数据
例如:

const obj = {
	name: '你好',
	age: 18,
	family: {
		son: '大儿子'
	}
}
const newObj = {}
Object.assign(newObj, obj)
newObj.family.son = '小儿子'
console.log(obj) // 此时打印出来的原数据obj里面的family对象值被修改

浅拷贝代码实现:

function clone(target) {
    let cloneTarget = {};
    for (const key in target) {
        cloneTarget[key] = target[key];
    }
    return cloneTarget;
};

深拷贝
与之相对,深拷贝指的是创建一个新的指针,同时也会生成一个新的内存地址,这个新的指针指向新的内存地址。与原对象是相互独立,互不影响

实现深拷贝的方法

  1. 最常用的
JSON.parse(JSON.stringify())
  1. 递归实现
    创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回新对象。

考虑到要拷贝的对象可能嵌套很深,因此可以通过递归的方法来实现,优化上方代码:

1. 如果是原始类型,无需拷贝,可以直接返回
2. 如果是引用类型,可以创建一个新的对象,遍历需要克隆的对象,将需要克隆对象执行深拷贝后依次添加到新对象上
module.exports = function clone(target) {
    if (typeof target === 'object') {
    	// 此处要先判断是否是数组,因为数组也属于对象
        let cloneTarget = Array.isArray(target) ? [] : {};
        for (const key in target) {  // key 是属性名,target[key]是属性值
            cloneTarget[key] = clone(target[key]); // cloneTarget[key] 是创建属性名
        }
        return cloneTarget;
    } else {
        return target;
    }
};

详细可参考:https://www.jianshu.com/p/a447d7325736
3)js库lodash里面的cloneDeep实现
例如:

let obj = [{'a' : 1}, {'b': 2}]
let deepObj = _.cloneDeep(obj)
console.log(obj)

4.2 $nextTick的作用

首先我们要先明白一个道理:Vue的响应式并不是数据发生变化后,DOM立即跟着发生变化的,而是按一定的策略进行DOM更新的。

作用:$nextTick 是在下次 DOM 更新循环结束之后执⾏延迟回调,在修改数据之后使⽤ $nextTick,则可以在回调中获取更新后的 DOM,在下次 DOM 更新循环结束之后执行延迟回调

说白了就是:当数据更新了,在DOM更新后,自动执行该函数

什么时候用?

  1. Vue⽣命周期的created()钩⼦函数进⾏的DOM操作⼀定要放在Vue.nextTick()的回调函数中,原因是在created()钩⼦函数执⾏的时候,DOM 其实并未进⾏任何渲染,⽽此时进⾏DOM操作⽆异于徒劳,所以此处⼀定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。
  2. 当项⽬中改变data函数的数据,想基于新的dom做点什么,对新DOM⼀系列的js操作都需要放进Vue.nextTick()的回调函数中

想了解更详细的🉑️参考:https://www.jianshu.com/p/8efa5cba7d07

4.3 Vue生命周期

可点击参考文档详细了解

4. ES6面试题

4.1 var let const 区别

var: 存在变量提升;存在变量覆盖,已经被定义且赋值的变量,如果再次被赋值,则以后一次值为准;没有块级作用域;

const: 定义的是常量,声明之后必须赋值;定义的值不能去修改,否则报错;有块级作用域;不存在变量提升和变量覆盖;对于数组和对象的元素修改,不算做对常量的修改,不会报错。

let: 有块级作用域;不存在变量提升和变量覆盖;let不允许在相同的作用域中重复声明,注意是相同作用域,不同作用域重复声明不会报错

4.2 解构赋值

let a = 1; let b = 2; 如果在不声明第三个变量的前提下,使a=2, b=1?

答案:[a, b] = [b, a]

4.3 如何利用es6快速的去重?

let arr = [23, 12, 13, 33, 22, 12, 21]

let item = [...new Set(arr)]

4.4 Promise 面试题 以下代码的执行结果是?

const promise = new Promise((resolve, reject) => {
   console.log(1)
   resolve()
   console.log(2)
})
promise.then(() => {
   console.log(3)
})
console.log(4)

答案:1,2,4,3

解释:以上考察的是关于promise的原理,promise的构造函数是同步执行的,当new Promise的一瞬间,1,2 就立刻被执行,而 .then方法是异步执行的,当执行完1和2之后,会执行输出4,最后执行输出3

4.5 跨域的解决方法

跨域:只要协议、域名和端口号有一个不相同就会产生跨域问题。同源策略是一个安全策略。同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。

解决办法:

4.5.1. webpack 里的proxy

devServer: {
    proxy: {  //配置跨域
      '/api': {
        target: 'http://121.121.67.254:8185/',  //这里后台的地址模拟的;应该填写你们真实的后台接口
        changOrigin: true,  //用于控制请求头中的post值,默认开启
        pathRewrite: {
          /* 重写路径,当我们在浏览器中看到请求的地址为:http://localhost:8080/api/core/getData/userInfo 时
            实际上访问的地址是:http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /api
           */
          '^/api': '' 
        }
      },
    }
  }

4.5.2. jsonp (需要后端支持 )

方案1 *:通配符,全部允许,存在安全隐患(不推荐)。

一旦启用本方法,表示任何域名皆可直接跨域请求:
  1     server {
  2         ...
  3         location / {
  4             # 允许 所有头部 所有域 所有方法
  5             add_header 'Access-Control-Allow-Origin' '*';
  6             add_header 'Access-Control-Allow-Headers' '*';
  7             add_header 'Access-Control-Allow-Methods' '*';
  8             # OPTIONS 直接返回204
  9             if ($request_method = 'OPTIONS') {
 10                 return 204;
 11             }
 12         }
 13         ...
 14     }

方案2:多域名配置(推荐)

配置多个域名在map中 只有配置过的允许跨域:

map $http_origin $corsHost {
  2         default 0;
  3         "~https://zzzmh.cn" https://zzzmh.cn;
  4         "~https://chrome.zzzmh.cn" https://chrome.zzzmh.cn;
  5         "~https://bz.zzzmh.cn" https://bz.zzzmh.cn;
  6     }
  7     server {
  8         ...
  9         location / {
 10             # 允许 所有头部 所有$corsHost域 所有方法
 11             add_header 'Access-Control-Allow-Origin' $corsHost;
 12             add_header 'Access-Control-Allow-Headers' '*';
 13             add_header 'Access-Control-Allow-Methods' '*';
 14             # OPTIONS 直接返回204
 15             if ($request_method = 'OPTIONS') {
 16                 return 204;
 17             }
 18         }
 19         ...
 20     }

4.5.3. webpack plugin (插件)

npm i -S webpack-dev-middleware 安装中间键,把前端和后端的服务绑在一起

中间件

let webpack = require('webpack')

let middle = require('webpack-dev-middleware')

let compiler = webpack(require('./webpack.config.js'))

app.use(middle(compiler))

4.5.4. cors (后端解决)

var allowCrossDomain = function(req,res,next) {

        // 请求源

        res.header("Access-Control-Allow-Origin", "*")

        // 请求头 token

        res.header("Access-Control-Allow-Headers", "*")

        // 请求方法 get post put del

        res.header("Access-Control-Allow-Methods", "*")

        next();

}

app.use(allowCrossDomain )

4.6 git命令(实际工作中常用)

4.6.1 基本

1. git init 初始化git仓库 (mac中Command+Shift+. 可以显示隐藏文件)

2. git status 查看文件状态

3. git add 文件列表 追踪文件

4. git commit -m 提交信息 向仓库中提交代码

5. git log 查看提交记录

4.6.2 分支明细

(1)主分支(master):第一次向 git 仓库中提交更新记录时自动产生的一个分支。

(2)开发分支(develop):作为开发的分支,基于 master 分支创建。

(3)功能分支(feature):作为开发具体功能的分支,基于开发分支创建

4.6.3 分支命令

(1)git branch 查看分支

(2)git branch 分支名称 创建分支

(3)git checkout 分支名称 切换分支

(4)git merge 来源分支 合并分支 (备注:必须在master分支上才能合并develop分支)

(5)git branch -d 分支名称 删除分支(分支被合并后才允许删除)(-D 强制删除)

4.6.4 暂时保存更改的代码

(1)存储临时改动:git stash

(2)恢复改动:git stash pop

4.6.5 代码回退

git reset --soft HEAD~n

4.7 get与post请求有什么区别

get是从服务器上获取数据,post是向服务器传送数据。

POST比GET安全,因为数据在地址栏上不可见。

get方式提交的数据最多只能有1024字节,而post则没有此限制。

GET使用URL或Cookie传参。而POST将数据放在request BODY中。

GET与POST都有自己的语义,不能随便混用。

据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基,本可以无视。而在网 络环境差的情况下,两次包的TCP在验证数据包完整 性上,有非常大的优点。post 发送两次,get 只发送一次。

4.8 cookie、localStorage、sessionStorage的区别

共同点: 都是保存在浏览器端、且同源的

不同点:

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。

存储大小限制也不同:

cookie数据不能超过4K,sessionStorage和localStorage可以达到5M

sessionStorage:仅在当前浏览器窗口关闭之前有效;

localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;

cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭

作用域不同

sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;

localstorage:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在

cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在

4.9 async 和 await 的区别

区别:

async是来定义函数的,定义异步函数,打印函数名可以得到一个promise对象,言外之意可以通过这个 函数名称.then 这个方法

await 后面跟的是任意表达式,一般使用promise的表达式

async 内部实现,有返回值 成功返回promise.resolve() ,出错返回promise.reject() 返回值用catch捕获

await 等待后面的promise对象执行完毕,拿到了promise.resolve()的值之后,执行后面的代码。await后面的表达式,能是promise.reject(),所以建议await放在try…catch语句中

优点:async和await 属于es7语法。编写方便,提高程序效率,避免了回调地狱

补充:promise和async和await的区别

promise es6语法,promise中包含catch,async需要自己定义catch

promise 提供的方法会多一些,all、race等方法,aync中是没有的。

5.0 setTimeout 时间为0, 以及误差的原因

setTimeout,如果时间为0,则会立即插入队列,不是立即执行,等待前面的代码执行完毕。

5.1 求数组的最大值?

function getMaxArryNum(arr) {
	return Math.max(...arr)
} 

getMaxArryNum([1,2,3,4,5,6])

5.2 求数组的最小值?

const getMinArryNum= (arr) => {
        return Math.min(...arr)
} 
getMinArryNum([1,2,3,4,5,6]) 

5.3 数组去重

5.3.1 利用filter 、indexof

const removeEqual = (arr) => {
	const result = arr.filter((item, index, self) => {
         return self.indexof(item) === index
	})
  return result
} 

removeEqual([1, 2, 2, 3, 4, 5, 5, 6, 7, 7])

第一次循环,传入元素1,index(1)的索引为0,而此时1的索引本来就是0,OK,满足。
第二次循环,传入元素2,index(2)的索引为1,而此时2的索引也是1,OK,也满足。
第三次循环,传入元素2,index(2)的索引为1,而此时2的索引为2,OK,不满足,被PASS,这里就是巧妙的借用了indexOf始终查找到第一次出现的位置。
总结
filter(x,index,self)可以为数组提供过滤功能,其中x代表元素,index是与X一同传入元素的索引,而self代表数组本身。
14. 生成从0 到 指定数字的数组

5.3.2 sort 、splice 实现去重

先对数组进行排序,再循环,如果相邻的两项相同,则删除一项,i–,再继续对比。

var arr = [1, 2, 3, 2, 4, 1];
arr.sort();

for (var i = 0; i < arr.length; i++) {
    if (arr[i] === arr[i+1]) {
        arr.splice(i,1);
        i--;
    }
}
console.log(arr); // [1, 2, 3, 4]

5.3.3 最短的方法,使用new Set([…])

var arr = [1, 2, 3, 2, 4, 1];
var newArr = new Set(arr);

console.log(newArr); // [1, 2, 3, 4];

5.3.4 indexOf 、push 实现

最简单的思路,先创建一个新数组作为容器,遍历原数组,判断每一项在新数组中是否存在,若不存在则把这一项push到新数组中,若存在则忽略。

var arr = [1, 2, 3, 2, 4, 1];
var newArr = [];

for (var i = 0; i < arr.length; i++) {
    if (newArr.indexOf(arr[i]) === -1) {
        newArr.push(arr[i]);
    }
}

console.log(newArr); // [1, 2, 3, 4]

5.3.5 对象属性名唯一性实现 数组去重

利用对象的属性名不能重复这一特性,创建一个对象,把数组中的值依次添加为对象的属性,再使用Object.keys()得到包含所有属性名的数组。但要注意对象的属性名都是字符串形式,所以在本例中还要把它们转回数字。

var arr = [1, 2, 3, 2, 4, 1];
var obj = {};
var res = [];

arr.forEach(n => obj[n] = 1); // 把每一项添加为对象的属性,重复的属性不会再次添加,而是修改已存在的属性的值

res = Object.keys(obj).map(n => +n); // 得到包含字符串属性名的数组并把每一项转换成数字

console.log(res); // [1, 2, 3, 4]

5.4. 数组求和

const arrSum = (arr) => {

        const temp = arr.reduce((pre, now) => {

                return pre+now

        },0)

        return temp

}

arrSum([1,2,3,4])

5.5 生成从0 到 指定数字的数组

const getArr = (startNum, endNum) => {

        let arr = []

        for(var i=startNum; i<=endNum; i++){

                arr.push(i)

        }

        return arr

} 

getArr(0,4)

5.6 js的数据类型

js 数据类型分为基本数据类型和复杂数据类型

基本数据类型:Boolean、Number、String、Null、Undefined

复杂数据类型: Object、Array、Function、Date

5.7 js的变量提升

在js中,变量和函数的声明会被提升到最顶部执行

函数提升高于变量的提升

函数内部如果用var声明了相同名称的外部变量,函数将不再向上寻找

匿名函数不会提升

5.8 this指向

this总是指向函数的直接调用者。

如果有new关键字,this指向new出来的对象

在事件中,this指向触发这个事件的对象

5.9 map和forEach的区别

forEach方法,是最基本的方法,遍历和循环。默认有3个参数:分别是遍历的每一个元素item,遍历的索引index,遍历的数组array

map方法,和foreach一致,不同的是会返回一个新的数组,所以callback需要有return返回值,如果没有会返回undefined

6.0 箭头函数和普通函数的区别?

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象

不可以当作构造函数,也就是说不可以使用new命令,否则会报错

不可以使用arguments对象,该对象在函数体内不存在,如果要用可以使用Rest参数代替

不可以使用yield命令,因此箭头函数不能用作Generator函数

6.1 es6新增

新增模版字符串

箭头函数

增加let、const来声明变量

for-of用来遍历数据-例如数组中的值

解构赋值

新增简单数据类型Symbol,独一无二的,不会与其他属性名冲突

将Promise对象纳入规范,提供了原生的Promise对象

6.2 数组方法汇总

map 循环遍历数组、返回一个新的数组

forEach 循环遍历数组,不改变原数组

push/pop 在数组的末尾添加/删除元素  改变原数组

unshift/ shift 在数组的头部添加/删除元素,改变原数组

join  把数组转化为字符串

some  有一项返回为true,则整体为true

every  有一项返回为true,则整体为false

filter 数组过滤

slice(start, end)  数组截取,包括开头,不包括截取,返回一个新的数组

splice(start, number, value)  删除数组元素,改变原数组

indexof/lastindexof: 查找数组项,返回对应的下标

concat:数组的拼接,不影响原数组,浅拷贝

sort:数组排序 改变原数组

reverse: 数组反转,改变原数组

6.3 项目性能优化方案

动态加载组件 <component :is="componA" />
减少http请求
减少DNS查询
使用CDN
避免重定向
图片懒加载
路由懒加载
减少DOM元素操作
使用外部js和css
压缩js、css、字体、图片等
使用iconfont字体图标、雪碧图等
避免图片的src为空
把样式表放在link中
把js放在页面的底部	

6.4 forEach 和 map的区别

  1. map有返回值且必须要return一个数组出来;而forEach没有返回值可直接打印结果
  2. map因为返回了数组可以进行链式操作,而forEach不行
  3. map可以用break;而forEach不可以用,否则会报错
  4. map不影响原数组;而forEach如果修改的是基本数据类型,不会修改原数组;如果修改的是引用数据类型且修改的是整个对象,不会修改原数据;如果是对象中的某一个属性,就会修改原数据
  5. 总体来说,map的速度比forEach快

6.5 forEach 和 for 循环的区别

  1. for循环是通过索引循环遍历每一个元素;而forEach是通过JS底层程序来实现循环遍历数组元素
  2. for循环可以通过break终止循环;而forEach不可以,会报错
  3. for循环可以通过控制循环变量的数值来控制循环次数;而forEach不行
  4. for循环可以在循环体外调用循环变量;而forEach不行
  5. for循环的执行效率比forEach高
    ==补充:==既然for比forEach效率高,为何不用for
    原因: for循环只能通过索引下标数值来操作数组元素,而forEach可以设置参数,操作上更加遍历。
    实际工作中,可自行根据需要选择

6.6 Vue2 和 Vue3 的区别

  1. api 类型不同
    vue2是选项式API(optionApi),vue3是组合式API(compositionApi)
  2. 双向数据绑定原理不同
    vue2 通过使用object.definePropert对数据劫持结合发布者订阅者的模式来实现
    vue3 是通过ES6的新特性proxy来劫持数据,当数据改变时发出通知
    区别:vue2双向数据绑定不能检测到下标的变化;vue3proxy可以劫持整个对象,并且返回一个新的对象
  3. 建立数据
    vue2 是将数据创建在data中
    vue3 则是需要使用setup()方法,此方法在组件实例化的时候触发
  4. 生命周期钩子不同
Vue2--------------vue3
beforeCreate  -> setup()
created       -> setup()
beforeMount   -> onBeforeMount
mounted       -> onMounted
beforeUpdate  -> onBeforeUpdate
updated       -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed     -> onUnmounted
activated     -> onActivated
deactivated   -> onDeactivated

  1. 对文件上的引用
    vue2 中new出的实例对象,所有的东西都在vue对象上,无论是否用到,都是跑一边
    vue3.0中可以用ES module imports按需引入,如:keep-alive内置组件、v-model指令,等等。
  2. 项目启动方式
    vue2 是 npm run dev
    vue3 是 npm run serve
  3. 语法上的区别
    取消v-model,采用modelValue
<input v-model="value" />

<input modelValue="value" />

弃用全局API new Vue 采用 createAPP

const app = Vue.creatApp()

不能使用vue.nextTick()或this.$nextTick(),改用如下:

import { nextTick } from 'vue'
nextTick(() => {
  // something
})

6.7 call、apply、bind使用和区别

call作用:
语法: fun.call(thisArg, arg1,arg2, …)
thisArg: fun函数运行时指定的this值; arg1,arg2参数;返回值是函数的返回值

  1. 调用函数
  2. 改变this指向
	const obj = {name: 'red'}
	function fn(x, y) { 
		console.log(this)
		console.log(x+y) // 3
	}
	fn.call() // 此时this指向为window
	fn.call(obj, 1, 2) //此时this指向为obj

apply作用:
语法: fun.apply(thisArg, [argsArray])
thisArg: fun函数运行时指定的this值;argsArray数组参数,传递的值必须在数组里面;返回值是函数的返回值

  1. 调用函数
  2. 改变this指向
	const obj = {name: 'red'}
	function fn(x, y) { 
		console.log(this)
		console.log(x+y) // 3
	}
	fn.apply() // 此时this指向为window
	fn.apply(obj, [1, 2]) //此时this指向为obj

apply主要和数组有关系,可以使用Math.max()求数组最大值等
// const max = Math.max(1, 2, 3)
const max = Math.max.apply(Math, [1,2,3])

bind作用:
语法:fun.bind(thisArg, arg1, arg2, …)
thisArg: fun函数运行时指定的this值;arg1, arg2传递的参数;返回值是拷贝后的新函数,且this更改过
注意:不会调用函数,但是可以改变this指向

const obj = {name: 'tiger'}
function fn() {
	console.log(this)
}
const fun = fn.bind(obj)
console.log(fun) // 打印出来是fn()函数,但是this指向obj
fun()

总结:
相同点:
三者都可以改变this的指向
不同点:
call和apply 可以调用函数
call和apply,传递的参数不同,call是以arg1,arg2形式传递,apply是以数组形式传递[argsArr]
bind不会调用函数

  • 51
    点赞
  • 269
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
以下是一些可能会在2023前端面试出现的题目: 1. 什么是闭包?如何使用闭包解决作用域问题? 2. 解释一下 JavaScript 的继承,有哪些实现继承的方法? 3. 解释一下 JavaScript 的事件循环机制(Event Loop),以及它与异步编程的关系。 4. 如何优化网页的性能?包括什么方面? 5. 解释一下 Vue.js 的双向数据绑定原理,它是如何实现的? 6. 解释一下 React 的虚拟 DOM 是什么,以及它的优点和缺点是什么? 7. 解释一下 CSS 盒模型的概念,包括 content、padding、border 和 margin。 8. 解释一下 CSS Flexbox 布局的概念,以及如何使用它进行布局。 9. 解释一下 CSS Grid 布局的概念,以及如何使用它进行布局。 10. 解释一下 Webpack 的概念和作用,以及如何使用它进行模块打包。 11. 解释一下 HTTPS 的概念和原理,以及它与 HTTP 的区别。 12. 解释一下 Web 应用程序的安全问题,包括 XSS 和 CSRF 攻击。 13. 解释一下前端工程化的概念和作用,以及常用的前端工程化工具。 14. 解释一下 Node.js 的概念和作用,以及如何使用它进行后端开发。 15. 解释一下 TypeScript 的概念和作用,以及如何使用它进行编程。 16. 解释一下 WebAssembly 的概念和作用,以及它与 JavaScript 的区别。 17. 解释一下 PWA 的概念和作用,以及如何开发一个 PWA 应用程序。 18. 解释一下 GraphQL 的概念和作用,以及如何使用它进行后端开发。 19. 解释一下微前端的概念和作用,以及如何使用它进行前端架构设计。 20. 解释一下 Serverless 的概念和作用,以及如何使用它进行后端开发。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_LiuP

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值