【面试题库】记录(二)

256 阅读11分钟

一、com 1

xiang wang

1. Vue响应式原理

答案:Vue2 是Object.defineProperty 与 观察者模式。Vue3是proxy 与 观察者模式

image.png

1) 检测变化的注意事项

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化

1.1) 对于对象

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

var vm = new Vue({ data:{ a:1 } }) // `vm.a` 是响应式的 vm.b = 2 // `vm.b` 是非响应式的

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。例如,对于:

Vue.set(vm.someObject, 'b', 2)

您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

this.$set(this.someObject,'b',2)
1.2) 对于数组

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

举个例子:

var vm = new Vue({
    data: {
        items: ['a', 'b', 'c'],
    },
});
vm.items[1] = 'x'; // 不是响应性的
vm.items.length = 2; // 不是响应性的

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用 splice

vm.items.splice(newLength)

2. webpack loader是否有顺序,与插件的区别

答案:借鉴了函数式 函数组合的思想: - webpack的加载从右往左进行

2.0 webpack loader与plugin的区别与实现loader和插件

Loader其实就是一个转换器,把你输入的内容翻译一遍,本质上是没有什么变化的,就像中文翻译成英文一样

相对于Loader来说,其实plugin的机制更加灵活,它可以在webpack的运行过程中改变输出结果。简单来说就是为输出添砖加瓦

实现Loader和插件 zhuanlan.zhihu.com/p/28245984

module:{
        rules:[{
            test:/\.js$/,
            use:[{
                loader:'babel-loader',
                options:{
                    presets:['react']
                }
            }]
        }]
    }

2.1 url-loader

url-loader引入图片,可以说它是file-loader的增强版

url-loader会把我们的图片使用base64的形式编码成另外一种字符串,网页是可以识别这种编码的东西的,这样的好处是,它减少了图片的请求,你只要请求回了这个页面,图片也就过来了,可以减少网络的请求,但是如果图片过大,这个字符串就会变得特变大,让加载的文件变得特别大

所以如果图片很小,没必要让其重新请求图片,直接将其写进页面中,让浏览器去解析,当图片过大时,就不让他编码,看下面的实现过程

{
    test:/\.jpg|gif|png$/,
    use:[{
        loader:'url-loader',
        options:{
            limit:10000//以bit为单位,当小于10000bit时,编码,大于10000bit时,不编码
        }
    }]
}

当使用url-loader去处理一些资源的时候,默认会把所有的资源都是用base64的形式进行编码,但是我们可以给它一个limit属性去约束他,当资源小于某个值的时候,才去编码,当不小于这个值时,它其实是会把这个资源交个file-loader去处理

3. Vuex install 做了哪些事情(源码)

答案:vue 使用插件的方式只需要 Vue.use(plugin),对于 Vuex 就是Vue.use(Vuex) .

4. 强缓存协商缓存

答案:优先级: 强缓存 > 协商缓存 强缓存: Cache-Control > Expires(http1.0产物, 受本地时间影响)

协商缓存: ETag文件指纹对比(http1.1出现) > Last-Modified最后修改时间对比(Last-Modified 打开文件的时候会变,以秒计算的)

5. 电商网站中地址组件的数据2M太大,如何处理,

答案:我的个人建议: 分批请求,按需请求。数据缓存到localStorage

6. 详细描述CSRF跨域请求伪造

答案:www.jianshu.com/p/7f33f9c79…

image.png

在业界目前防御 CSRF 攻击主要有三种策略:

  • 验证 HTTP Referer 字段;
  • 在请求地址中添加 token 并验证;
  • 在 HTTP 头中自定义属性并验证
思考点
  • 攻击者网站B如何跨域发送请求到被攻击者A网站: 通过img标签发请求
  • 攻击者也可以通过抓包https包的形式,抓取A请求头中的token信息,因为tooken在过期时间内是可以使用的。

7. Vue 异步更新队列

答案: Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.thenMutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用

methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
}

因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2017 async/await 语法完成相同的事情:

methods: {
    updateMessage: async function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      await this.$nextTick()
      console.log(this.$el.textContent) // => '已更新'
    }
 }

8. vue中Toast插件的封装。比如toast组件是如何挂载到Vue原型上的?

答案:

//导入组件
import Toast from './Toast'

const obj = {}

// install方法
obj.install = function(Vue) {
    // 1.创建组件构造器
    const toastConstructor = Vue.extend(Toast)

    // 2.new一个组件对象
    const toast = new toastConstructor()

    // 3.将组件对象手动挂载到某个元素上
    toast.$mount(document.createElement('div'))

    // 4.将挂载了组件对象的元素添加到body
    document.body.appendChild(toast.$el)

    // 5.将组件对象挂载到Vue的原型上
    Vue.prototype.$toast = toast
}

// 暴露当前插件
export default obj

二、com 2

tx

1. 小程序打包后的体积大小。

小程序对大小有2m的限制

因为小程序对大小有2m的限制,所以有些时候小程序过大,没法上传,解决方法如下;

1,看引入的静态资源,如图片什么的,可以压缩,换成png,或者让后端放进后台资源,https引入,

2,引入的js包css包网上如果有资源可以换成网络资源,

3,引入的插件包可以按需引入,不用全部引入,

4,一下复用多的功能可以封装,减少代码冗余

以上处理后如果还是较大可以考虑小程序分包,通过分包减少包的大小,

api地址如下developers.weixin.qq.com/miniprogram…

在通过在 app.json subpackages 字段声明项目分包结构就可以把项目分包,减少包的大小,分包可以简单分包和独立分包

主要区别是独立分包的小程序可以不下主包的情况下运行,值得注意的是分包以后js不能跨包复用,独立分包可以提高页面加载这速度,可以有多个独立分包

2. vue组件转wechat组件

3. 输入URL后发生了什么?

4. 渲染DOM时为何要转换成一个DOM树呢,为何不直接处理字符串呢。

image.png

从网络传给渲染引擎的 HTML 文件字节流是无法直接被渲染引擎理解的,所以要将其转化 为渲染引擎能够理解的内部结构,这个结构就是 DOM。DOM 提供了对 HTML 文档结构 化的表述。

DOM 是表述 HTML 的内部数据结构,它会将 Web 页面和 JavaScript 脚本连接 起来,并过滤一些不安全的内容。

HTML解析器不等待,网络进程加载了多少数据,解析器就解析了多少数据。

HTML 解析器并不是等整个文档加载完成之后再解析的,而是网络进程加载了多少数据,HTML 解析器便解析多少数据。

image.png

5. 防抖和节流的实现与应用场景

6. http3.0 udp协议相关

三、com 3

tujia

1. state中a为0,React中componentDidMount中多次setState后打印state.a的值是?此时render函数中a的值是什么?以及在setTimeout中修改a的值并打印,是多少?

答案:首次打印的是初始值0。首次render的是2。 定时器的代码加上后打印的是修改后的值,此时render中展示是修改后的值。

2. 数组shift,与数组的方法中哪些是纯函数, 哪些又会改变原始数据?

2.1) 不会改变原来数组的有

  • concat()---连接两个或更多的数组,并返回结果。
  • every()---检测数组元素的每个元素是否都符合条件。
  • some()---检测数组元素中是否有元素符合指定条件。
  • filter()---检测数组元素,并返回符合条件所有元素的数组。
  • indexOf()---搜索数组中的元素,并返回它所在的位置。
  • join()---把数组的所有元素放入一个字符串。
  • toString()---把数组转换为字符串,并返回结果。
  • lastIndexOf()---返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
  • map()---通过指定函数处理数组的每个元素,并返回处理后的数组。
  • slice()---选取数组的的一部分,并返回一个新数组。
  • valueOf()---返回数组对象的原始值。

2.2)会改变原来数组的有

  • pop()---删除数组的最后一个元素并返回删除的元素。
  • push()---向数组的末尾添加一个或更多元素,并返回新的长度。
  • shift()---删除并返回数组的第一个元素。
  • unshift()---向数组的开头添加一个或更多元素,并返回新的长度。
  • reverse()---反转数组的元素顺序。
  • sort()---对数组的元素进行排序。
  • splice()---用于插入、删除或替换数组的元素。

2.1) 数组可以表现的像栈(LIFO)和队列(FIFO)一样操作

JavaScript的数组是一个拥有堆栈和队列自身优点的global对象。也就是说JavaScript数组可以表现的像栈(LIFO)和队列(FIFO)一样操作。这也是JavaScript数组强大的可操作性的体现。

栈和队列都是动态的集合,

  • 在栈中,可以去掉的元素是最近插入的那一个。栈实现了后进先出。

    push()pop()结合在一起,我们就可以实现类似栈的行为

  • 在队列中,可以去掉的元素总是在集合中存在的时间最长的那一个。队列实现了先进先出的策略。

    shift()push()方法结合在一起,可以像使用队列一样使用数组。即在数组的后端添加项,从数组的前端移除项

3. axios封装需要考虑哪些因素?

答案:默认的头配置,与timeout配置,与用户认证token配置。与请求,响应配置与错误处理。

4. 如果我们在拦截器当中限制业务当中只能发起5个请求,怎么处理。来了第6个请求时,需要判断之前的5个请求是否有哪些完成了,并且把新来的请求推进去。

答案:我的个人建议是通过一个变量去统计请求发起的个数,在请求发起中添加num++。在请求响应回调中添加num--。然后,判断num为几与5的差值,然后去补充后续的请求进来。后续的请求可以先统一不发出去,放到一个数组中去维护,等待发出。

不过目前的tcp 多路复用。可以同时建立多个tcp,每个tcp可以发起多个http请求。不会存在这种需求。

5. 浏览器并发提升,http2多路复用。可以建立多个TCP连接,每个TCP可以发起多个HTTP请求。

答案:juejin.cn/post/700180…

6. tujia这边主要是用RN来嵌入到多个APP壳子中。

7. REACT 与VUE diff算法区别

7.1)相同点:

Vue和react的diff算法,都是不进行跨层级比较,只做同级比较。

7.2)不同点:

1.Vue进行diff时,调用patch打补丁函数,一边比较一边给真实的DOM打补丁

2.Vue对比节点,当节点元素类型相同,但是className不同时,认为是不同类型的元素,删除重新创建,而react则认为是同类型节点,进行修改操作

① Vue的列表比对,采用从两端到中间的方式,旧集合和新集合两端各存在两个指针,两两进行比较,如果匹配上了就按照新集合去调整旧集合,每次对比结束后,指针向队列中间移动;

②而react则是从左往右依次对比,利用元素的index和标识lastIndex进行比较,如果满足index < lastIndex就移动元素,删除和添加则各自按照规则调整;

③当一个集合把最后一个节点移动到最前面,react会把前面的节点依次向后移动,而Vue只会把最后一个节点放在最前面,这样的操作来看,Vue的diff性能是高于react的

8. let var const 内部实现的区别

  1. var声明变量存在变量提升,let和const不存在变量提升
  2. let和const声明形成块作用域,而var不存在此作用域
  3. 同一作用域下let和const不能声明同名变量,而var可以
  4. let、const存在暂存死区

原理: var会预分配内存空间,其他不会。 const注意

const 声明了一个复合类型的常量,其存储的是一个引用地址,不允许改变的是这个地址,而对象本身是可变的。 const 如果声明的是复合类型数据,可以修改其属性

9. 手写题

Promise.reject(1).then().catch((e) => {
    return e
}).then((data) => {
    console.log(1, data)
}).catch((e) => {
    console.log(2, e)
})

image.png

image.png

// 多个then

image.png

// 多个then

前面的then如果没有取结果做处理的话,结果会一直到最后一个then当中

image.png

1)promise多then情况
var p2 = new Promise((resolve,reject) => {
    resolve(666)
});
p2.then().then().then((data)=>{console.log(data)}).catch((err)=>{console.log(err)});
// 666
// Promise {<fulfilled>: undefined}

image.png

image.png

10. 手写题

image.png

注意:此版本只是思路不能使用:


const obj = {a: {b: {c:1} }, e:2};

function find(obj, str){
    let strArr  = str.split('.'); // [a,b,c]
    for(let i in obj){ // a , e
        if(obj[i] === strArr[0]){//a
            strArr.shift();
            if(typeof obj[i] === 'object'){
                find(obj[i], str)
            }else{
                return obj[i]
            }
        }
    }
};

参考

总结