记录用

411 阅读25分钟

2022/1/12---2022/未知

2022/2/28

99. webpack工作流程:

webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  1. 初始化参数:从配置文件和shell语句中读取与合并参数,得出最终的参数。
  2. 开始编译: 用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译。
  3. 确定入口:根据配置中的entry找出所有的入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的loader对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
  5. 完成模块编译:再经过第4步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,再把每个chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
  7. 输出完成,在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。 在以上的过程中,webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用webpack提供的api改变webpack的运行结果。

100.react setState过程

link.juejin.cn/?target=htt…

101.Vue数据绑定原理

Vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤: 1. 需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter 这样的话,给这个对象某个值赋值,就会触发setter,那么就能监听到了数据变化 2. compile解析模版指令,将模版中的变量替换成数据,然后初始化渲染页面时图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新时图。 3. watcher订阅者是observe和compile之间通信的桥梁,主要做的事情时: 1. 在自身实例化时往属性订阅器(dep)里添加自己 2. 自身必须有一个update()方法 3. 待属性变动dep.notice()通知时,能调用自身的update()方法。并触发compile中绑定的回调,则功成身退。 4. MVVM作为数据绑定的入口,整合observe、compile、和watcher三者,通过observe来监听自己的model数据变化,通过compile来解析编译模版指令,最终利用watcher搭起observe和compile之间的通信桥梁,达到数据变化->视图更新;视图交互变化(input)->数据model变更的双向绑定效果。

image.png

2022/2/25

96.说一下冒泡排序和快速排序

冒泡排序: 重复便利要排序的元素列,依次比较两个相邻的元素,前一个元素若比后一个元素大则互换位置。以生序排序为例,最大的元素会在第一次遍历后“冒泡”到数组的末端。假如数组长度为n,在n-1次遍历后可完成排序 image.png 实现:

let arr = [1, 5, 2, 9, 7, 4, 2, 3, 6, 8]
function bubbleSort(arr) {
  let time = arr.length - 1
  while (time) {
    let i = 0
    while (i<time) {
      if (arr[i] > arr[i+1]) [arr[i], arr[i+1]] = [arr[i+1], arr[i]]
      i ++
    }    
    time --
  }
}

bubbleSort(arr)

快速排序: 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按照此方法都对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

image.png

实现:

let arr = [1, 5, 2, 9, 7, 4, 2, 3, 6, 8]
function quickSort (arr) {
    if(arr.length <= 1) return arr
    let pivotVal = arr[0]
    smallers = []
    biggers = []
    idx = 1
    while(idx < arr.length) {
        if(pivotVal > arr[idx]) {
            smallers.push(arr[idx])
        }else {
            biggers.push(arr[idx])
        }
        idx++
    }
    return quickSort(smallers).concat(pivotVal,quickSort(biggers))
}
quickSort(arr)

97.localstorage、sessionstorage、cookie的区别?

  • cookie: 最开始是服务端用于记录用户状态的一种方式,由服务器设置,在客户端存储,然后每次发起同源请求时,发送给服务器端。cookie最多能存4k数据,他的生存时间由expires属性制定,并且cookie只能被同源的页面访问共享。
  • sessstorage: html5提供的一种浏览器本地存储的方法,它借鉴了服务端session的概念,代表的是一次会话中所保存的数据。它一般能够存储5M或者更大的数据,它在当前窗口关闭后就失效了,并且sessionStorage只能被同一个窗口的同源页面所访问共享。
  • localStorage: html5提供的一种浏览器本地存储的方法,它一般也能存储5M或者更大的数据。它和sessionStorage不同的是,除非手动删除,否则不会失效,并且localStorage也只能被同源页面所访问共享。

98.说出下面打印结果:

console.log( true + 1 ); //2       
console.log( 'name'+true ); //nametrue    
console.log( undefined + 1 );  // NaN
console.log( typeof undefined );  //undefined 
console.log( typeof (undefined+1) ); // number  
console.log( typeof NaN );  //number    
console.log( typeof null ); //object

2022/2/24

92.你在项目中有自己封装过组件么,请列举下你使用过的属性,并说明作用。

开放题,结合自身项目回答

93.简述下深拷贝,浅拷贝的方法

link.juejin.cn/?target=htt…

94.简述EventLoop执行顺序

  • 事件循环从宏任务队列开始,一开始宏任务队列中只有一个script(整体代码)任务,遇到任务源时,分发到相应的任务队列中。
  • 异步任务可分为task和micrtask两类(requestAnimationFrame既不属于macrotask,也不属于micrtask),不同的API注册的异步任务会依次进入自身对应的队列中。
  • 然后等待event loop将他们依次压入执行栈中执行
  • 执行栈执行完同步任务后,检查执行栈是否为空,如果为空,检查微任务队列是否为空,如果微任务队列不为空,则一次性执行完所有的微任务
  • 如果微任务为空,则执行下一个宏任务
  • 每次单个宏任务执行完之后,都会检查微任务队列是否为空,如果不为空,则会按照先进先出的方式执行完所有的微任务,然后执行下一个宏任务,以此循环。
  • 每次宏任务产生的微任务队列都是新创建的,宏任务队列只有一个。

95.axios和Ajax本质的区别是什么?

axios是通过promise实现对ajax技术的一种封装。就像ajax是通过jQuery来封装的一样。也就是说,jQuery将请求技术进行了封装变成了ajax,而通过promise又把ajax进行封装就成了axios

2022/2/23

89.谈谈对 window.requestAnimationFrame的理解

requestAnimationFrame方法会告诉浏览器希望执行动画并请求浏览器在下一次重绘之前调用回调函数来更新动画 优点: 1. requestAnimationFrame自带函数节流功能,采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间的过短,造成过度绘制,增加页面开销,也不会因为间隔时间过长,造成动画卡顿,不流畅,影响页面美观。

浏览器的重绘频率一般会和显示器的刷新率保持同步。大多数采用W3C规范,浏览器的渲染页面的标准频率也为60FPS(frames\per second) 即每秒重绘60次,requestAnimationFrame的基本思想是 让页面重绘的频率和刷新频率保持同步,即每1000ms/60 = 16.7ms执行一次。

通过requestAnimationFrame 调用回调函数引起的页面重绘或回流的时间间隔和显示器的刷新时间间隔相同。所以requestAnimationFrame不需要像setTimeout那样传递时间间隔,而是浏览器通过系统获取并使用显示器刷新频率。例如在某些高频事件(resize,scroll)等中,使用requestAnimationFrame可以放置在一个刷新间隔内发生多次函数执行,这样保证了流程度,也节省了开销。

2. 该函数的延时效果是精确的,没有setTimeout或setInterval不准的情况(JS是单线程的,setTimeout任务被放进异步队列中,只有当主线程上的任务执行完以后,才回去检查该队列的任务是否需要开始执行,造成时间延时)。 setTimeout的执行只是在内存中对图像属性进行改变,这个改变必须要等到下次浏览器重绘时才会被更新到屏幕上。如果和屏幕刷新步调不一致,就可能导致中间某些帧的操作被跨越过去,直接更新下下一帧的图像。即 掉帧

使用 requestAnimationFrame 执行动画,最大优势是能保证回调函数在屏幕每一次刷新间隔中只被执行一次,这样就不会引起丢帧,动画也就不会卡顿

3、节省资源,节省开销

在之前介绍requestAnimationFrame执行过程,我们知道只有当页面激活的状态下,页面刷新任务才会开始,才执行 requestAnimationFrame,当页面隐藏或最小化时,会被暂停,页面显示,会继续执行。节省了 CPU 开销。

注意:当页面被隐藏或最小化时,定时器setTimeout仍在后台执行动画任务,此时刷新动画是完全没有意义的(实际上 FireFox/Chrome 浏览器对定时器做了优化:页面闲置时,如果时间间隔小于 1000ms,则停止定时器,与requestAnimationFrame行为类似。如果时间间隔>=1000ms,定时器依然在后台执行)

4、能够在动画流刷新之后执行,即上一个动画流会完整执行

90.谈谈对以下几个页面生命周期事件的理解: DOMContentLoaded,load,beforeunload,unload

  • DOMContentLoaded 浏览器已经完全加载了html,Dom树已经构建完毕,但是像是<img>和样式表等外部资源可能并没有下载完毕。
  • load 浏览器已经加载了所有的资源(图像,样式表等)。
  • beforeunload/unload当用户离开页面的时候触发。

91. vue/react常见的性能优化方案

link.juejin.cn/?target=htt…

2022/2/22

86.用webpack怎么做性能优化

看第48题

87.从地址栏输入到url到显示页面发生了什么?

看第7题

说一说你们项目vue后台管理系统怎么做的路由权限和按钮权限?

两种场景:

动态路由:link.juejin.cn/?target=htt… 指令形式:link.juejin.cn/?target=htt…

2022/2/21

83.思考题:计算每个item的宽度?

image.png flex-basis表示在flex items 被放入flex容器之前的大小,也就是items的理想或者假设大小,但是并不是其真实大小,其真实大小取决于flex容器的宽度,flex items的min-width,max-width等其他样式 答案:50 250 50 300 分析: item1:当flex-basis和width属性同时存在时,width属性不生效,flex item的宽度为flex-basis设置的宽度 item2:flex属性有flex-grow、flex-shrink、flex-basis组成,默认值为0 1 auto,它会覆盖设置的flex-basis值 item3:max-width决定了flex items的最大宽度 item4:min-width决定了flex items的最小宽度,flex-basis会覆盖flex里的flex-basis值,此处相当于flex:0 1 50px,浏览器预留了50px位置,剩余空间均等分配500/2 = 250px,最后items4为300px

84.说一下slice splice split的区别和作用

split

  • 将字符串分割成数组
  • 语法:str.split([separator, limit]),参数:分隔符、返回数组的最长长度
  • 返回值为一个数组
let str = 'abcd d'
console.log(str.split()) // ['abcd d']
console.log(str.split(' ')) // ['abcd', 'd]
console.log(str.split('')) // ['a', 'b', 'c', 'd', ' ', 'd']

splice

  • 添加或删除元素,返回删除的元素
  • 会改变原数组
  • 语法:array.splice(start[,num, item1...itemN)
// 删除,第二个参数可选,没有则删除第一个参数位置以后所有的元素,为0则不删除
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.splice(2,1)
console.log(arr)// ['a', 'b', 'd']
console.log(arr1)// ['c']
// 新增
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.splice(2, 0, 'h')
console.log(arr)// ['a', 'b','h', 'c', 'd']
console.log(arr1)// []

splice

  • 返回被选中的元素
  • 不改变原数组
  • 语法:array.slice([start,end])
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.slice()
console.log(arr)// ['a', 'b', 'c', 'd']
console.log(arr1)// ['a', 'b', 'c', 'd']
// 1位置开始,2位置结束,但不包括2
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.slice(1,2)
console.log(arr)// ['a', 'b', 'c', 'd']
console.log(arr1)// ['a', 'c', 'd']

let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.slice(1)
console.log(arr)// ['a', 'b', 'c', 'd']
console.log(arr1)// ['b', 'c', 'd']

85.vue如何检测数组变化

vue内部没有通过Object.defineProperty去对数组进行观测,因为消耗严重,而是通过重写数组的7个方法,并对数组中不是基本数据类型的进行检测,重写的方法是:push shift pop unshift reverse sort splice

基本思路:

  1. 在对数据进行检测的时候,判断对象类型,根据添加的__ob__属性判断数据是否被观测
export function observe (data) {
  //如果是对象类型才观测
  if(!isObject(data)) {
    return
  }
  //判断data有没有被观测
  if(data.__ob__) {
    return
  }
  return new Observer(data)
}

Observer类中的判断

class Observer{
  constructor(data) {//对对象中的所有属性进行劫持
    Object.defineProperty(data, '__ob__', {
      value: this,
      enumerable: false //不可枚举
    })
    // data.__ob__ = this //所有被劫持过的属性都有__ob__
    if(Array.isArray(data)) {
      //数组劫持,对数组原来方法改写,切片编程
      data.__proto__  = arrayMethods
      //如果数组中的数据是对象类型,需要监控对象的变化
      this.observeArray(data)
    }else {
      this.walk(data) //对象劫持
    }
  }

  observeArray(data) { //对数组中的数组和数组中的对象再次劫持
    data.forEach(item => {
      observe(item)
    })
  }

  walk(data) {//传入的对象
    Object.keys(data).forEach(key => {
      defineReactive(data, key, data[key])
    })
  }
}

  1. 获取数组原型
  2. 新建一个对象继承数组方法
  3. 重写数组的七个写法
  4. 将新增的数组继续劫持进行观测
  5. 返回方法执行结果
let oldArrayPrototype = Array.prototype
// 继承数组方法(保留原有数组功能)
export let arrayMethods = Object.create(oldArrayPrototype)
// arrayMethods.__proto__ = Array.prototype 

//只有这7个可以改变原数组
let methods = [
  'push',
  'shift',
  'unshift',
  'pop',
  'reverse',
  'sort',
  'splice'
]

methods.forEach(method => {
  // 用户调用的如果是以上七个方法,会用重写的,负责调用原来的数组方法
  arrayMethods[method] = function (...args) {
    // 此处this是数组
    oldArrayPrototypep[method].call(this, ...args)
    let inserted
    let ob = this.__ob__//根据当前数组获取observe实例
    switch(method){
      case 'push':
      case 'unshift':
        inserted = args // 就是新增的内容
        break
      case 'splice':
        inserted = args.slice(2)
        break
      default: 
        break
    }
    // 如果有新增的内容要继续劫持 需要观测数组每一项,而不是数组
    if(inserted) ob.observeArray(inserted)

  }
})

2022/2/18

79.深拷贝和浅拷贝有几种实现方式,分别举例

讲的全的一篇文章:link.juejin.cn/?target=htt…

80.如何避免回流与重绘?

css中

  • 尽可能在DOM树的最末端改变class
  • 避免设置多层内联样式
  • 动画效果用到position属性为absolute或fixed的元素上
  • 避免使用table布局
  • 使用css3硬件加速,可以让transform、opacity、filters等动画效果不会引起重排重绘

js中

  • 使用resize(浏览器窗口大小)、scroll时进行节流防抖处理
  • 批量修改元素时,可以先让元素脱离文档流,等修改完毕后再放入文档流
  • 在获取offsetWidth这类属性值时,可以将使用变量将查询的结果储存起来,避免多次查询。

81.如何减少webpack打包体积

link.juejin.cn/?target=htt…

82.get方法url长度限制的原因

在HTTP1.1协议中并没有提出针对URL的长度进行限制,RFC协议里面是这样描述的,HTTP协议并不对URI的长度做任何的限制,服务器端必须能够处理任何它们所提供服务多能接受的URI,并且能够处理无限长度的URI,如果服务器不能处理过长的URI,那么应该返回414状态吗,虽然HTTP协议规定了,但是web服务器和浏览器对uri都有自己的长度限制。此外,对url的长度进行限制可以防止有人恶意使用大数据频繁发请求攻击服务器。

2022/2/17

75.如何判断一个变量是否为对象,如何判断一个变量是否为数组,有哪些方法?

如何判断一个变量是否为对象

  • instanceof
  • constructor
  • Object.prototype.toString.call() 如何判断一个变量是否为数组
  • Array.isArray
  • Object.prototype.toString.call()
  • instanceof
  • 原型链:obj.proto === Array.protype
  • Array.prototype.isPrototypeOf()

76.箭头函数和普通函数有什么区别

  • 箭头函数写法更简洁
  • 箭头函数没有自己的this,并且继承来的this不会改变
  • 箭头函数没有prototype(没有显示原型,但是有隐式原型__proto__)
  • 箭头函数不能用作generator函数,不能使用yield关键字
  • 箭头函数没有自己的arguments
  • band,call,apply 不能改变箭头函数this指向

77.vue中computed、methods和watch的区别

  • methods就是正常用proxy代理到vm下,触发的时候就直接调用,
  • watch和computed都有自己专属的watcher对象,每个k都对应一个watcher,
  • watch是直接触发绑定的,computed则是懒加载,watch的响应式绑定是在observe()时候做的,
  • computed的响应式则要单独做,也是用的defineProperty,computed并不会随着回调中数据更新直接重新计算,数据更新只会改变computed下的watcher中的dirty字段,而真正的重新极端是只有用户访问computed下属性的时候会去判断computed的watcher的dirty字段,根据是否是脏数据来决定要不要重新计算。

78.JS作用域+this指向+原型的考题的代码输出

function Foo(){
	getName = function(){console.log(1)} //注意是全局的window.
	return this;
}

Foo.getName = function(){console.log(2)}
Foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){
	console.log(5)
}

Foo.getName();          //2
getName(); 		   //4
Foo().getName();          //1
getName();		   //1
new Foo().getName();  //3

扩展: 写一个方法,让promiseList中的promise按顺序执行,不允许使用async,await,generator

const promise1 = () => Promise.resolve(1)
const promise2 = () => {
   new Promise(resolve => {
      setTimeout(() => {
        resolve(2)
      }, 3000)
   })
}
const promise3 = () => {
   new Promise(resolve => {
      setTimeout(() => {
        resolve(3)
      }, 2000)
   })
}
const promiseList = [promise1, promise2, promise3]

2022/2/16

72.描述一下vue自定义指令(你是怎么用自定义指令的)?

自定义指令:我们可以自己写一个自定义指令去操作DOM元素,已达到代码复用的目的。注意,在vue中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通DOM元素进行底层操作,这时候就会用到自定义指令。 全局注册指令:vue.directive('name',{}) 局部注册指令:directives:{focus:{}} 自定义指令对象提供了钩子函数供我们使用,这些钩子函数都为可选 使用:节流防抖:link.juejin.cn/?target=htt…

73.介绍一下VUE单页应用和多页应用的区别?

标题多页应用模式MPA单页应用模式SPA
应用构成由多个完成页面构成一个外壳页面和多个页面片段构成
跳转方式页面之间的跳转是从一个页面跳到另一个页面页面片段之间的跳转是吧一个页面片段删除或隐藏,加载另一个页面片段并显示出来。这是片段之间的模拟跳转,并没有开壳页面
跳转后公共资源是否重新加载
URL模式link.juejin.cn/?target=htt…link.juejin.cn/?target=htt…
用户体验页面间切换加载慢,不流畅,用户体验差,特别是在移动设备上页面片段间切换快,用户体验好,包括移动设备
能否实现转场动画无法实现容易实现(手机app动效)
页面见传递数据依赖url、cookie或者localstorage,实现麻烦因为在一个页面内,页面间传递数据很容易实现
搜索引擎优化(SEO)可以直接做需要单独方案做,有点麻烦
特别适用的范围需要对搜索引擎友好的网站对体验要求高的应用,特别是移动应用
开发难度低一些,框架选择容易高一些,需要专门的框架来降低这种模式的开发难度

74.vue是如何收集依赖的?

link.juejin.cn/?target=htt…

2022/2/15

69.输出运算的结果:let a = 10 + null + true + [] + undefined + '你好' + null + 10 + false

与字符串进行+运算时会执行拼接,其他情况转为数值类型相加 答案:11undefined你好null10false

70.事件循环:输出什么?页面会有什么变化?

document.body.style="background:red"
console.log(1)
Promise.resolve().then(()=>{
  console.log(2)
  document.body.style="background:yellow"
})
console.log(3)

微任务之后会进行一次渲染操作 输出:132页面变为黄色

71.请说一下ts函数重载是什么?

link.juejin.cn/?target=htt…

2022/2/14

66.判断字符串以字母开头,后面可以是数字,下划线,字母,长度为6-30

var reg = /^[a-zA-Z]\w{5,29}$/

67.VUE的父组件和子组件生命周期钩子函数执行顺序

加载渲染过程:beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted 子组件更新过程:BeforeUpdate -> 子 BeforeUpdate -> 子Updated -> 父Updated 父组件更新过程:BeforeUpdate -> 子 Updated 销毁过程:beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

68.Proxy与Object.defineProperty优劣对比

Proxy proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找,赋值,枚举,函数调用等)。

  • 劫持方式:代理整个对象,只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性。
  • 本质:proxy本质上属于原编程非破坏性数据劫持,在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念。

Object.defineProperty Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

  • 劫持方式:只能劫持对象的属性,不能直接代理对象
  • 流程:get中进行依赖收集,set数据时通知订阅者更新。
  • 存在的问题:虽然Object.definProperty通过为属性设置getter/setter能够完成数据的响应式,但是它并不算是实现数据的响应式的完美方案,某些情况下需要对其进行修补或者hack,这也是他的缺陷,主要表现在两个方面:
    • 无法检测到对象属性的新增或删除,不能监听数组的变化

优劣势 proxy的优势如下: - proxy代理整个对象,Object.defineProperty只代理对象上的某个属性 - 数组新增删除修改时,proxy可以监听到 - proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.definProperty不具备的 - 如果对象内部要全部递归代理,则proxy可以只在调用时递归,而Object.definProperty需要在一开始就全部递归,proxy性能优于Object.definProperty - 对象上定义的新属性,proxy可以监听到

Object.definProperty的优势如下: - 兼容性好,支持ie9以上,而proxy存在浏览器兼容性问题,而且无法用polyfill磨平,因此VUE的作者才声明需要等到下个大版本(3.0)才能用proxy重写。

2022/2/11

63.common.js和es6中模块引入的区别?

CommonJS是一种模块规范,最初被应用于Nodejs,成为Nodejs的模块规范。运行在浏览器端的JavaScript由于也缺少类似的规范,在ES6出来之前,前端也实现了一套相同的模块规范(例如:AMD) ,用来对前端模块进行管理。自ES6起,引入了一套新的ES6 Module规范,在语言标准的层面上实现了模块功能,而且实现的相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对ES6 Module兼容还不太好,我们平时在Weakpack中使用的export和import,会经过Babel转换为commonJs规范。在使用上的差别主要有:

  • CommonJs模块输出的是一个值的拷贝,ES6模块输出的是值的引用
  • CommonJs模块是运行时加载,eS6模块是编译时输出接口
  • CommonJs是单个值导出,ES6 Module可以导出多个
  • CommonJs是动态语法可以写在判断里,ES6 Module静态语法只能写在顶层

64.用js判断给定的字符串是否是同构的?

link.juejin.cn/?target=htt…

65.为什么函数的arguments参数是类数组而不是数组? 如何遍历类数组?

link.juejin.cn/?target=htt… 如何遍历类数组请看57题

2022/2/10

60.localhost:3000与localhost:5000的cookie信息是否共享

根据同源策略,cookie是区分端口的,但是浏览器实现来说,“cookie区分域,而不区分端口,也就是说,同一个ip下的多个端口下的cookie是共享的!”

61.Array(100).map(x => 1)结果是多少

Array(100)将会创建一个稀疏数组(sparse array),即不存在真实元素,节省内存空间。在控制台上显示为[empty],正因为没有元素,所以他也不会有map操作,所以Array(100).map(x => 1)仍然返回为[empty]

62.输出题

const obj = { a: 3, b: 4, c: null, d: undefined, get e() {},}
console.log(JSON.stringify(obj))

输出:{"a":3,"b":4,"c":null}

2022/2/09

57.如何转化类数组成数组?

第一种:Array.prototype.slice.call(likeArray)

function sum(a,b) {
    let args = Array.prototype.slice.call(arguments);
    console.log(args.reduce((sum,cur) => sum + cur )); //args可以调用数组原生的方法
}
sum(1,2) //3

第二种:Array.from(likeArray)

function sum(a,b) {
    let args = Array.from(arguments)
    console.log(args.reduce((sum,cur) => sum + cur)) 
}
sum(1,2) //3

第三种:ES6展开运算符

function sum(a,b) {
    let args = [...arguments]
    console.log(args.reduce((sum,cur) => sum + cur)) 
}
sum(1,2) //3

第四种:Array.prototype.splice.call(likeArray,0)

function sum(a,b) {
    let args = Array.prototype.splice.call(arguments,0)
    console.log(args.reduce((sum,cur) => sum + cur)) 
}
sum(1,2) //3

第五种:Array.prototype.concat.apply([],likeArray)

function sum(a,b) {
    let args = Array.prototype.concat.apply([],arguments)
    console.log(args.reduce((sum,cur) => sum + cur)) 
}
sum(1,2) //3

58.forEach中return有效果吗?如何中断forEach循环?

有效果,但是只是不走本次循环,后边的还是会走使用break/continue无效,会报语法错误

let nums = [1,2,3]
nums.forEach((item,index) => {
    return //无效
})

中断方法: 使用try监视代码块,在需要中断的地方抛出异常。 官方推荐方法(替换方法): - 用every和some替代forEach函数。 - every在碰到return false的时候,中止循环。 - some在碰到return true的时候,中止循环。

59.['1','2','3'].map(parseInt) 答案是多少?

数组的map函数,接受三个参数,当前值,当前索引,当前数组。parseInt接受两个参数,需要转换的字符串,基数(基数取值范围2~36)

['1','2','3'].map((item,index) => {
    return parsInt(item,index)
})
// parseInt('1', 0) 1 
// parseInt('2', 1) NaN 
// parseInt('3', 2) NaN

扩展:为什么forEach中return 不能退出循环

首先大概实现下forEach

Array.prototype.myEach = function(callback) {
    for(var i = 0; i < this.length; i++) {
        callback(this[i],i,this)
    }
}
原因:在foreach中使用return相当于函数中使用return,它只影响了回调函数,并不会影响到函数上级的for循环,同理,在函数中使用`beack/continue`会报语法错误。这和在foreach中使用`break/continue`

是同样的效果

常见的类数组有哪些

  • arguments
  • dom元素用attributes属性获取NameNodeMap对象
  • 通过dom元素的children属性获取元素的子元素集合即HTMLCollection集合对象
  • 通过dom元素的childNodes属性返回包含被选节点的子节点的NodeList ps:HTMLCollection集合中只有dom元素节点,NodeList中包含dom元素节点和文本节点(text)和注释节点

2022/2/08

54.如何防御XSS攻击

  • 可以从浏览器的执行来进行预防,一种是使用纯前端的方式,不用服务器端拼接后返回(不使用服务端渲染)。另一种是对需要插入到HTML中的代码做好充分的转义。对于dom型的攻击,主要是前端脚本的不可靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能出现的恶意代码情况进行判断。
  • 使用CSP,CSP的本质是建立一个白名单,高速浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。

1.CSP指的是内容安全策略,他的本质是建立一个白名单,高速浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截由浏览器自己来实现。 2.通常有两种方式来开启CSP,一种是设置http首部中的Content-Security-Policy,一种是设置meta标签的方式

  • 对一些敏感信息进行保护,比如cookie使用http-only,使得脚本无法获取。也可以使用验证码,避免脚本伪装成用户执行一些操作。

55. 简述TCP的三次握手和四次挥手

三次握手: - 第一次握手:客户端向服务器端发送连接请求报文段。 - 第二次握手:服务端收到请求报文段后,如果同意连接则发送一个应答 - 第三次握手:当客户端收到连接同意的应答后,还要向服务器发送一个确认报文连接成功。

四次挥手: - 第一次挥手:若客户端认为数据发送完成,则向服务端发送连接释放请求。 - 第二次挥手:服务端收到连接释放请求后,告诉应用层要释放TCP连接 - 第三次挥手:如果服务器端还没有发送完数据则会继续发送,发送完成后向客户端发送释放通知 - 第四次挥手:客户端收到释放通知后,向服务器发送确认应答,如果在规定的时间内没有收到重发通知则结束。

56.闭包的应用场景有哪些?

  1. 采用函数引用方式的setTimeout调用。
  2. 小范围替代全局变量。
  3. 有权访问私有变量和私有函数的公有方法。
  4. 节流防抖。

2022/2/07

51.‘==’比较时候的相互转换规则

1 == true //true
1 === true //false
  • 先判断两者类型是否相同,相同进行判断,不同进行强制类型转换后判断。
  • 先判断是否为null或者undefined对比,是的话直接返回true
  • 判断两者类型如果是string和number,是则将字符串转换为number
'1' == 1 //true
'2' == 1 //false
  • 如果其中有一方为boolean,将boolean转换为number
//true转换为1,false是0
1 == true //true
0 == false //true
  • 其中一方为object,另一方是string,number,symbol,将object转换为原始类型再进行对比 为了将值转换为相应的基本类型值,抽象操作ToPrimitive会首先(通过内部操作DefaultValue)检查该值是有valueOf()方法,如果有并且返回基本类型值,就是用该值强制类型转换。如果没有就使用toString()的返回值(如果存在)来进行强制类型转换 如果valueOf()toString()均不返回基本类型值,会产生TypeError错误

52.事件传播机制/事件委托/事件代理

事件传播机制分为三个阶段:

  • 第一阶段:从window对象传导到目标节点,称为‘捕获阶段’
  • 第二阶段:在目标节点上触发,称为‘目标阶段’
  • 第三阶段:从目标节点传导回window对象,称为‘冒泡阶段’

事件委托(事件代理)本质上是利用了浏览器冒泡事件的机制。因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件委托(事件代理)

好处: 使用事件委托可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理还可以实现事件的动态绑定,比如说新增了一个子节点,并不需要单独地为它添加一个监听事件,他绑定的事件会交给父元素的监听函数来处理

缺点: 事件委托会影响页面性能,主要影响因素有:

  1. 元素中,绑定事件委托的次数。
  2. 点击的最底层元素,到绑定事件元素之间的DOM层数。

局限性: focus,blur之类的事件没有事件冒泡机制,所以无法实现事件委托。mousemove、mouseout这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合事件委托的。

53、mouseover/mouseenter的区别

区别请看27题:juejin.cn/post/705232…

扩展:场景题:

如果在axios的拦截器里面统一捕获了401的后端错误,然后前端页面报‘登陆失效请重新登录’。如果登陆失效,当一进页面发了十个请求,不作处理是会立即弹出十条报错提醒的,请问如何解决这样不友好的报错提示?

2022/2/28

48.webpack构建过程有什么优化手段?weakpack优化打包提及,可以怎么做?

提升构建速度可以从三个维度入手,一是利用缓存,二是利用机器性能,三是减少需要打包的内容部分,内容预打包主要措施有:

  1. 多入口的情况下,使用commonsChunkPlugin来提取公共代码。
  2. 通过externals配置来提取常用库。
  3. 利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin对我们引入但是绝对不对修改的npm包进行预编译,再通过DllReferencePlugin 将预编译的模块加载进来。
  4. 使用Happypack实现多线程加速编译。
  5. 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用了多核并
  6. 使用Tree-shaking和Scope Hoisting来剔除多余代码

优化打包体积:

  1. 压缩代码:删除多余的代码,注释,简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件,利用cssnano(css-loader?minimize)来压缩css
  2. 利用CDN加速:在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
  3. Tree Shaking:将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数---optimize-minimize来实现

再补充一些优化前端性能的点: webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。 Code Splitting:将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利用浏览器缓存 提取公共第三方库:SplitChunkPlugin插件来进行公共模块抽取,利用浏览器缓存可以长期缓存这些无需频繁变动的公共代码

49.react和vue都使用了vdom,为什么我们需要使用vdom,vdom有什么好处?替我们解决了什么问题?

1.保证性能下限,在不进行手动优化的情况下,提供过得去的性能 页面渲染的流程:解析html -> 生成dom -> 生成cssom -> layout -> paint -> compiler 下面对比一下修改DOM时真实DOM操作和Virtual DOM的过程,来看一下它们重排重绘的性能消耗:

  • 真实DOM:生成HTML字符串+重建所有的DOM元素
  • 虚拟DOM:生成vNode+DOMDiff+必要的dom更新 Virtual DOM的更新DOM的准备工作耗费更多的时间,也就是js层面,相比于更多的DOM操作它的消费是极其便宜的。尤雨溪在社区论坛中说到:框架给你的保证是,你不需要手动优化的情况下,依然可以给你提供过得去的性能。 2.跨平台 Virtual DOM本质上是JavaSript的对象,它可以很方便的跨平台操作,比如服务端渲染,uniapp等。

50.对SPA单页面的理解,它的优缺点分别是什么?

SPA仅在web页面初始化时加载相应的HTML,JavaScript和css。一旦页面加载完成,spa不会因为用户的操作而进行页面的重新加载或跳转;取而代之是利用路由机制实现HTML内容的变换,UI与用户的交互,避免页面的重新加载。

  • 优点:
    • 用户体验好,快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染。
    • 基于上面一点,SPA相对对服务器压力小。
    • 前后端职责分离,架构清晰。
  • 缺点:
    • 初次加载耗时过多:为实现单页web应用功能及显示效果,需要在加载页面的时候将JavaScript,css统一加载,部分页面按需加载。
    • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有页面切换需要自己建立堆栈管理
    • SEO难度较大:由于所有的内容都在一个页面中动态替换显示,所以在SEO上有着天然的弱势。

2022/1/27

45.事件捕获和冒泡

冒泡:事件从目标元素冒泡到document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过attchEvent来添加监听函数,可以添加多个监听函数,会按顺序依次执行。 ---从里向外 捕获:指的是事件从document一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则依次执行。 ---从外向里

46.pointer-events属性不同值的作用

pointer-events:auto/ none/ visiblePainted/ visibleFill/ visibleStroke/ visible/ painted/ fill/ stroke/ all/ inherit

pointer-events属性有很多值,但是对于浏览器来说,只有auto和none两个值可用,其他的几个是针对SVG的(本身这个属性就来自于SVG技术)。pointer-events属性值详解:auto效果和没有定义这个属性相同,鼠标不会穿透当前层。在svg中国,该值和vieiblePainted的效果相同。none元素不再是鼠标事件的目标,鼠标不再监听当前层而去监听下面的层中的元素,但是如果它的子元素设置了pointer-events为其他值,比如auto,鼠标还是会监听这个子元素的。

47.weakmap和weakset的用处和场景。

WeakMap weakmap的设计目的在于,有时想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用。一旦不再需要这两个对象,就必须手动删除这个引用,否则垃圾回收机制就不会释放对象占用的内存。 weakmap的键名所用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,weakmap里面的键名对象和所对应的键值都会自动消失,不用手动删除引用。 weakSet 用于存储dom节点,而不用担心这些节点从文档移除时会引发内存泄漏。 即可以用来避免内存泄漏的情况。

2022/1/26

40.vue的$set原理

image.png

41.$nextTick原理

鲨鱼哥文章:juejin.cn/post/693970…

42.简述vue diff算法过程

鲨鱼哥文章:juejin.cn/post/695343…

43.vue中的methods为什么可以访问到实例,属性写在data中this为什么可以直接访问到?

若川大佬文章:juejin.cn/post/701092…

44.输出题

const async1 = async () => {
  console.log('async1');
  setTimeout(() => {
    console.log('timer1')
  }, 2000)
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 end')
  return 'async1 success'
} 
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then(res => console.log(res))
setTimeout(() => {
  console.log('timer2')
}, 1000)
/** 输出 
* script start
* async1
* promise1
* script end
* 1
* timer2
* timer1
**/

解析:

  1. 定义一个async函数,无事发生。
  2. 输出async start
  3. 执行saync1和then,然后将then里的回调函数放进微任务记为p1
  4. 输出async1,讲setTimeout:timer1放入宏任务记为s1,输出promise1,由于await和这个promise没有resolve或者reject,则async1在这里堵塞,且返回一个promise状态为pending
  5. 输出script end
  6. 遇到promise.resolve(1),将它后面的then回调放入微任务记为p2
  7. 遇到setTimeout:timer2放入宏任务记为t2
  8. 检查微任务队列:p1和p2
  9. 执行p1,发现async的状态为pending,所以进行输出
  10. 执行p2,发现then的回调函数不是函数,所以带着resolve(1)向下穿透,一直透透透,到了最后一个then,输出1
  11. 微任务执行完毕

第二轮: 12. 根据宏任务的延时时间执行t2,输出timer2 13. 输出timer1

2022/1/25

35.this指向问题

function fn () { console.log(this) }
var arr = [fn]
arr[0]()

详解:link.juejin.cn/?target=htt…

36.如何判断元素在当前可视区域内?

以图片显示为例:

  • window.innerHeight 是浏览器可视区的高度;
  • document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的距离;
  • imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离) 内容达到显示区域的:img.offsetTop < window.innerHeight + document.body.scrollTop

image.png

37.requestAnimationframe 相比setTimeout在执行动画时候的优势

requestAnimationframe优点:

  1. CPU节能: 当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停
  2. 函数节流: 在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,requestAnimationframe可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销,一个刷新间隔内函数执行多次是没有意义的,因为多数显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来
  3. 减少dom操作: requestAnimationframe会把每一帧中所有的dom操作集中起来,在一次重绘或回流中完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。

settimeout执行动画的缺点:它是通过设定间隔时间来不断改变图像位置,达到动画效果,但是容易出现卡顿,都懂的现象,原因是:

  • settimeout任务被放入异步队列,只有当主线程任务执行完后才会执行队列中的任务,因此实际执行时间总是比设定时间要晚;
  • settimeout的固定时间间隔不一定与屏幕刷新间隔时间相同,会引起丢帧。

38.react中setState是同步的还是异步的

setState并不是单纯同步/异步的,它的表现会因调用场景的不同而不同。在源码中,通过isBatchingUpdates来判断setState是先存进state队列还是直接更新,如果值为true则执行异步操作,false则直接更新。

  • 异步:在react可以控制的地方,就为true,比如在react生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。
  • 同步:在react无法控制的地方,比如原声事件,具体就是在addEventListener,setTimeout,setInterval等事件中,就只能同步更新。

39.定义变量a,让a==1 && a==2 && a==3 为true

详解:link.juejin.cn/?target=htt…

2022/1/24

31.如何判断一个对象是空对象?

  1. for...in
function checkObj(obj) {
    for(let i in obj) {
        return false
    }
    return true
}
let obj = {}
console.log(checkObj(obj)) //true

let obj = {a:1}
console.log(checkObj(obj)) //false

需要注意:for...in可以遍历原型上的属性,需要注意这一点。

let obj = {a:1}
let obj1 = Object.create(obj)

console.log(obj1) // {}
console.log(checkObj(obj1)) // false

2.JSON.stringity() 此方法有一定的漏洞且耗时

let obj = {}
if (JSON.stringify(obj) === '{}') {
    return true //是空对象
}

注意: 1. 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。 2. 布尔值,数字,字符串的包装对象在序列化过程中会自动转换成对应的原始值。 3. undefined,任意的函数以及symbol值,在序列化过程中会被忽略(出现在非数组对象的属性值中时),或者被转换为null(出现在数组中) 4. 所有以symbol为属性键的属性都会被完全忽略掉,即便replacer参数中强制指定包含了它们。 5. 不可枚举的属性会被忽略。

const a = {a:undefined}
let b = JSON.stringity(a)
console.log(b) //{}

const a = {a:{}=>{}}
let b = JSON.stringity(a)
console.log(b) //{}

3.Object.keys()

function checkObj(obj) {
    let arr = Object.keys(obj)
    return arr.length > 0?false:true
}

4.Object.getOwnPropertyNames() ->最优选择

let obj = {}
let arr = Object.getOwnPropertyNames(obj)
console.log(arr.length == 0) //true

区别:Object.keys()返回可枚举的,Object.getOwnPropertyNames()返回所有的(可枚举和不可枚举,但是不包括Symbol

32.HTTP/1.0和HTTP/1.1有什么区别

  • 连接方面,http1.0默认非持久连接,而http1.1默认是持久连接。http1.1通过使用持久链接来使多个http请求复用同一个TCP连接,以此来避免使用非持久连接时,每次需要建立连接的时间。

  • 资源请求方面,在http1.0中,存在一些浪费宽带的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,http1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206,(分段传输,可续传),这样方便了开发者自由的选择以便于充分利用带宽和连接。

  • 缓存方面,在http1.0中主要是使用header里的If-Modifield-Since,Expires来做为缓存判断的标准,http1.1引入了更多的缓存控制策略,例如Etag,If-Modifield-Since,If-Match,If-None-Match等更多可供选择的缓存头来控制策略缓存。

  • http1.1中新增了host字段,用来指定服务器的域名。http1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,这样就可以将请求发送到同一台服务器上的 不同网站。

  • http1.1相对于http1.0还新增了很多请求方法,如PUT,HEAD,OPTIONS等。

33.已知如下数组:var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组

let arr = [[1,2,2],[3,4,5,5],[6,7,8,9,[11,12,[12,13,[14]]]],10]
//扁平化
let flatArr = arr.flat(4)
//去重
let disArr = Array.from(new Set(flatArr))
//排序
let result = disArr.sort((a,b)=>a-b)
console.log(result)
//[1,2,3,4,5,6,7,8,9,10,11,12,13,14]

flat()方法对node版本有要求,至少要12.0以上

34.什么是GPU加速,如何使用GPU加速,GPU加速的缺点

GPU硬件加速是指应用GPU的图形性能对浏览器中的一些图形操作交给GPU来完成,因为GPU是专门为处理图形而设计,所以它在速度和能耗上更有效率 GPU加速通常包括以下几个部分:Canvas2D,布局合成(Latout Compositing),CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和(video). GPU实现动画的优缺点 - 优点: 利用了GPU合成图层实现动画,可以做到动画平滑,流畅,动画合成工作在GPU线程,不会被GPU的js运行阻塞 - 缺点: 绘图层必须传输到GPU,当图层较多时传输过程可能会导致渲染缓慢,每个复合层都需要消耗额外的内存,过多的内存可能导致浏览器的崩溃,复合层合成需要更多的时间。

2022/1/21

28.简单说下js的执行过程(变量提升,执行上下文,编译阶段,执行阶段,异步方法处理)ps:代码中存在多个相同的变量和函数他们怎么处理的

链接:juejin.cn/post/705151…

29.介绍下谷歌浏览器控制台的前端常用的调试工具

  • element面板:用于查看和编辑当前页面中的html和css元素。

  • network面板:用于查看http请求的详细信息,例如请求头,响应头及返回内容。

  • sources面板:用于查看和调试当前页面所加载的脚本的源文件。

  • resource面板:用于查看当前页面所请求的资源文件,如html,css样式文件等。

  • performance面板:用于分析页面加载的过程,进而提供减少页面加载时间,提升响应速度的方案,用于优化前端页面,加载网页加载速度等。

  • console面板:显示脚本中所输出的调试信息,或运行测试脚本等。

30.计算出时钟的时针和分针的角度(两个角度的较小者,四舍五入)。时间以HH:mm的格式传入。例如:angle(‘12:00’)//0,angle('23:30')//165

leetcode题目:link.juejin.cn/?target=htt…

2022/1/20

25.说说for...infor...of的区别,用什么方法可以过滤掉原型链上的属性名?怎样定义不可枚举的属性?用该方法定义的属性名在不改变方法描述的情况下怎样得到该属性名?

如何判断是否有迭代能力Array.prototype.hasOwnproperty(Symbol.iterator)

  • for...in可以遍历对象和数组,for...of不能遍历对象,但如果你想迭代一个对象的属性,可以for...in循环或内建的object.keys()方法。
  • for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
  • for...in遍历的索引为字符串类型。
  • for...of适用遍历树/数组对象/字符串/map/set等拥有迭代器对象的集合,但是不能遍历对象。
  • for...of与forEach()不同的是,它可以正确响应break,continue和return语句。
  • 具有迭代器对象(iterator) 才可以使用for...of 总结:遍历数组使用for...of,遍历对象使用for...in 使用object.hasOwnproperty()过滤原型链上的属性名。

定义不可枚举的属性可使用Object.defineProperty()Object.defineProperties()(补充:这两者的功能相同,Object.defineProperties()可以理解为Object.defineProperty()的升级版,可修改多个对象的属性)设置enumerable描述符为false

通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for...in或 Object.keys方法) 获取不可枚举的属性时,可使用Object.getOwnPropertyNames()方法,该方法可返回目标对象的可枚举值和不可枚举值,同时,还可使用Reflect.ownKeys()来获取。Reflect.ownKeys()返回值相当于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

26.slice和splice的区别

splice改变原数组,slice不会。 splice

  • 添加或删除元素,返回删除的元素
  • 会改变原数组
  • 语法:array.splice(start[,num,item1...itemn])
// 删除,第二个参数可选,没有则删除第一个参数位置以后所有的元素,为0则不删除
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.splice(2,1)
console.log(arr)// ['a', 'b', 'd']
console.log(arr1)// ['c']
// 新增
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.splice(2, 0, 'h')
console.log(arr)// ['a', 'b','h', 'c', 'd']
console.log(arr1)// []

slice

  • 返回被选中的元素
  • 不改变原数组
  • 语法:array.slice([start,end])
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.slice()
console.log(arr)// ['a', 'b', 'c', 'd']
console.log(arr1)// ['a', 'b', 'c', 'd']
// 1位置开始,2位置结束,但不包括2
let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.slice(1,2)
console.log(arr)// ['a', 'b', 'c', 'd']
console.log(arr1)// ['a', 'c', 'd']

let arr = ['a', 'b', 'c', 'd']
let arr1 = arr.slice(1)
console.log(arr)// ['a', 'b', 'c', 'd']
console.log(arr1)// ['b', 'c', 'd']

27. 说说mouseover和mouseenter的区别

  • 不论鼠标指针穿过被选元素或其子元素,都会触发mouseover事件(支持冒泡)。对应mouseout。
  • 只有在鼠标指针穿过被选元素,才会触发mouseenter事件(不支持冒泡)。对应mouseleave。

image.png

2022/1/19

20.css画扇形

.box {
    height:0;
    width:0;
    border-top:50px solid red;
    border-left:50px solid transparent;
    border-right:50px solid transparent;
    border-radius:50%;
}
.box{
    height:0;
    width:0;
    border:50px solid transparent;
    border-radius:50%;
    border-top-color:red;
    transform:rotate(90deg)
}

21.有了解过浏览器层(渲染层/复合层)等GPU渲染(如何触发GPU渲染?GPU渲染的好处)么?

相关文章:juejin.cn/post/684490… link.juejin.cn/?target=htt…

22. object.defineProperty有哪些描述符?

  • configurable 默认为false。 当且仅当该属性的configurable键值为true时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
  • enumerable 默认为false。 当且仅当该属性的值为true时,该属性才会出现在对象的枚举属性中。
  • value 默认为undefined 该属性对应的值,可以是任何有效的javascript值(数值,对象,函数等)。
  • writable 默认为false。 当且仅当该属性的值为true时,属性的值,也就是上面的value,才能被赋值运算符改变。 存取描述符还具有以下可选键值:
  • get属性的getter函数,如果没有getter,则为undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。 该函数的返回值会被用作属性的值。默认为undefined.
  • set属性的setter函数,如果没有setter,则为undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的this对象。 默认为undefined

23.object.freeze()有什么用?

  • Object.freeze()是es5新增特性,可以冻结一个对象,意思为不能向这个对象添加新的属性,不能修改其已有的属性值,不得删除已有属性,以及不能修改该对象已有属性的可枚举性;可配置性;可写性。防止对象被修改,但不能防止被修改引用。如果你有一个巨大的数组或Object,并且确信数据不会修改,使用Object.freeze()可以让性能大幅提升,多用于展示。
  • freeze可以用来实现const
  • object.freeze可以让vue不添加响应。

24.call,apply,bind的区别

  • 都用来改变this指向,当第一个参数传入null或undefined时,this指向window,在传参的时候,bind可传入多个参数,而apply传入的是数组或类数组对象。
  • bind返回的是一个函数,而apply/call返回的是执行结果。
  • 当bind返回的函数作为构造函数时,bind时传入的this值会失效,但是参数仍然生效。
  • bind可实现两次传参,调用函数的时候会把两次的传参合并起来,类似于柯里化传参。

2022/1/18

16.let var const 的区别

  1. 块级作用域:块作用域由{}包括,let const有块级作用域,var是全局作用域。块级作用域解决了es5中的两个问题:内层变量可能覆盖外层变量,用来计数的循环变量泄漏为全局变量。
  2. 变量提升:var存在变量提升,let和const不存在变量提升,即在变量声明后使用,否则会报错。
  3. 给全局添加属性:浏览器的全局对象是window,node的全局对象是global,var声明的变量是全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。
  4. 重复声明:var声明变量时,可以重复声明
  5. 暂时性死区: 在使用let const声明变量之前,该变量丢失不可用的。在语法上称为暂时性死区。使用var不存在暂时性死区。
  6. 初始值设置: 在变量声明之前,var和let可以不用设置初始值。const声明变量必须设置初始值。
  7. 指针指向: let const都是es6新增的用于创建变量的语法。let创建的变量是可以改变指针指向(可以重新赋值),const声明的变量不允许改变指针的指向。

17.判断对象中是否包含某个属性(至少三种)

  1. 点(.)或者方括号([])
let obj = {a:1}
console.log(obj.b) // undefined
console.log(obj[b]) // undefined
//可根据obj.b ==undefined或obj[b]==undefined判断是否存在该属性。但是这个方法有一个弊端,当obj存在b属性,但属性b的值为undefined时,会判断出错。
  1. in运算符
//如果指定的属性在指定的对象或其原型链中,则in运算符返回true

let obj = {
    a:1,
    b:undefined
}
'a' in obj // true
'b' in obj // true
'c' in obj // false
let obj = {a:1}
let obj1 = Object.create(obj)
'a' in obj1 // true
这种方法可以判断正常属性值为undefined的情况,但是无法判断出属性是对象的属性还是对象原型上的属性。

3.hasOwnProperty()

let obj = {a:1}
let obj1 = Object.create(obj,{
    b:{
        value:1,
        enumerable:true
    }
})
obj1.hasOwnProperty('b') // true 自身属性
obj1.hasOwnProperty('c') // false 不存在
obj1.hasOwnProperty('a') // false 原型链上不存在

4.Object.keys(obj).includes('a')

let obj = {a:1}
Object.keys(obj).includes('a') // true

5.Reflect.has() 静态方法Reflect.has()作用与in操作符相同

let obj = {a:1}
Reflect.has(obj,'a') //true

18.vue-router 的实现

blog.csdn.net/qq_27674439…

  1. hash实现: hash是URL中hash(#)及后面的那部分,常用作锚点在页面中进行导航,改变URL中的hash部分会引起页面刷新,通过hashchange时间监听url的变化,改变URL的方式有以下几种:
    • 通过浏览器前进后退改变url
    • 通过标签改变url
    • 通过window.location改变url

HashHistory.push()  将新路由添加到浏览器访问历史的栈顶 HashHistory.replace() 替换掉当前的路由

  1. history实现history提供了pushState和replaceState两个方法,这两个方法改变url的path部分不会引起页面刷新,与hash模式类似。 history 提供类似hashchange事件的popstate事件,但popstate事件有些不同:
    • 通过浏览器前进后退改变url时会触发popstate事件
    • 通过pushState/replaceState或标签改变url不会触发popState事件,好在我们可以拦截pushState/replaceState的调用和标签的点击事件来检测url变化。
    • 通过js调用history的back go forward方法可触发该事件,所以监听url变化可以实现,只是没有hashchange那么方便 核心参考原理:juejin.cn/post/685457…

19.js执行上下文创建过程

一个执行上下文的生命周期包括两个阶段

  1. 创建阶段,在这个阶段,执行上下文分别创建变量对象建立作用域链,以及确定this指向。
  2. 代码执行阶段完成变量赋值,函数引用以及执行其他代码。

相关文章:juejin.cn/post/684490… juejin.cn/post/684490…

2022/1/17

12.说一下options请求

options请求可以用来询问支持的请求方法,用来跨域请求,也就是我们常说的预检请求。 跨域共享标准规范要求: 对于可能对服务器数据产生副作用的http请求方法(特别是get以外的http请求,或者搭配某些MIME类型的POST请求),浏览器必须首先使用OPTIONS方法发起一个预检请求,来判断服务端是否允许跨域请求

主要用途: · 获取服务器支持的所有http请求方法。· 检查访问权限(CORS跨域资源共享)

详细内容可参考:juejin.cn/post/684490…

13.跨域是什么?怎么解决跨域?

跨域: 浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。

同源策略: 限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。

同源: 协议;端口号;域名必须一致。

解决跨域:

1.cors:关键是服务器,只要服务器实现了cors请求就可以跨源通信了。

2.jsonp: 原理:利用script标签没有跨域限制,通过script标签src属性,发送带有callback参数的get请求,服务端收到请求后将结果拼凑到callback函数中,返回给浏览器,让浏览器去执行。 缺点: 只支持get方法;不安全,可能会遭到XSS攻击。

3.postMessage可解决的问题:

  • 页面和其打开的新窗口之间的数据传递。

  • 多页面之间的数据传递。

  • 页面和其iframe之间的数据传递。

  • 以上三种的跨域数据传递。 用法:postMessage(data,origin)

  • data: h5规定支持任意基本数据类型和可复制对象,但部分浏览器只支持字符串,最好使用JSO.stringify序列化一下。

  • origin: 协议+主机+端口号,也可以设置为*表示支持传递给任意窗口,如果要和当前窗口同源可以设置为/

4.document.domain+iframe 仅限主域相同,子域不同的情况。原理: 两个页面都强制设置document.domain为基础主域,就实现了同域。

5.nginx代理: 远离和cors跨域一样,通过设置请求响应头Access-Controll-Allow-Origin 等字段

6.node中间件 原理同nginx代理,都是启用一个代理服务器实现数据的转发。

7.location.hash +iframe 原理: a与b跨域通信,用c来做中间件。不同域之间用iframe的location.hash传值,相同域直接使用parent.parent访问对象。

8.window.name + iframe

9.webScoket协议

14.http和tcp的区别

http是应用层协议,是用来说明请求的一些信息的,有请求头请求行请求体,在传输过程的作用是提供一个网址,供后面层去查ip地址。

tcp是传输层协议,应用层协议被传输层加上tcp首部,传给下一层,首部中提供了端口号,tcp是有差错控制的,靠首部校验和这个字段来做一些检验操作,保证传输过程中没有出错。

http协议是建立在tcp协议之上的一种应用。

扩展:为什么http无连接,但http基于tcp来实现,tcp却是面向连接。

tcp的面向连接是基于网络底层的数据传输,http的无连接是基于应用层面的沟通交互,http使用tcp是为了保证数据传输的可靠性和完整性。

15.什么是节流防抖,以及应用场景。

节流:在一定时间内,如果再次触发事件,不予处理,直到计时完会才能再次触发。

应用场景:解决一个需要频繁发生的事件,防止事件触发太多次,可使用在scroll函数的事件监听上,降低事件调用频率。

//定时器版本
function throttle(fn,await) {
    let timer = null
    return function(){
        let context = this,args = arguments
        if(!timer) {
            const timer = setTimeout(()=>{
                clearTimeout(timer)
                fn.apply(context,args)
            },await)
        }
    }
}
//时间戳版本
function throttle(fn,await) {
    let curTime = Date.now()
    return function(){
        let context =this,args = arguments,nowTime = Date.now()
        //如果两次时间超过了规定的时间,则执行函数
        if(nowTime - curTime >=await ) {
            curTime = Date.now()
            return fn.apply(context,args)
        }
    }
}

防抖:在一定时间内,如果再次触发事件,则取消计时,重新计时。

应用场景:输入框onchange事件远程实时查询,触发取消上次的事件。

function debounce(fn,await) {
    let timer = null
    return function() {
        let context = this,args = arguments
        if(timer) {
            clearTimeout(timer)
        }
        const timer = setTimeout(()=>{
            fn.apply(context,args)
            cleaTimeout(timer)
        })
    }
}

2022/1/14

7.输入一个url到页面渲染发生了什么?

1.浏览器地址栏输入url并回车 2.浏览器查找当前url是否存在缓存,并比较缓存是否过期。 3.NDS解析URL对应的IP 4.根据IP建立TCP链接(三次握手) 5.发送http请求 6.服务器处理请求,浏览器接受http响应。 7.浏览器解析并渲染页面。 8.关闭TCP连接(四次挥手)

8.flex:1的属性组成?

flex 是 flex-grow;flex-shrink和flex-basis三个属性的缩写。

flex-grow:定义项目的放大比例,默认为0,如果存在剩余空间,也不放大。 flex-shrink:定义项目的缩小比例,默认为1,如果空间不足,项目缩小/ flex-basis:定义了再分配多余空间之前,项目战局的主轴空间。浏览器根据这个属性,计算主轴是否有多余空间,默认值为auto,即项目本来的大小。

9.Promise.all()和Promise.race()和Promise.allsettled()有什么区别?

-Promise.all():接受一个promise数组作为参数,如果不是则会调用promise.resolve()方法,将参数转为Promise实例再进一步处理(参数可以不是数组,但是必须具有Iterator接口,且返回的每个成员都是Promise实例),当数组内每个成员的状态变为成功状态时,返回由成员返回值组成的数组。当数组内有一个或多个成员变为失败状态时,返回第一个失败成员的返回值。

-Promise.race():参数同Promise.all(),只要数组成员有一个成员状态改变,Promise.race()返回的promise实例状态就会改变。

-Promise.allsettled() (ES2020):参数同Promise.all(),Promise.all()可以确定所有请求都成功了,但是只要一个请求失败,就会报错,不管另外的请求是否结束,而Promise.allsettled()来确定一组异步操作是否都结束了(不管成功或失败),当数组每个成员状态都改变时,Promise.allsettled()返回的心promise对象才会改变。

10.说一下nextTick是做什么的?

nextTick()是将回调函数延迟在下一次dom更新数据后调节,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,nextTick多次调用会维持一个数组,之后会异步的把数组中的方法以此执行,这样的话用户就会在视图更新后再获得真实的dom元素

nextTick和nextTick的区别:1.nextTIck(callback):当数据发生变化,更新后执行回调,在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM2.nextTick的区别: 1.nextTIck(callback):当数据发生变化,更新后执行回调,在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。 2.nextTick(callback):当dom发生变化,更新后执行的回调。将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新。 ps:这两个方法没有太大不同,区别在于nextTick(callback)是全局的方法;而$nextTick(callback)是回调的this自动绑定到调用它的实例上,所以用的更多的是nextTick(callback)

###11.uselmperativeHandles和forwardRef的作用? ref是为了获取某个节点的实例,但是函数式组件(PureComponent)是没有实例的,不存在this的,这种时候是拿不到函数式组件的ref的,为了解决这个问题,由此引入React.forwardRef,React.forwardRef 允许某些组件接收ref,并将其向下传递给子组件

· uselmperativeHandles:在函数式组件中,用于定义暴露给父组件的ref方法。 · React.forwardRef:将ref父类的ref作为参数传入函数式组件中,本身props只带有children这个参数,这样可以让子类转发父类的ref,当父类把ref挂在子组件上时,子组件外部通过forwardRef包裹,可以直接将父组件创建的ref挂载到子组件的某个dom元素上

2022/1/13

4.试解释setTimeout和setInterval定时器无法按时执行的原因

1.js是单线程,所以异步事件(比如鼠标点击和定时器)仅在线程空闲时才会被调度运行,代码执行时异步事件任务会按照将它们添加到队列的顺序执行,而setTimeout()的第二个参数只是告诉js再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行。

setTimeout的时间,并不是回调执行的时间,而是进入任务队列的时间,但有可能进入队列的时候前面的任务没有执行完,所以会有比设定时间晚执行的情况

5.函数调用的几种方式

4种:一般形式的函数调用;作为对象的方法调用;使用call和apply动态调用;使用new间隔调用。 那么既然都有apply/call,为什么没有bind呢,因为bind返回的是一个函数,并没有调用,实现上相当于只是在一个对象上挂了一个属性方法,此外IIFE属于自执行函数,js引擎会直接执行,并不需要调用(个人观点有待商榷)

6.现有全局构造函数 function f(){},说出f.proto===f.proto.constructor.proto 的输出结果

function f(){}
f.__proto__===f.__proto__.constructor.__proto__ //true

关于函数: 任何函数的__proto__都是Function.prototype,并且Function.proto 和 Function.prototype是相等的,函数是个特殊情况,其他的不会出现自己的隐式原型和自己的显式原型一样的情况。

Function._proto_ === Function.prototype //true对于函数自己生成自己的说法解释
//Function作为一个内置对象,是运行前就已经存在的东西,所以根本就不会根据自己生成自己。
//这种现象可能的解释:1.只是简单表明一种关系。2.为了和其他函数保持一致。

解析:

constructor 构造器   __proto__ 隐式原型  prototype 显式原型
f.__proto__ == Function.prototype
Function.prototype.constructor == Function
Function.__proto__ == Function.prototype

这个最对:
Object.__proto__ == Function.prototype
Function.prototype.__proto__ == Object.prototype

image.png

今日扩展:

1.为什么用setTimeout模拟setInterval会比较好:setInterval 会漏执行

间隔执行器每到固定间隔就添加此执行器到队列

· 如果队列中已经有此执行器的任务,就终止添加

· 如果没有未执行的当前同个执行器任务,就正常添加

· 如果有个当前同个执行器任务正在被处理,说明任务已经从队列中移走了,所以也添加 ps:例如间隔是1s执行,那么每过1s,都会添加一次,但如果interval代码很长,要执行2s,执行代码时间大于设置的时间会漏执行。

2.关于promise值穿透:

    Promise.resolve(1)
        .then(2)
        .then(Promise.resolve(3))
        .then(console.log)
   //1

.then或.catch的参数期望是函数,传入非函数会发生值穿透

3.移动端click:为何会有300ms延迟

由于手机屏幕小,用户在浏览网页时可能需要放大或缩小屏幕,而300ms延迟就是用来判断用户操作时是双指还是三指还是单指。

2022/1/12

1:vue2和vue3的区别:

1:双向数据绑定原理发生了变化: vue2双向绑定是利用ES5的Object.defineProperty()对数据进行劫持,结合发布订阅模式来实现. vue3是使用ES6的Proxy API对数据代理

2:根节点: vue2只有一个根节点 vue3可以有多个根节点

3:API vue2使用选项类型API(Options API):用属性来分割组件 vue3使用组合型API(Composition API):用方法来分割组件 ps:结合API可以根据逻辑性轻松组织代码,选项类型API的问题是this,由于代码依赖组件,难以按兴趣单元汇总

4:建立数据 vue2把数据放在data中

        export default {
            data(){
                return{
                    name:''
                }
            }
        }
   

vue3使用setup方法,在组件初始化构造的时候触发

    <template>
         <div><h2>{{state.name}}</h2></div>
    </template>
    <scrpit>
        import {reactive} from 'vue' //引入reactive声明数据为响应性数据
        export default {
            props:{
                title:String
            },
            setup(){ //使用setup方法来返回我们的响应性数据
               const state = reactive ({
                   name:'',
               })
               return {state}
            }
        }
    </scrpit>
    

5.生命钩子变化

vue2vue3
beforeCreatedsetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeDestroy
destroyedonUnmounted
activatedonActivated
deactivatedonDeactivated

6.传参不同: 父传子:vue3通过props接收并通过toRefs转成响应式toRefs(props) 子传父:在vue2中会调用this.$emit然后传入史建明和参数对象. 在vue3中没有this,只能通过setup中的参数传递

7.vue3较vue2体积小,速度快 8.在vue2中主要用的是观察者模式,不管数据多大,都会对数据进行创建检查者,而vue3对数据进行了懒观察,仅对可见部分数据进行了懒观察,大大节省了性能.

新增特性: Performace:性能优化 Tree-shaking support : 支持摇树优化 Composition API:组合API Fragment,Teleport,Suspence :新增的组件 Better TypeScript support :更好的TyprScript支持 Custom Renderer API:自定义渲染器

2.vue3.0里为什么要用proxy API替代definedProperty API?

definedProperty API的弊端:

  • vue2是通过Object.definedProperty中的getter和setter函数进行数据劫持完成数据响应的.
  • 无法直接监听属性的新增和删除
  • 无法直接监听数组(尽管重写了数组方法,但也仅能完成7个数组方法的监听)

proxy的优势:

  • 作为vue3中替代definedProperty的新API,相当于给一个对象的外层加了一层拦截,这层拦截可以做很多操作,比如数据信息的过滤,修改,收集数据信息.
  • 因为实在整个对象外层进行了拦截,所以可以操作对象中的属性同时可以监听属性的新增和删除
  • proxy可以监听数组的变化

3.vue3.0所采用的ComOption API与vue2使用的Options API有什么区别?

Options API: vue2.0为了向组件中添加逻辑,我们填充属性,例如:data methods mounted 以及computed等,这个API最大的缺点,是其本身并不是有效的js代码,我们在使用Options API的时候,需要确切的了解我们具体可以访问到哪些属性,以及我们访问到的当前属性的行为,在后台VUE需要将此属性转换为工作代码,因为我们无法从自动建议和类型检查中收益,因此给我们在使用相关属性时,造成了一定弊端.

ComOption API: vue3.0中通过ComPosition API来解决这一问题,目的是为了将组件重的可用属性,作为js函数直接暴露出来的机制解决上面我们所处的问题,代码更易读,更易理解和学习,没有任何幕后操作. ComPosition API 的好处不仅仅是以不同的方式进行编码,更重要的是对于代码的重用,不受模板和组件范围的限制,也可以准确地知道我们可以使用哪些属性,由于幕后没有什么操作,所以编辑器可以帮助我们进行类型检查和建议.


作者:NexusFeng
链接:juejin.cn/post/705232… 来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。