js面试题整理

132 阅读6分钟

为之后的面试准备的复习资料,先整理一些基本的,后期会慢慢增加.

var,let和const

  • var: var是挂载在全局作用域上的,存在变量提升.可以重复命名,可以在定义变量前使用.
  • let: let在块级作用域内起作用,存在暂时性死区. 不能被重复命名, 必须在定义变量后使用.
  • const: 定义常量, 定义时必须赋值并且定义后不能被修改

数据类型

  • js中只有七种数据类型
  • 基本数据类型: undefined, null, string, number, boolear, symbol
  • 复杂数据类型: object
  • 基本数据类型存在栈中,复杂数据类型存在于堆中在栈中有指向堆内存的地址
  • 函数 和 数组等都是object中的子类型
  • 函数length的长度是传入参数的个数

深浅拷贝

  • 浅拷贝: 只复制对象的指针,不复制对象本身,新老对象共享一块内存
  • 深拷贝: 复制并拷贝一个新的对象, 新对象指向一个新的指针,不共享内存.

浅拷贝方法

  • 展开运算符:
let Array1 = [1,2,3,4,5]
let Array2 = [...Array1]
console.log(Array2); //=> [ 1, 2, 3, 4, 5 ]
  • Object.assign()
let obj1 = {
    a: 1,
    b: 2,
    c: 3,
}
let obj2 = Object.assign(obj1)
console.log(obj2); //=> { a: 1, b: 2, c: 3 }

深拷贝方法

  • json.parse(json.stringfy(obj))
    • 缺点: 对于不安全的json值可能会出问题,不安全的json值包括undefined,function,symbol和包含引用(对象间的相互引用)
    • 遇到undefined,function和symbol会自动忽略如果他们在数组中则为null
    • 如果对象中包括循环引用则会报错
     console.log(JSON.stringify(undefined)); // undefined
     console.log(JSON.stringify(function() {})); // undefined
     console.log(JSON.stringify([1,undefined, 2,3,function() {} ])); // [1,null,2,3,null]
    
  • 利用递归手写深拷贝方法
let newObj = {
    a: 1,
    b: 'f3',
    c: [1,2,3,4],
    d: {a:1,b:1},
    e: undefined,
    f: () => {
       alert('ff')
    }
}
// 利用递归实现深拷贝
let deep = (obj) => {
    // 判断是否是复杂数据类型不是的话返回他自己
    if(!(obj instanceof Array) || !(obj instanceof Object)) { return obj }
    // 判断数据类型是否是数组或对象,进行数据声明
    // 新建一个新的内存地址,根据传递的是对象还是数组判断给予内存地址对应的是对象还是数组
    let newStrace = obj instanceof Array ? []:{}
    // console.log(newStrace);
    for(let key in obj) {
        // 忽略从原型继承的属性,不进行拷贝
        if(obj.hasOwnProperty(key)) {
            const value = obj[key] // 获取对应的值
            // 对数据类型进行判断
            if(obj[key] instanceof Array || obj[key] instanceof Object) {
                // 如果是复杂数据类型则进行递归复制新的内存地址
                newStrace[key] = deep(value)
            } else {
                // 否则直接赋值
                newStrace[key] = value
            }
        }
    }
    return newStrace
}
let newObj1 = deep(newObj)
let newObj2 = deep(newObj)
newObj2.a = 'ggg'
newObj2.d.a = 33
console.log(newObj1);
console.log(newObj2);
  • 利用深拷贝对两个对象进行合并
// 参数: 需要合并的两个对象
let testFn = (obj1,obj2) => {
    // 分别调用深拷贝方法,各准备出一份对象出来
    var a = deep(obj1)
    var b = deep(obj2)
        console.log(a);
    // 循环第二个对象获取其属性,如果第一个对象没有该属性则会被添加该属性
    for(var key in b) {
        a[key] = b[key]
    }
    // 处理后的第一个对象就是合并后的对象数据
    // 如果属性名重复,a对象中的属性值会被b中重复的属性值覆盖
    console.log(a);
}

闭包

  • 闭包本质上是内部函数和外部函数沟通的桥梁,由于函数作用域的规则内部函数可以访问外部函数的变量.其作用在于可以访问到函数内部的变量,并将这些变量值始终保存在内存中. 但是会导致内存泄露
  • 形成闭包的原因: 将函数当做参数传递或将函数作为返回值返回
  • 解决闭包导致内存泄露的方法: 在退出函数前将不需要的值赋值为null

es新特性

  • let和const
  • 箭头函数
    • 箭头函数和普通函数区别: 没有this,需要向上层寻找,不能作为构造函数
  • promise
  • async/awit
  • new Set(): 常用于数组去重
  • find: 数组方法
  • includes ...

this指向

  • 谁调用指向谁
  • 箭头函数没有this,他的this指向向上寻找

数组

数组常用方法

  • push
    • 功能: 向数组末尾增加数据
    • 参数: 多个任意类型
    • 返回值: 新增数据后的数组长度
  • pop
    • 功能: 删除数组最后一位数据
    • 参数: 无
    • 返回值: 删除的数据
  • shift
    • 功能: 删除数组第一位数据
    • 参数: 无
    • 返回值: 删除的数据
  • unshift
    • 功能: 向数组开头增加数据
    • 参数: 多个任意类型
    • 返回值: 新增数据后的数组长度
  • splice
    • 功能: 实现数组的增删改
    • 参数: splice(n,m,x) //=> n: 开始删除的数组下标. m: 删除的个数. x: 新增的数据
    • 返回值: 将删除的部分作为一个新数组返回
    • 注意: 会修改原数组
  • slice
    • 功能: 进行数组的查询
    • 参数: slice(n,m), 从数组下标n开始查询,查询到下标m为止不包括下标m项
    • 返回值: 将查询到的数据以新数组返回
  • content
    • 功能: 实现数组的拼接
    • 参数: 需要拼接的数组,或需要拼接的任意类型数据
    • 返回值: 拼接后的数组
    let arr1 = [1,2,3,4]
    let arr2 = [5,6,7,8]
    let arr = arr1.concat(arr2, 3,4,5)
    console.log(arr); //=> [1, 2, 3, 4, 5,6, 7, 8, 3, 4,5]
    
  • join
    • 功能: 将数组转换成字符串,并指定分隔符
    • 参数: 指定的分隔符
    • 返回值: 转换好的字符串
    let arr = [1,3,4,,65,7]
    let str = arr.join('?')    
    console.log(str); //=> 1?3?4??65?7
    
  • sort
    • 功能: 实现数组的排序
    • 参数: 一个回调函数
    • 返回值: 排序后的数组(会改变原数组)
    let arr = [2,7,342,4,6,7,1,7]
    arr.sort((a,b) => {
    return a - b // 由小到大排列,如果 b-a 则由大到小排列
    })
    console.log(arr);
    

数组的高级方法

foreach

  • 功能: foreach方法用来循环数组,不会返回新数组
  • 参数: arr.foreach(数组成员,下标,数组本身)
  • 返回值: undefined

map

  • 功能: 对原数组数据进行处理,并返回新数组
  • 参数: map.map(数组成员,下标,数组本身)
  • 返回值: 处理后的数组

filter

  • 功能: 对原数组数据进行过滤,筛选出符合标准的数据
  • 参数: arr.filter(数组成员,下标,数组本身)
  • 返回值: 过滤后符合标准的数据形成的数组

find

  • 功能: 用于在数组中查找数据,当查找到数据第一次出现的位置后不在循环
  • 参数: arr.find(数组成员,下标,数组本身)
  • 返回值: 查找到的数据,如果没找到返回undefined
let arr = [1,2,3,4]
let a = arr.find((item) =>{ return item === 2}) 
console.log(a) //=> 2

数组去重

  • new Set
Array.from(new Set(arr))
let a = new Set(arr)
let b = [...a]
  • 双重for循环
for (let i = 0; i < arr.length; i++) {
    // item: 每一次循环的当前项, 获取外层循环当前项
    let item = arr[i]
    for (let j = i + 1; j < arr.length; j++) {
        // 后面要拿出比较的项
        let itemJ = arr[j]
        if (item === itemJ) {
            // 如果相等,删除该下标对应的数组
            arr.splice(j, 1)
            // splice后出现数组塌陷,j后面的数组提前了,下
            // 次比较的还是j的索引,所以为要先--,防止数组塌陷
            j--
        }
    }
}
console.log(arr);
  • 利用一个空数组做中转,foreach循环处理数据
 let arr = [1,2,3,2,1,2,3,2,1]
 let newArr = []
 // for循环可以简化成
 // includes 判断一个数组内是否存在一个指定值,存在返回true否则返回false
  arr.forEach(item => {
      // 检测到重复数据跳出不做操作
      if (newArr.includes(item)) continue;
      // 否则证明没有重复数据,向中转数组push新数据
      newArr.push(item)
  })
 console.log(newArr);

字符串

字符串常用方法

  • split
    • 功能: 将字符串分割成数组
    • 参数: str.split(/\n/), 传入的参数为分割规则
    • 返回值: 将分割后的数组返回
  • replace
    • 功能: 实现新老字符的替换,常搭配正则使用,在不执行正则时只替换一次
    • 参数: str.splice('需要替换的字符(正则)','替换后的字符')
    • 返回值: 替换后的字符串
      let str = 'ni@hao@li@huan@ying'
      str = str.replace('@',",")
      str1 = str.replace(/@/g,",")
      console.log(str1); //=> ni,hao@li@huan@y  在不执行正则表达式时只替换一次字符.