JS基础知识学习--真题总结

举报
楚楚冻人玥玥仙女 发表于 2021/11/19 00:18:56 2021/11/19
【摘要】 JS真题 题目一 1. var和let const的区别 答: var是ES5语法,let、const是ES6语法; var有变量提升 var和let是变量,可修改; const...

JS真题

题目一

1. var和let const的区别

答:

  • var是ES5语法,let、const是ES6语法; var有变量提升

  • var和let是变量,可修改; const是常量,不可修改;

  • let、const有块级作用域,var没有

2. typeof返回哪些类型

答:

  • undefined、string、number、boolean、symbol

  • object(注意,typeof null === ‘object’)

  • function

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

答:

  • 强制:parseInt、parseFloat、toString等

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

题目二

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

function isObject(obj){
  return typeof obj === 'object' && obj !== null
}

function isEqual(obj1, obj2) {
  if(!isObject(obj1) || !isObject(obj2)){
    // 值类型
    return obj1 === obj2
  }
  if(obj1 === obj2){
    return true
  }
  // 两个都是对象或者数组,而且不相等

  // 1. 先判断keys个数
  const obj1Keys = Object.keys(obj1)
  const obj2Keys = Object.keys(obj2)
  if (obj1Keys.length !== obj2Keys.length) {
    return false
  }
  // 2. 以obj1为基准,和obj2 一次递归比较
  for (let key in obj1) {
    // 递归比较
    const res = isEqual(obj1[key], obj2[key])
    if (!res) {
      return false
    }
  }
  return true
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

2. split()和join()的区别

​ 答:split是字符串的方法,用来分割字符串为数组,'1-2-3'.split('-') // [1, 2, 3]

​ join是数组方法,用来拼接数组为字符串,[1, 2, 3].join('-') // '1-2-3'

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

  • pop : 从 数组末尾弹出一个元素,无参,返回弹出的元素,原位操作

  • push : 从数组末尾加入元素,参数是加入的元素,按顺序加入,返回数组长度,原位操作

  • shift : 从数组的开头弹出一个元素,无参,返回弹出的元素,原位操作

  • unshift : 从数组的开头加入元素,参数是加入的元素,按顺序一起加入,返回数组的长度,原位操作

    const arr = [10, 20, 30, 40]
    // pop
    // const popRes = arr.pop()
    // console.log(arr, popRes) // [10, 20, 30] 40
    
    // push
    // const pushRes = arr.push(50, 60)
    // console.log(arr, pushRes) // [ 10, 20, 30, 40, 50, 60 ] 6
    
    // unshift
    // const unshiftRes = arr.unshift(-10, -20)
    // console.log(arr, unshiftRes) // [ -10, -20, 10, 20, 30, 40 ] 6
    
    // shift
    // const shiftRes = arr.shift()
    // console.log(arr, shiftRes) // [ 20, 30, 40 ] 10
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

扩展:数组的API,有哪些纯函数?

​ 纯函数:1. 不改变源数组(没有副作用); 2. 返回一个数组

  • concat

  • map

  • filter

  • slice

    // concat
    // const arr1 = arr.concat([50, 60, 70])
    // console.log(arr, arr1) // [ 10, 20, 30, 40 ] [10, 20, 30, 40, 50, 60, 70]
    
    // map
    // const arr2 = arr.map(val => val * 10)
    // console.log(arr, arr2) // [ 10, 20, 30, 40 ] [ 100, 200, 300, 400 ]
    
    // filter
    // const arr3 = arr.filter(val => val > 25)
    // console.log(arr, arr3) // [ 10, 20, 30, 40 ] [ 30, 40 ]
    
    // slice
    // const arr4 = arr.slice()
    // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 10, 20, 30, 40 ]
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

非纯函数: push pop shift unshift forEach some every

题目三

1. 数组slice和splice的区别

  • 功能区别(slice-切片, splice-剪切)

  • 参数和返回值

  • 是否纯函数

    // slice 纯函数
    // const arr4 = arr.slice()
    // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 10, 20, 30, 40 ]
    // const arr4 = arr.slice(2, 4) // start, end
    // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 30, 40 ]
    // const arr4 = arr.slice(1) // start
    // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 20, 30, 40 ]
    // const arr4 = arr.slice(-2) // lastest start
    // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 30, 40 ]
    // const arr4 = arr.slice(-3, -2) // lastest start, lastest end
    // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 20 ]
    
    // splice 非纯函数
    // const spliceRes = arr.splice(1, 2, 'a', 'b', 'c') // start, length, 
    // console.log(arr, spliceRes) // [ 10, 'a', 'b', 'c', 40 ] [ 20, 30 ]
    // const spliceRes = arr.splice(1, 2)
    // console.log(arr, spliceRes) // [ 10, 40 ] [ 20, 30 ]
    // const spliceRes = arr.splice(1, 0, 'a', 'b', 'c') // 只增加不剪切
    // console.log(arr, spliceRes) // [ 10, 'a', 'b', 'c', 20, 30,  40 ] []
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

  • map的参数和返回值:参数:数组的每个元素,对应元素下标。返回值:新数组

  • parseInt参数和返回值:参数:待转换的该进制数字,进制。返回值:转换过的十进制数

    const res = [10, 10, 10, 10].map(parseInt)
    // 拆解,相当于:
    // const res = [10, 10, 10, 10].map((item, index) => {
    //   return parseInt(item, index)
    // })
    // parseInt(num, base = 10)  其中base = 0时,相当于base = 10,也就是相当于10进制转换
    console.log(res) // [ 10, NaN, 2, 3 ]
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

3. ajax请求的get和post的区别

  • get一般用于查询操作,post一般用于用户提交操作
  • get参数拼接在url上,post放在请求体内(数据体积可更大)
  • 安全性:post易于防止CSRF

题目四

1. 函数call和apply的区别

​ 答:call的参数是直接传入,而apply的参数是放到数组中

​ fn.call(this, p1, p2, p3)

​ fn.apply(this, arguments)

2. 事件代理(委托)是什么

​ 答:在父元素上定义一个事件,监听子元素触发时的冒泡。 如果子元素想要阻止冒泡,则使用:e.stopPropagation()

3. 闭包是什么,有什么特性,有什么负面影响

  • 回顾作用域和自由变量
  • 回顾闭包应用场景:作为参数被传入,作为返回值被返回
  • 回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
  • 影响:变量会常驻内存,得不到释放(但不一定会造成内存泄漏,内存泄漏一般属于Bug,而闭包不是Bug,而是故意为之)。闭包不要乱放。

题目五

1. 如何阻止事件冒泡和默认行为

  • event.stoppropagation()
  • event.preventDefault()

2. 查找、添加、删除、移动DOM节点的方法

  • 查找节点:document.getElementById(“id”)、document.getElementsByTagName(“h1”)、document.getElementsByClassName(“container”)、document.querySelector("#div")、document.querySelectorAll("#div")
  • 添加节点:document.createElement(“span”)
  • 插入、移动节点:ele.appendChild§
  • 获取父元素:ele.parentNode
  • 获取子元素:ele.childNodes(含有文本节点)、ele.children(不含文本节点)
  • 删除子元素:ele.removeChild

3. 如何减少DOM操作

  • 缓存DOM查询结果

  • 多次DOM操作,合并到一次插入

    const list = document.getElementById('list')
    // 创建一个文档片段
    const fragment = document.createDocumentFragment()
    for(let i = 0; i < 10; i++){
      const li = document.createElement('li')
      li.innerHTML = i
      // 插入到文档片段,文档片段是在内存中
      fragment.appendChild(li)
    }
    // 将文档片段一次性插入
    list.appendChild(fragment)
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

题目六

1. 解释jsonp的原理,为何它不是真正的ajax?

  • 浏览器的同源策略(服务端没有同源策略)和跨域

  • 哪些HTML标签能绕过跨域? script、link、img

  • ajax是通过XMLHTTPRequest实现的,jsonp是通过script标签实现的

  • 实现跨域必须得到服务器端的允许和配合

    // jsonp  
    	<script>
        window.callback = function (data) {
          console.log(data)
        }
      </script>
    
      <script src="http://localhost:8002/JSONP.js?username=xxx&callback=abc"></script>
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

2. document load和ready的区别

  • load是页面的资源全部加载完才执行

  • ready是DOM渲染完就执行,此时图片、视频还可能没有加载完

    window.addEventListener('load', function () {
      // 页面的全部资源加载完才执行,包括图片、视频等
    })
    document.addEventListener('DOMContentLoaded', function () {
      // DOM 渲染完即可执行,此时图片、视频还可能没有加载完
    })
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

3. == 和 === 的不同

  • == 会尝试类型转换
  • === 严格相等
  • 哪些场景采用== :判断对象属性是否为null:a.xxx == null。 其他场合一律用===

题目七

1. 函数声明和函数表达式的区别

  • 函数声明:function fn () {…}
  • 函数表达式: const fn = function () {…}
  • 函数声明会在代码执行前预加载,而函数表达式不会

2. new Object()和Object.create()的区别

  • {}等同于new Object(),原型Object.prototype

  • Object .create(null)没有原型

  • Object.create({…})可指定原型

    const obj1 = {a: 1}
    const obj2 = new Object(obj1) // obj1 === obj2
    
    const obj1 = {a: 1}
    const obj2 = new Object({a: 1}) // obj1 !== obj2, isEqual(obj1, obj2) === true
    
    const obj1 = {a: 1}
    const obj2 = Object.create(obj1) // obj2.__proto__ === obj1
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

3. this的场景题

  • this的取值在执行的时候再知道
const User = {
  count: 1,
  getCount: function () {
    return this.count
  }
}
console.log(User.getCount()) // 1

const func = User.getCount
console.log(func()) // undefined

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

题目八

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

let i
for(i = 1; i <= 3; i++) {
setTimeout(function () {
  console.log(i)
}, 0)
}
// 4 4 4

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

  • const reg = /1\w{5, 29}$/

    // 小写英文字母
    /^[a-z]+$/
    
    // 英文字母
    /^[a-zA-Z]+$/
      
    // 日期格式 2019-12-1
    /^\d{4}-\d{1,2}-\d{1,2}$/
    
    // 用户名
    /^[a-zA-Z]\w{5,17}$/
    
    // 简单的IP地址匹配
    /\d+\.\d+\.\d+\.\d+/
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

3. 关于作用域和自由变量的场景题 -2

let a = 100
function test() {
  console.log(a)
  a = 10
  console.log(a)
}
test()
console.log(a)
// 100 10 10

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

题目九

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

String.prototype.trim = function () {
  return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
// 原型、this、正则表达式

  
 
  • 1
  • 2
  • 3
  • 4

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

function max() {
  const arr = Array.prototype.slice.call(arguments)
  let res = arr[0]
  arr.forEach(item => {
    if(item > res)res = item
  })
  return res
}
console.log(max(3, -1, 20, 4, 34))
console.log(Math.max(3, -1, 20, 4, 34))

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3. 如何用JS实现继承

  • class继承
  • prototype继承

题目十

1. 如何捕获JS程序中的异常

try {
  // TODO
} catch(e) {
  console.log(e) // 手动捕获异常
} finally {
  // TODO
}

// 自动捕获
window.onerror = function (message, source, lineNum, colNum, error) {
  // 第一,对跨域的js, 如CDN的,不会有详细的报错信息
  // 第二,对于压缩的js,还要配合sourceMap,反查到未压缩代码的行、列
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. 什么是JSON

  • json是一种数据格式,本质是一段字符串
  • json格式和JS对象结构一致,对JS语言更友好
  • Window.JSON是一个全局对象:JSON.stringify将json转化成字符串,JSON.parse将字符串转化成json

3. 获取当前页面url参数

  • 传统方式,查找location.search

    // 传统方式
    function query(name) {
      const search = location.search.substr(1)
      // search: 'a=10&b=20&c=30'
      const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
      const res = search.match(reg)
      console.log(res)
      // 数组第一个是完全匹配,第二个是匹配的第一个括号里面的内容,第3个是匹配的第2个括号里面的内容,第4个是匹配的第3个括号里面的内容,
      // ["&b=20&", "&", "20", "&"]
      if(res === null) {
        return null
      }
      return res[2]
    }
    console.log(query('b'))
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 新的API,URLSearchParams

    // URLSearchParams
    function query(name) {
      const search = location.search
      const p = new URLSearchParams(search)
      return p.get(name)
    }
    console.log(query('b'))
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

题目11

1. 将url参数解析为JS对象

// 传统方式,分析search
function queryToObj() {
  const res = {}
  const search = location.search.substr(1) // 去掉前面的?
  search.split('&').map(paramStr => {
    const arr = paramStr.split('=')
    res[arr[0]] = arr[1]
  })
  return res
}
console.log(queryToObj()) // {a: "10", b: "20", c: "30"}

// 使用URLSearchParams
function queryToObj2(){
  const res = {}
  const pList = new URLSearchParams(location.search)
  pList.forEach((val, key) => {
    res[key] = val
  })
  return res
}
console.log(queryToObj2()) // {a: "10", b: "20", c: "30"}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2. 手写数组flatern,考虑多层级

function flat(arr){
  // 验证arr中有没有深层数组
  const isDeep = arr.some(item => item instanceof Array)
  if(!isDeep)return arr
  const res = Array.prototype.concat.apply([], arr)
  return flat(res) // 递归
}

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


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. 数组去重

// 传统方式 比较慢 可兼容
function unique(arr) {
  const res = []
  arr.forEach(item => {
    if(res.indexOf(item) < 0) {
      res.push(item)
    }
  })
  return res
}

console.log(unique([30, 20, 40, 50, 20, 30]))

// set方式 (无序结构,不能重复) 快 新的API,可能有兼容性问题
function unique2(arr){
  const set = new Set(arr)
  return [...set]
}
console.log(unique2([30, 20, 40, 50, 20, 30]))

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

题目12

1. 手写深拷贝

/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj) {
  if (typeof obj !== 'object' || obj === null) return obj
  let newObj
  if (obj instanceof Array) {
    newObj = []
  } else {
    newObj = {}
  }
  for (let key in obj) {
    if (obj.hasOwnProperty(key))
      newObj[key] = deepClone(obj[key])
  }
  return newObj
}

// Object.assign是浅拷贝,只拷贝第一层级,不要踩坑

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2. 介绍一下RAF: window.requestAnimationFrame

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

  • setTimeout要手动控制频率,而RAF浏览器会自动控制

  • 后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行

    // 3s 把宽度从100px变为640px,即增加540px
    // 60帧/s, 3s 180帧,每次变化3px
    let curWidth = 100
    const maxWidth = 1000
    const $div1 = $("#div1")
    
    // setTimeout
    // function animate(){
    //   curWidth += 3
    //   $div1.css('width', curWidth)
    //   if(curWidth < maxWidth) {
    //     setTimeout(animate, 16.7); // 得自己控制时间
    //   }
    // }
    // animate()
    
    function animate(){
      curWidth += 3
      $div1.css('width', curWidth)
      if(curWidth < maxWidth) {
        window.requestAnimationFrame(animate)
      }
    }
    animate()
    
        
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

  • 原则:多使用内存、缓存、减少计算、减少网络请求
  • 方向:加载页面,页面渲染,页面操作流畅度

  1. a-zA-Z ↩︎

文章来源: blog.csdn.net,作者:爱玲姐姐,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jal517486222/article/details/105837236

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。