前端问题自我总结

112 阅读11分钟

早餐奶

1. 自我介绍
2. 说说async、await的设计和实现

设计:

  • async是用来声明一个异步函数,该函数返回一个promise对象;await用来等待一个promise完成,并获取其结果;

  • 当函数执行的时候,一旦遇到awiait就会先返回,等到触发的异步操作完成,再接着执行函数体内后的语句; 实现: async/await语法糖就是使用Generator函数+自动执行器来运作的;

3. 深拷贝需要注意哪些问题?
  1. 性能问题: 深拷贝操作需要遍历对象及其所有的嵌套对象,可能会导致性能问题,尤其是大型对象;
  2. 基本类型和特殊类型的处理
    • 要处理基本类型(字符串,数字,布尔值等)和特殊类型(函数,正则表达式和日期对象等)。
    • 基本类型,直接返回其值即可,特殊类型,可以选择直接引用原始对象,而不进行复制;

  1. 处理循环引用: 对象属性之间存在相互引用的情况,导致递归复制陷入无限循环,使用一个额外的数据结构(map活weakmap)来存储已经复制的对象;
  • 实现: 浅拷贝: object.assign(); slice();JSON.stringify()和JSON.parse();

    递归实现深拷贝

  • 应用场景: 深拷贝:

        需要创建一个完全独立的副本时,防止对原对象得修改;
        在对象状态管理中,需要创建对象的副本以记录历史状态、实现撤销和重做等操作。 
    

    浅拷贝:

     当只需要复制对象的引用,而不需要创建对象的副本时。
    
4. 判断数组的方法有哪些?手写一个instanceof方法
   1. Array.isArray() //返回一个布尔值 true是,false2. 构造函数属性constructor //arr.constructor==Array; true是,false3. Object.getPrototypeOf()方法返回指定对象(arr)原型,与array对比
       //Object.getPrototypeOf(arr) == Array.prototype;true是,false4. instanceof运算符 // arr1 instanceof Array; true是,false
    function myInstanceof(obj, constructor) {
        //获取对象原型
        let proto = obj._proto_;
        //获取对比数据原型
        let prototype = constructor.prototype;
        while(proto) {
            if(proto === prototype) {
                return true; //相同原型,返回true
            }
            proto = proto._proto_; //继续查找上一层
        }
        return false //原型链结束都没找到,返回false
    }
    //示例
    console.log(myInstanceof([1,2,3]), Array)
5、如何借鉴React diff算法的思想,实现各种情况树节点的更新
  diff思想:
      用于更新虚拟DOM树的一种算法;通过比较虚拟DOM树的差异,
      然后对有差异的部分进行更新,从而提高性能;
  原理:
  1.逐层比较:从根节点开始逐层比较新旧虚拟DOM树的节点,根节点不同,整个树更新,
      相同,继续比较子节点;
  2.列表比较:使用Key属性来确定哪些节点需要更新,删除或添加;
       节点被移动新位置,采取尽量复用,而不是重新创建;
  3.不同类型的节点;节点类型不同,直接删除旧节点,创建新节点替换;
  4.属性比较:逐个比较属性的值,属性值不同,更新该属性;
  5.子节点比较:递归调用自身,继续比较子节点的差异;
   

// 通过递归比对新旧两颗虚拟DOM树的差异,核心思想:相同类型的节点,更新需要变化的属性和递归处理子节点;节点类型不同直接替换整个节点;

    function updateTreeNode(newNode, oldNode) {
        // 如果旧节点不存在,直接创建新节点
         if (!oldNode) {
            createNode(newNode);
            return;
        }
        // 如果新节点不存在,直接删除旧节点
        if (!newNode) {
            removeNode(oldNode);
            return;
        }
        // 如果两个节点相同,则更新属性
        if (newNode.type === oldNode.type) {
            updateNodeAttributes(oldNode, newNode.attributes);
            // 递归比对子节点
            const oldChildren = oldNode.children || [];
            const newChildren = newNode.children || [];
            oldChildren.forEach((child, index) => {
                updateTreeNode(newChildren[index], child);
            });
        } else {
            // 节点类型不同,直接替换旧节点
            replaceNode(oldNode, newNode);
        }
}
6. 怎么让中间页携带上cookie?
疑似:1`withCredentials为true;` 2`后端设置Header(请求头)`
7. 说说跨域问题
```js
    跨域: 指浏览器不能执行其他网站的脚本。是由浏览器的同源策略造成的,
    是浏览器对javascript实施的安全限制;
    同源指的是: 域名,协议,端口相同
解决方案:
   1. JSONP: 在HTML标签里,比如script,img
   2. jQuery Ajax;
   3. CORS,通过服务端设置Access-Control-Allow-Origin请求头,带cookie,前后都设置
   4. nginx代理跨域
   5. postMessage跨域
   6. nodejs中间件代理跨域
   7. vue框架 通过wabpack设置prosy设置
```
8. 讲讲webpack的整个工作流程
9. 有没有用过webpack的loader解决过一些具体的场景问题?
     loader用于对模块得“源代码”进行转换,在import或“加载”模块时3预处理文件;
     
     style-loader: 将css添加到DOM的内联样式标签style里;
     css-loader: 允许将css文件通过require的方式引入,并返回css代码;
     less-loader: 处理less;
     sass-laoder: 处理sass;
     babel-loader: 用babel来转换ES6ES5
10. ES5怎么实现继承?讲讲对原型链的理解
11. require和import的区别?
1.加载时机不同
    import是在编译时加载,必须放在文件的开头;
    require是在运行时加载,可以放在代码的任何位置;
2.所属规范不同
    import是ES6(ECMAScript 2015)引入的关键字,属于ES模块化语法规范;
    require是CommonJS规范的一部分,主要用于Node。js环境
3.本质
    require是赋值过程;
    import是解构过程;
   
12. 有没有什么想问我的?
什么是事件代理
    事件代理:  指利用JavaScript的事件冒泡机制,将子元素事件委托到父元素来处理,
    而不是每个子元素单独绑定事件监听器;
    工作原理: 
        1.事件冒泡: 但某个子元素触发事件(点击,键盘等)时,事件会从目标逐层向上传递到它的父元素,直至根元素(通常是document).
        2.父元素处理子元素事件: 利用事件冒泡机制,可以在父元素上监听事件,通过事件对象event.target获取具体触发事件的子元素,然后根据需要进行处理;
    优点: 可以提高性能
Object.freeze 的作用
     是javaScript用于冻结对象的方法,冻结后的对象变的不可变,不能添加、删除或修改其属性,
     优化性能;(避免vue初始化时候,做一些无用的操作,从而提高性能)
MVC和MVVM
    MVVM是一种设计思想; 即 Model-View-ViewModel的简写,即模型-视图-视图模型。
    View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互;
    Model 和 ViewModel 之间的交互是双向的, 
    因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
    将模型中的数据转化成视图可直接使用的形式;简化了视图和模型直接的交互逻辑。
    关注业务逻辑,不需要手动操作DOM,
   MVC即Model-View-Controller。

MVVM主要解决了MVC中大量的dom操作是页面渲染性能降低,加载速度变慢,影响用户体验

Flex布局,Gird布局对比
   flex: 是一维布局系统,适合做局部布局;
       一次只能处理一个维度上的元素布局,一行或者一列;
   gird:是二维布局系统,通常用于整个要页面的规划;
       可同时在行和列上进行布局;
性能优化

HTML

    1.避免HTML中书写CSS代码,因为这样难以维护;
    2.使用语义化标签,减少css代码,增加可读性和SEO;
    3.减少标签的使用,DOM解析式一个大量遍历的过程,减少不必要的标签,能降低遍历次数;

CSS

   1.优化选择器路径:使用。c{},而不是 .a .b .c{};
   2.选择器合并:共同的属性内容提取出来,压缩空间和资源开销;
   3.精准样式,使用padding-left:10px 而不是padding:0 0 0 10px;
   4.雪碧图
   5.为0值去单位:添加兼容性;
   6.压缩文件大小,减少资源下载负担;

JS

   1.尽可能合并JS代码:提取公共方法,进行面对对象设计;
   2.尽可能少地创建DOM,而是在HTML和CSS中使用display: none 来隐藏;
   3.压缩文件大小,减少资源下载负担;

VUE

vue数据双向绑定

简单理解

   采用数据劫持结合发布者订阅者模式的方式;
   通过Object.defindProperty()劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,
   触发相应的监听回调。

加深

  • 采用数据劫持结合发布-订阅模式实现的;
  • 通过Object.defindProperty()来实现数据劫持,对于每一个数据属性,vue会将它转化为一个getter和setter,从而拦截对数据的访问和修改操作。

发布-订阅模式

  • 视图和数据之间的关联是通过一个叫Watcher的类来实现的。Watcher作为发布者和订阅者之间的桥梁,每一个Watcher订阅了某个数据属性,当该属性发生变化时,Watcher就会被通知触发相应的视图更新;

实现步骤:

  1. Dep(依赖收集器):每个数据属性都有一个对应的 Dep 对象,用来收集所有依赖于该属性的 Watcher。
  2. Watcher:当视图模板被解析时,Vue 会为每一个依赖数据的地方创建一个 Watcher,并将其添加到对应数据属性的 Dep 中。
  3. 通知更新:当数据属性发生变化时,对应的 Dep 会通知所有相关的 Watcher,进而触发视图更新。
vue 封装的数组方法,手写一下
vue data 属性值发生变化,会不会立即更新
vue watch 监听到属性值发生变化,是怎么处理的
  1. 依赖追踪:Vue使用ES6的Proxy对象(或在Vue 2中使用Object.defineProperty)来劫持(或“拦截”)你对数据的访问和修改操作。这使得Vue能够跟踪每个属性的依赖关系。
  2. 派发更新:当你修改一个响应式属性时,Vue会通知依赖该属性的观察者(watcher)。这通常是通过调用观察者的回调函数来实现的。
  3. 执行回调:一旦通知被发送,Vue就会执行与该属性相关联的观察者的回调函数。回调函数可以接收新旧值作为参数,允许你执行一些逻辑,比如更新DOM、计算新值等。
mixin 的原理
extends 的原理
自定义指令的原理和用法
什么是高阶函数,什么是高阶组件
v-if v-for 为什么不能连用
  • 性能问题
    • 同时使用时,Vue会在每次循环渲染时重新计算v-if指令的值。会导致额外的性能开销,尤其处理大量数据时;此外v-for的优先级高于v-if(在vue3中,v-if 具有比 v-for 更高的优先级。)Vue 会首先为每一个数组或对象元素创建 DOM 元素,然后再根据 v-if 的条件来销毁其中一部分。这实际上是一种资源浪费,因为很多被创建的 DOM 元素可能最终不会被渲染到页面上。
  • 逻辑复杂性
    • 会使代码逻辑变得复杂,增加代码的阅读难度和维护成本;
  • 渲染问题
    • 同时使用 v-if 和 v-for 可能会导致意外的渲染结果。
v-for 事件代理
手写 v-for
provide inject 的原理
vue parent 和 children 之间的通信
attrsattrs 和 listeners 的作用
手写 vue router
手写 vuex
vuex 是怎么全局注册的
  1. 首先安装Vuex
    npm install vuex --save
    
  2. 创建一个Vuex Store。咋项目src目录下新建一个store文件夹,然后在该文件夹中个创建一个index.js文件。
  3. 在store/index.js文件中定义store并导出。
    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(Vuex);
    
    const store = new Vuex.Store({
      // state, mutations, actions, getters 等
    });
    
    export default store;
    
  4. 在主文件main.js中引入store并注册到vue实例。
      import Vue from 'vue';
      import App from './App.vue';
      import store from './store';
    
      new Vue({
        store,
        render: h => h(App),
      }).$mount('#app');
    
redux 和 vuex 的区别
composition api 和 vue2 的区别
  • 虚拟dom
    • 虚拟DOM:就是用JS来模拟DOM结构。
  • 优点:

    • 性能优化: 减少对真是DOM的操作次数,提高渲染效率;
    • 跨平台:可以渲染到不同的平台,例如浏览器、移动端等;
    • 简化开发:开发者可以使用声明式的方式操作虚拟DOM,无需关心底层DOM操作细节;

虚拟 DOM 是 Vue.js 渲染机制的核心,它通过比较新旧虚拟 DOM 的差异,最终只更新真实 DOM 中需要改变的部分,从而优化性能。

手写 diff
手写 EventBus