题组1

var和let const的区别

  1. var是ES5语法,let const是ES6语法;var有变量提升
  2. var和let是变量,可修改;const是常量,不可修改;
  3. let const有块级作用域,var没有

    typeof返回哪些类型

  4. 值类型:undefined string number boolean symbol

  5. 引用类型:object
  6. function

    列举强制类型转换和隐式类型转换

  7. 强制:parseInt、parseFloat、toString等

  8. 隐式:if、逻辑运算、==、+号拼接字符串

题组2

手写深度比较,模拟lodash.isEqual

  1. // 判断是不是对象
  2. function isObject (obj) {
  3. return typeof obj === 'object' && Object !== null
  4. }
  5. // 深度比较
  6. function isEqual (obj1, obj2) {
  7. if (!isObject(obj1) || !isObject(obj2)) {
  8. // 如果其中一个不是对象或数组,直接进行判断,一般是值类型
  9. return obj1 === obj2
  10. }
  11. if (obj1 === obj2) {
  12. return true
  13. }
  14. // 两个都是对象或数组,而且不相等
  15. // 先比较object的属性个数,不一样的话肯定不相等
  16. const obj1Keys = Object.keys(obj1)
  17. const obj2Keys = Object.keys(obj2)
  18. if (obj1Keys.length !== obj2Keys.length) {
  19. return false
  20. }
  21. // 以obj1为基准,和obj2依次递归比较
  22. for (let key in obj1) {
  23. // 比较当前key的value 递归
  24. const res = isEqual(obj1[key], obj2[key])
  25. if (!res) {
  26. return false
  27. }
  28. }
  29. return true
  30. }

split()和join()的区别

  1. split把字符串拆分成数组
  2. join把数组拼接成字符串

    数组的pop push unshift shift分别做什么

  3. 功能是什么?返回值是什么?是否会对原数组造成影响?

  4. pop:把数组最后一个元素弹出,返回值是弹出的这个元素,会修改原数组
  5. push:往数组追加一个元素,返回值是修改后的数组长度,会修改原数组
  6. unshift:往数组第一个位置插入元素,返回值是修改后的数组长度,会修改原数组
  7. shift:把数组第一个元素弹出,返回值是弹出的这个元素,会修改原数组

    数组哪些API是纯函数

  8. 纯函数:不改变原数组(没有副作用)、返回一个数组

  9. concat:在数组后面再追加一个数组,返回新数组
  10. map:给数组每个元素都执行一个函数,返回新数组
  11. filter:过滤数组元素,返回新数组
  12. slice:截取从start到end(不包含该元素)的数组元素,返回新数组
  13. 非纯函数:pop push shift unshift forEach some every reduce

题组3

数组slice和splice的区别

  1. 功能区别:slice是切片,splice是剪接
  2. slice(start,end):截取从start到end(不包含该元素)。
    如果只有一个参数,就从start截取到数组结束,
    如果参数是负数,就从数组末尾开始截取。
  3. splice看这篇博客:https://blog.csdn.net/weixin_45726044/article/details/120151153
  4. slice是纯函数,splice不是

    [10,20,30].map(parseInt)返回结果是什么?

  5. 结果:[ 10, NaN, NaN ]

  6. 拆解

    1. [10, 20, 30].map((num, index) => {
    2. return parseInt(num, index)
    3. })
  7. parseInt第二个参数:表示要解析的数字的基数。该值介于 2 ~ 36 之间。
    如果省略该参数或其值为 0,则数字将以 10 为基础来解析。
    如果它以 “0x” 或 “0X” 开头,将以 16 为基数,也就是16进制。
    如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。、

  8. 参考博客:https://www.jianshu.com/p/bec353b4eaaf

    ajax请求get和post的区别?

  9. get一般用于查询操作,post一般用户提交操作

  10. get参数拼接在URL上,post放在请求体内(数据体积可更大)
  11. 安全性:post易于防止CSRF攻击

题组4

函数call和apply的区别?

  1. call参数零散传入,apply以集合的形式传入
    image.png

    事件代理(委托)是什么

  2. image.png

    闭包是什么?有什么特性?有什么负面影响?

  3. 回顾作用域和自由变量

  4. 回顾闭包应用场景:作为参数被传入,作为返回值被返回
  5. 回顾自由变量的查找:在函数定义的地方查找(而非执行的地方)
  6. 影响:变量会常驻内存,得不到释放(不一定是内存泄露)。闭包不要乱用。
  7. 代码示例 ```javascript // // 自由变量示例 —— 内存会被释放 // let a = 0 // function fn1() { // let a1 = 100

// function fn2() { // let a2 = 200

// function fn3() { // let a3 = 300 // return a + a1 + a2 + a3 // } // fn3() // } // fn2() // } // fn1()

// // 闭包 函数作为返回值 —— 内存不会被释放 // function create() { // let a = 100 // return function () { // console.log(a) // } // } // let fn = create() // let a = 200 // fn() // 100

// 函数作为参数 function print(fn) { let a = 200 fn() } let a = 100 function fn() { console.log(a) } print(fn) // 100

  1. ---
  2. <a name="HlAIW"></a>
  3. ## 题组5
  4. <a name="zpW36"></a>
  5. ### 如何阻止事件冒泡和默认行为?
  6. 1. 阻止冒泡:event.stopPropagation()
  7. 2. 阻止默认行为:event.preventDefault()
  8. <a name="phx36"></a>
  9. ### 查找、添加、删除、移动DOM节点的方法?
  10. 1. 查找:getElementByIdgetElementsByTagNamegetElementsByClassNamequerySelectorAll
  11. 2. 添加:createElementappendChild
  12. 3. 移动:先获取再插入
  13. 4. 删除:removeChild
  14. <a name="HJ6pg"></a>
  15. ### 如何减少DOM操作?
  16. 1. 缓存DOM查询结果
  17. 2. 多次DOM操作,合并到一次插入
  18. ---
  19. <a name="P9SoL"></a>
  20. ## 题组6
  21. <a name="vClwP"></a>
  22. ### 解释JSONP的原理,为何它不是真正的Ajax?
  23. 1. 浏览器的同源策略(服务端没有同源策略)和跨域
  24. 2. script img link标签可以绕过跨域
  25. 3. jsonp的原理就是定义一个全局函数去访问JS,通过script标签
  26. 4. Ajax是通过XMLHttpRequest
  27. <a name="CfYYo"></a>
  28. ### document load 和 ready 的区别
  29. 1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/32815999/1664720772469-b792b570-ea3f-4e96-b7ef-f1a8653a5102.png#clientId=u0131cbc5-bee7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=180&id=u02a17f06&margin=%5Bobject%20Object%5D&name=image.png&originHeight=322&originWidth=1202&originalType=binary&ratio=1&rotation=0&showTitle=false&size=224466&status=done&style=none&taskId=u8eb16827-9cf1-4b8c-85c5-68443559720&title=&width=673)
  30. <a name="CLY4F"></a>
  31. ### == 和 === 的不同
  32. 1. ==会尝试类型转换
  33. 2. ===是值和类型完全相等
  34. 3. 只有判断null的时候才用==
  35. ---
  36. <a name="uHfGF"></a>
  37. ## 题组7
  38. <a name="QEsyX"></a>
  39. ### 函数声明和函数表达式的区别
  40. 1. 函数声明function fn(){…}
  41. 2. 函数表达式const fn=function() {...}
  42. 3. 函数声明会在代码执行前预加载(和变量提升有些相似),而函数表达式不会
  43. <a name="SphQ5"></a>
  44. ### new Object() 和 Object.create() 的区别
  45. 1. {} 等同于 new Object(),原型Object.prototype
  46. 2. Object.create(null)没有原型
  47. 3. Object.create({...})可以指定原型
  48. 4. 代码示例
  49. ```javascript
  50. const obj1 = {
  51. a: 10,
  52. b: 20,
  53. sum() {
  54. return this.a + this.b
  55. }
  56. }
  57. const obj2 = new Object({
  58. a: 10,
  59. b: 20,
  60. sum() {
  61. return this.a + this.b
  62. }
  63. })
  64. // obj1和obj2并不全等,只是值一样
  65. const obj21 = new Object(obj1)
  66. // obj1 === obj21 内存地址也相等,两者同时改变
  67. const obj3 = Object.create(null) // {}
  68. const obj4 = new Object() // {}
  69. // 虽然两者都是{},但是obj3没有属性没有原型,obj4没有属性有原型
  70. const obj5 = Object.create({
  71. a: 10,
  72. b: 20,
  73. sum() {
  74. return this.a + this.b
  75. }
  76. })
  77. // obj5是把传入的东西当做原型使用,不是属性
  78. const obj6 = Object.create(obj1)
  79. // obj6的原型指向obj1,两者同时改变
  80. // obj6.__proto__ === obj1
  1. image.png

    关于this的场景题

  2. image.png

  3. 第一个打印1
  4. 第二个返回undefined,因为this指向window,在执行的时候才能确定this

题组8

关于作用域的自由变量的场景题1

  1. image.png
  2. 输出三个4,因为i的作用域是全局,当启动定时器的时候,i是4
  3. 如果改成for (let i =1; i <= 3 ; i++)就会输出1 2 3

    关于作用域的自由变量的场景题2

  4. image.png

  5. 100 10 10

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

  6. 正则表达式使用const reg = /^[a-zA-Z]\w{5,29}$/

  7. ^表示以什么开头
  8. [a-zA-Z]表示大小写字母
  9. \w是命中字母数字下划线
  10. {5,29}表示长度
  11. $是以什么结尾
  12. 学习:https://www.runoob.com/regexp/regexp-syntax.html
  13. 测试工具:https://tool.oschina.net/regex#
  14. https://c.runoob.com/front-end/854/

题组9

手写字符串trim方法,保证浏览器兼容性

  1. String.prototype.trim = function () {
  2. // 利用正则表达式把字符串前后的空格换成空字符
  3. return this.replace(/^s+/, '').replace(/\s+$/, '')
  4. }

如何获取多个数字的最大值

  1. 使用Math.max()
  2. 手写

    1. function max () {
    2. const nums = Array.prototype.call(arguments) // 变成数组
    3. let max = -Infinity
    4. nums.forEach(n => {
    5. if (n > max) {
    6. max = n
    7. }
    8. })
    9. return max
    10. }

    如何用JS实现继承?

  3. class继承

  4. prototype继承

题组10

如何捕获JS程序中的异常?

  1. try… catch 手动捕获异常
    image.png
  2. 自动捕获异常
    image.png

    什么是JSON?

  3. JSON是一种数据格式,本质是一段字符串。属性名用双引号包裹

  4. JSON格式和JS对象结构一致,对JS语言更友好
  5. window.JSON是一个全局对象。JSON.stringify和JSON.parse

    获取当前页面URL参数

  6. 传统方式,查找location.search

  7. 新API,URLSearchParams

    1. // 传统方式
    2. function query(name){
    3. const search = location.search.substr(1) // 把查询参数第一位问号截取掉
    4. // search: 'a=10&b=20&c=30'
    5. const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`,'i')
    6. const res = search.match(reg)
    7. if(res == null){
    8. return null
    9. }
    10. return res[2] // 是数值
    11. }
    12. // URLSearchParams
    13. function query(name){
    14. const search = location.search
    15. const p = new URLSearchParams(search)
    16. return p.get(name)
    17. }

题组11

将URL参数解析为JS对象

  1. 传统方式
    image.png
  2. 第二种方式
    image.png

    手写数组flatern,考虑多层级

  3. contact方法可以摊平 ```javascript function flat (arr) { // 判断还有没有深层数组 const isDeep = arr.some(iten => iten instanceof Array) // 已经摊平 if (!isDeep) {

    1. return arr

    } // 利用contact可以摊平一层深度的数组 const res = Array.prototype.concat.apply([], arr) // 继续递归 return flat(res) }

const res = flat([1, 2, [3, 4, [10, 20]], 5]) console.log(res);

  1. <a name="lWLwG"></a>
  2. ### 数组去重
  3. 1. 传统方式,遍历元素挨个比较、去重
  4. 2. 使用Set
  5. 3. 考虑计算效率,建议使用Set
  6. ```javascript
  7. // 传统方式
  8. function unique (arr) {
  9. const res = []
  10. arr.forEach(item => {
  11. // 如果找不到这个item对应的索引就加入
  12. if (res.indexOf(item) < 0) {
  13. res.push(item)
  14. }
  15. })
  16. return res
  17. }
  18. // 使用Set(无序,不能重复)
  19. function unique (arr) {
  20. const set = new Set(arr)
  21. return [...set]
  22. }
  23. const res = [1, 1, 2, 3, 4, 2, 5]
  24. console.log(unique(res));

题组12

手写深拷贝

  1. Object.assign不是深拷贝,一般用于给对象追加属性

    1. function deepClone (obj = {}) {
    2. if (typeof obj !== 'object' || obj == null) {
    3. // 如果不是对象或者数组,直接返回值
    4. return obj
    5. }
    6. // 初始化返回结果
    7. let result
    8. if (obj instanceof Array) {
    9. result = []
    10. } else {
    11. result = {}
    12. }
    13. // 遍历obj key是键名
    14. for (let key in obj) {
    15. // 保证key不是原型的属性
    16. if (obj.hasOwnProperty(key)) {
    17. // 递归调用 遇到对象或者数组会再深入一层
    18. result[key] = deepClone(obj[key])
    19. }
    20. }
    21. // 返回结果
    22. return result
    23. }

    介绍一下RAF(requestAnimationFrame)

  2. 要想动画流畅,更新频率要60帧/s,即16.67ms更新一次视图

  3. setTimeout要手动控制频率,而RAF浏览器会自动控制
  4. 后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行

    1. // 3秒之内把宽度从100px变成640px,即增加540px
    2. // 60帧每秒,三秒180帧,每秒变化3px
    3. let currentWidth = 100
    4. const maxWidth = 640
    5. const div1 = document.getElementById('div1')
    6. /* function animate () {
    7. currentWidth += 3
    8. div1.style.width = currentWidth + 'px'
    9. if (currentWidth < maxWidth) {
    10. // 需要自己控制事件
    11. setTimeout(animate, 16.67)
    12. }
    13. } */
    14. function animate(){
    15. currentWidth += 3
    16. div1.style.width = currentWidth + 'px'
    17. if (currentWidth < maxWidth) {
    18. // 浏览器自动控制
    19. window.requestAnimationFrame(animate)
    20. }
    21. }
    22. animate()

    前端性能如何优化?从哪几个方面考虑

  5. 原则:多使用内存、缓存,减少计算、减少网络请求

  6. 方向:加载页面,页面渲染,页面操作流畅度

Map和Set

有序和无序

  1. 案例分析
    image.png
  2. 对象Object就是一个无序结构,里面的属性位置随意。
  3. 数组Array是有序的结构,不能随便调换位置。

    Map和Object的区别

  4. API不同,Map可以以任意类型为key。函数、对象、字符串都可以

  5. Map是有序结构(重要)
  6. Map操作同样很快,两者速度差不多。

    Set和Array的区别

  7. API不同

  8. Set元素不能重复
  9. Set是无序结构,操作很快。

WeakMap和WeakSet(进阶)

  1. 弱引用,防止内存泄露
  2. WeakMap只能也用对象作为key,WeakSet只能用对象做value
  3. 没有forEach和size,只能用add delete has

数组reduce的用法

  1. 数组求和 ```javascript // 传统方式 / function sum (arr) { let res = 0 arr.forEach(n => res = res + n) return res } /

const arr = [10, 20, 30, 40] // 完整写法 /* const res = arr.reduce((sum, curVal, index, arr) => { console.log(‘sum’, sum); console.log(‘curVal’, curVal); console.log(‘index’, index); console.log(‘arr’, arr);

  1. return sum + curVal // 返回值,会作为下一次执行的第一个参数sum的值

}, 0) // 0是sum的初始值 */

// 简化写法 const res = arr.reduce((sum, curVal) => sum + curVal, 0) console.log(‘res’,res);

  1. 2. 计数
  2. ```javascript
  3. const arr2 = [10, 20, 30, 20, 100, 20]
  4. const n = 20
  5. const count = arr2.reduce((count, val) => {
  6. return val === n ? count + 1 : count
  7. }, 0)
  8. console.log(count);
  1. 拼接字符串
    1. const arr3 = [
    2. { name: '张三', age: '20' },
    3. { name: '李四', age: '21' },
    4. { name: '小明', age: '22' },
    5. ]
    6. const result = arr3.reduce((str,item) => {
    7. return `${str}${item.name} - ${item.age}\n`
    8. }, '')
    9. console.log(result);