前端面试题八股文

JS

ES6新特性

1. let const (let const var 区别)

2. promise  (promise与async await的区别)

3.箭头函数 (与普通函数的区别)

4.解构赋值

5.模板字符串

6.set    set 与map的区别

                   ①set:数组去重、集合、值的集合

                   ②map:数据存储、字典、键值对的集合,可用get获取值

                   ③两者无论是键还是值都可以是任意数据类型,都可以用for..of遍历

             map与对象的区别

                   ①键值设置类型方面

                      对象的键只能是字符串或者是symbol,其他类型也会被强制转换成字符串

                      map的键值对可以为任何类型

                   ②初始化

                       对象可以直接使用字面量进行初始化

                       map需要使用map()构造函数进行初始化

                   ③迭代方面

                       对象不可以使用for..of..迭代

                       map可以使用for..of..,或者用Map.prototype.forEach可以进行迭代

                   ④序列化

                       对象可以使用JSON进行序列化,map不可以

                   ⑤性能

                       Map对象在涉及频繁删除,添加键值对的过程中表现的会更好,

                       普通对象没什么优化

7.迭代器 iterator

      String Array Map Set Dom元素是所有内置的可迭代对象  可以用for..of..进行遍历

8.扩展运算符 ...

      可以实现浅拷贝 

9.新增模块化概念 import export

10.class 类

for...in...  for...of..  map  forEach的区别

for..in..   for..of..区别

   for...in.. 获取的是数组的下标,可以遍历普通对象和可迭代对象

   for...of.. 获取的是可迭代对象的值。

    let arr = [1, 2, 3]
    let obj = {
      name: 'Lucky',
      age: 18,
      hobby: ['swimming', 'running', 'ball']
    }
    for (let i in arr) {
      console.log(i);  //0,1,2
    }
    for (let i in obj) {
      console.log(i);  //name,age,hobby
    }
    for (let i of arr) {
      console.log(i)  //1,2,3
    }
    for (let i of obj) {
      console.log(i); //Uncaught TypeError: obj is not iterable
    }

forEach 与 map的区别

  map会返回一个新的数组,不会修改原数组,一般用于需要修改数据的场景

  forEach没有返回值,一般用于你并不打算改变数据,只是想用数据做一些事情,比如将数据存储,或者打印。

  

    let arr = [1, 2, 3]
    arr.forEach((item, index, arr) => {
      return arr[index] = item * 2
    });
    console.log(arr); //[2,4,6]      


    let arr = [1, 2, 3]
    let newArr = arr.map((item, index, arr) => {
      return item * 2
    })
    console.log(arr); //[1,2,3]
    console.log(newArr);//[2,4,6]

事件

文档与浏览器发生交互的瞬间,就会发生事件

分为两种:事件捕获,事件冒泡

事件冒泡:IE公司提出,从目标事件向外一层一层触发(从最明确到最不明确),事件委托                    就是利用了这个,将事件绑定在父元素身上,如果子元素不想触发可以直接阻止

                   event.stopPropagation()

                    .stop

                    addEventListener('click',函数名,false/ture)

事件捕获:网景公司提出,由外向内

事件流:就是事件执行的顺序

执行顺序:事件捕获 事件流 事件冒泡

作用域与作用域链

作用域:一块独立的区域,设置的变量不会泄露,不同作用域下的相同变量互不干扰。

     分为:全局作用域:在外层函数 、变量 ;赋值但是未被定义的变量;window上的属性

                函数作用域

                块级作用域(es6新增),通过let const实现 {}

作用域链:当我们访问一个变量时,会从当前作用域中进行查找,如果没有向父级作用域进                    行查找,最终查找到全局作用域上。这一层一层的查找关系就是作用域链。

js数据类型

基础类型:Number String Boolean Null Undefined Symbol Bigint

引用类型:Object Array Function Date RegExp..

如何判断是哪种?

     typeof

     intanceof

     constructor

    Object.propetype.toString.call()

各个数据类型之间的转换,隐式转换规则

防抖节流实现原理

防抖:事件被频繁的触发只执行最后一次,上一次的执行会被清空重新执行

function debounce(fn,delay){
  let timer = null
  return function(){
    clearTimeout(timer)
    timer = setInterval(()=>{
      fn.call(this)
    },delay)
  }
}

节流:设置n秒时间,n秒内只执行一次。

      function throttle(fn, delay) {
        let timer = null;
        return function () {
          if (!timer) {
            timer = setTimeout(() => {
              fn.call(this);
              timer = null;
            }, delay);
          }
        };
      }

深浅拷贝

浅拷贝:基础类型直接赋值,引用类型复制的是内存地址

深拷贝:开辟一块新的内存地址存放数据,两者互不干扰

举个例子吧

let obj1 = {a:1}
let obj2 = obj1
obj1.a = 2

浅拷贝
console.log(obj1.a)  //2
console.log(obj2.a)  //2

深拷贝
console.log(obj1.a)  //2
console.log(obj2.a)  //1

如何实现深浅拷贝:

  浅拷贝:...扩展运算符

               Object.assign()

  深拷贝:JSON.parse(JSON.stringify())   但是日期,正则不能正确转换

                Jquery.extend()

                手写cloneDeep

    function deepClone(obj) {
      if (typeof obj !== "object" || obj == null) {
        return obj;
      }
      let result;
      if (obj instanceof Array) {
        result = [];
      } else {
        result = {};
      }
      for (let key in obj) {
        //对象原型上的数据不需要进行拷贝
        if (obj.hasOwnProperty(key)) {
          result[key] = deepClone(obj[key]);
        }
      }
      return result;
    }

call、apply、bind的区别

都是用来改变this指向的

call与apply类似,只是传参方式不同, call用扩展运算符来传参就可以把apply来代替了

call,apply定义完是直接调用的

bind是不会立即执行的 而是传回一个已经改变this指向的函数,可继续传参,需要()调用

函数柯里化

是将一个多参数的函数转换为一个参数的方法,

传递一个参数,并返回一个函数,让返回的函数处理其余的参数

利用了闭包特点

我觉得好处就是不需要将所有需要用到的参数在最开始就要传进去,可以根据自己的逻辑逐渐将参数传递,那样更加清晰明了

举例

    let add = function (x, y) {
      return x + y
    }
    add(1, 2)

    let add1 = function (x) {
      return function (y) {
        return x + y
      }
    }
    add1(1)(2)

高阶函数

其实就是将函数作为参数,返回值也是一个函数

function hFunction(param,callback){
   return callback(param)
}

Js运行机制

单线程 

为了防止阻塞分为同步任务,异步任务(微任务,宏任务)

执行顺序 同步任务>微任务>宏任务

微任务:promise.then..   async await..  process.nextTick(Node.js环境)  它是执行在所有异                                                                                                                       步任务最前面的

宏任务:定时器,ajax,script整体代码,Dom事件

Promise

异步编程的解决方案

其自身有race、all、resolve、reject方法

原型上有then,catch方法

有三种状态 pedding、fulfilied、rejected 不可逆

可解决低于回调地域的问题

本质就是用链式调用的方式执行回调函数

Promise.all()使用场景多个异步操作必须成功才会执行后续操作

Promise.race() 可以设置异步操作的超时时间

.then会返回一个promise对象,以保证then可以继续进行链式调用

Promise.all如果有一个请求失败了,如何能得到其余的正确结果

    Promise.all([
      Promise.reject({ code: 500, mag: "服务器异常" }),
      Promise.resolve({ code: 200, list: [] }),
      Promise.resolve({ code: 200, list: [] })
    ].map(p => p.catch(e => e))
    )
      .then(res => {
        console.log("res=>", res);
      }
      )
      .catch(error => {
        console.log(error);
      })

//核心就是.map(p => p.catch(e => e)),map的每一项都是promise,catch返回的结果都会被promise.resolve进行包裹

async await

同步写法,执行异步操作

函数前面有async关键字,说明该函数内部有异步操作,调用该函数时,会返回一个promise对象。

使用场景:第一步执行得到的结果返给第二部使用

                  使用ajax获取第一个接口返回的数据,第二次接口调用依赖这个数据进行调用

clientWidth、offsetWidth,scrollWidth区别

都是元素视图的属性

clientWidth:元素内容可视区宽度 padding+content(width)

offsetWidth:元素的实际宽度 padding + content + border

scrollWidth:元素内容实际宽度 如果没有滚动条的话 = clientWidth  如果有,加上滚动条的宽度

vite和webpack的区别

Webpack基于commonjs先打包合并然后请求服务器,更改一个模块,其他有依赖关系的模块都会重新打包(成为一个大的bundle文件);

webpack从一开始dev server的时候,就会进入入口文件,找到找到整个项目中所有依赖文件,然后对这写文件进行编译,打包到boundle文件中,项目越大,启动就会越慢

Vite基于es6 module,自动向依赖的module发请求,服务端按需编译返回,改动一个模块仅仅会重新请求该模块;有利于http2的缓存和压缩

vite启动的时候是不需要打包的,也就是说不会对模块的依赖进行拆分编译,只有当浏览器对某个模块发送请求的时候,才会对该模块进行编译,实现的是按需编译,所以启动速度会比webpack快

axios怎么取消上次请求

       最近在项目中遇到一个问题,在连续发送同一请求时,如果第二次请求比第一次请求快,那么实际显示的是第一次请求的数据,这就会造成数据和我选择的内容不一致的问题。解决的方案:在后续发送请求时,判断之前的请求是否完成(同一个接口),如果未完成则立即取消。然后在发送新的请求。

①可以使用CancelToken.source工厂方法创建cancel token

var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
 cancelToken: source.token
}).catch(function(thrown) {
 if (axios.isCancel(thrown)) {
  console.log('Request canceled', thrown.message);
 } else {
  // 处理错误
 }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

② 还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:

var CancelToken = axios.CancelToken;
var cancel;

axios.get('/user/12345', {
 cancelToken: new CancelToken(function executor(c) {
  // executor 函数接收一个 cancel 函数作为参数
  cancel = c;
 })
});

// 取消请求
cancel();

js如何封装动画函数

(1).获得盒子当前位置

(2).让盒子在当前位置加上1个移动距离

(3).利用定时器不断重复这个操作

(4).加一个结束定时器的条件

(5)..注意此元素需要添加定位,才能使用element.style.left


        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: aqua;
        }
  

    <div></div>


    //实现步骤:
    1.获得盒子当前位置
    2.让盒子在当前位置加上1个移动距离
    3.利用定时器不断重复这个操作
    4.加一个结束定时器的条件
    5.注意此元素需要添加定位,才能使用element.style.left -->
    <script>
        var div = document.querySelector('div');
        var animation = setInterval(function(){
            //停止动画本质是停止定时器
            if (div.offsetLeft>=400) {
                clearInterval(animation);
            }
            div.style.left = div.offsetLeft + 5 + 'px';
        },30)
    </script>

VUE

vuex开启模块化命名空间后有两个模块AB怎么在B模块的actions中拿到A模块的内容

1.state

//B模块
const actions = {
    async ['shop'](store, config = {}) {
        const { commit, dispatch, state, rootState } = store
        ...
       //rootState 通过根state就会获得A模块中的state数据
    }
}

2.actions

const actions = {
    async ['shop'](store, config = {}) {
        const { commit, dispatch, state, rootState } = store
        ...
        dispatch('xxxx', {}, {root: true}) // 调用其他模块的 actions
    }
//dispach 三个参数 第一个"xxx"为A模块的异步操作的路径
                 // 第二个是穿给actions的数据,如果没有数据就写成{}
                 // 第三个配置选项 必须写 声明此actions不是当前模块的
               

3.mutations 

const actions = {
    async ['shop'](store, config = {}) {
        const { commit, dispatch, state, rootState } = store
        ...
        commit('xxxx', {}, {root: true}) // 调用其他模块的 mutations
    }
//dispach 三个参数 第一个"xxx"为A模块的异步操作的路径
                 // 第二个是穿给actions的数据,如果没有数据就写成{}
                 // 第三个配置选项 必须写 声明此actions不是当前模块的

4.getters

//B模块
const actions = {
    async ['shop'](store, config = {}) {
        const { commit, dispatch, state, rootState ,rootGetters} = store
        ...
       //rootGetters 通过根state就会获得A模块中的getters数据  rootGetters['gettersAxxx']
    }
}

算法

求出数组的最大值最小值

    let arr = [1, 3, 5, 4, 9, 7]
    // 1.借助es6的扩展运算符
    console.log(Math.max(...arr));
    console.log(Math.min(...arr)); 

    // 2. 数组的sort的方法进行排序
    let sortArr = arr.sort((a, b) => a - b) //sort会改变原数组
    console.log(sortArr[0]);
    console.log(sortArr[sortArr.length - 1]);

    // 3.借助reduce在原型上添加方法
    Array.prototype.max = function () {
      return this.reduce((pre, next) => {
        return pre < next ? next : pre
      })
    }
    Array.prototype.min = function () {
      return this.reduce((pre, next) => {
        return pre < next ? pre : next
      })
    }
    console.log(arr.max());
    console.log(arr.min());
    
    // 4.在原型上通过for循环将前后两个值进行比较
    Array.prototype.max = function () {
      let max = arr[0]
      let len = arr.length
      for (let i = 1; i < len; i++) {
        if (arr[i] > max) max = arr[i]
      }
      return max
    }
    Array.prototype.min = function () {
      let min = arr[0]
      let len = arr.length
      for (let i = 1; i < len; i++) {
        if (arr[i] < min) min = arr[i]
      }
      return min
    }
    console.log(arr.max());
    console.log(arr.min());

求两个数组的交集

 // 求出两个数组的交集
    let arr1 = [1, 2, 3, 4, 5, 3, 3]
    let arr2 = [3, 4, 5, 6, 7]


    // 1.双重for循环 先将两数组去重
    let arrCoin = []
    arr1 = [...new Set(arr1)]
    arr2 = [...new Set(arr2)]
    let len1 = arr1.length
    let len2 = arr2.length
    for (let i = 0; i < len1; i++) {
      for (let j = 0; j < len2; j++) {
        if (arr1[i] == arr2[j]) {
          arrCoin.push(arr1[i])
        }
      }
    }
    console.log(arrCoin);

    // 2.for循环+includes
    let arrCoin = []
    arr1 = [...new Set(arr1)]
    arr2 = [...new Set(arr2)]
    let len1 = arr1.length
    for (let i = 0; i < len1; i++) {
      if (arr2.includes(arr1[i])) {
        arrCoin.push(arr1[i])
      }
    }
    console.log(arrCoin);

    // 3.filter + includes
    let arr3 = arr1.filter((i) => {
      return arr2.includes(i)
    })
    console.log(arr3);

求出两个数组的差集

    // 求出两个数组中不一样的值
    let arr1 = [1, 2, 3, 4, 5, 3, 3]
    let arr2 = [3, 4, 5, 6, 7]
    // 1.合并去重 - 交集
    // 合集
    let arr3 = [...new Set(arr1.concat(arr2))]
    // 交集
    let arr4 = [...new Set(arr1.filter((i) => {
      return arr2.includes(i)
    }))]
    let arr5 = arr3.filter((i) => {
      ...
    })

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BoZai_ya

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

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

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

打赏作者

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

抵扣说明:

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

余额充值