前端面试-js

214 阅读10分钟

数据类型

基本类型:UndefinedNullBooleanNumberStringSymbol (ES6新增)
引用类型:ObjectArrayFunction

遍历操作(数组、对象)

  • for - 十分常见,主要是用在数组中和一些dom操作中
  • while - 先判断再执行
  • do while - 先执行再判断
  • for in - 一般用于遍历对象,也可以遍历数组
  • for of - ES6 新增,遍历所有数据结构的统一方法。不仅可以用来遍历数组,还可以遍历字符串,以及ES6中的Map,Set。相关的方法:keys() 键名集合、 values() 键值集合、 entries() 键值对结合

跳出循环的方式

  • break - 跳出当前循环
  • continue - 跳出本次循环 继续下次循环

字符串常用方法

查找charAt 指定位置的字符、indexOf 指定字符的位置、lastIndexOf 从后往前找
截取substr 按个数截取、substring 按位置截取
字大小写转换toUpperCase 转换成大写、toLowerCase 转换成小写
分割split 将字符串分割成数组
拼接concat 将多个字符串拼接成一个字符串
替换replace 把第一个参数拿到字符串中去搜索,匹配上了就用第二个参数去替换
搜索search 匹配子字符串或正则是否在字符串中, 匹配上就返回具体的位置, 否则返回-1
匹配match 匹配上就返回内容,匹配不上就返回null

数组常用方法

查找indexOf 指定元素的位置、lastIndexOf 从后往前找
截取slice 指定位置的元素(按位置截取)
增加unshift 前增、push 后增
删除shift 前删、pop 后删
格式化map 将结果作为新的数组返回,不改变原数组
反转reverse 颠倒数组中元素的顺序
遍历forEach 没有返回值,直接改变原数组
过滤filter 将符合条件的元素作为一个新数组返回、every 假如所有元素都符合判断条件,则返回true,否则为false、some 假如存在元素符合判断条件,则返回true,假如都不符合则为false
排序sort 直接改变原数组,参数必须是规定元素排序顺序的函数
拼接concat 将两个或多个数组拼接成一个数组,不会改变现有数组,只会返回一个拼接后的新数组
转化为字符串toString 元素之间通过逗号分隔、join 元素通过指定的分隔符分隔,默认使用逗号分隔 reduce 数组中所有元素调用传参函数,返回值为最后的结果,传入的参数必须是函数
splice 数组的万能方法,可以实现增删改,不仅会改变原数组,而且将删除的元素作为一个新数组返回

Object 常用方法

  • .keys:返回一个由对象的键(key)组成的数组
  • .create:返回一个新对象,带着指定的原型对象和属性。
  • .assign:合并对象,浅拷贝

浅拷贝-深拷贝

一、浅拷贝

将源对象的属性拷贝一份,如果属性时基本类型值,直接拷贝基本类型值,如果属性是引用类型,则拷贝的是 该引用类型在堆中的地址 的指向

  1. 展开运算符 ...(es6新增)
  2. Object.assign()
  3. concatslice(0)(数组)

二、深拷贝

比浅拷贝能够更深层级的拷贝,它能够将拷贝过程中遇到的引用类型都新开辟一块地址拷贝对应的数据,这样就能避免子对象共享同一份内存的问题

  1. JSON.parse(JSON.stringify())
    JSON.stringify将对象先转成字符串,再通过JSON.parse将字符串转成对象
    缺陷
    a、不能解决循环引用的问题
    b、无法拷贝特殊对象,比如:SetMapDateRegExp
    c、忽略掉值为 undefined 以及函数表达式

  2. 深拷贝方法

    • 自定义:根据自己需求封装,使用遍历、递归、判断,
    • 第三方:使用第三方js工具库,比如 Lodash

this 指向

this 指向最终调用它的那个对象。this 的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定。

  • 全局:严格模式下指向 undefined,非严格模式下且在浏览器环境下指向 window
  • 上下文对象this 指向最后调用它的对象
  • 改变this指向callapplybind
  • 构造函数this 指向实例

new 操作符调用构造函数的整个过程

  • 创建一个新的对象
  • 将构造函数的 this 指向这个新对象
  • 为这个对象添加属性、方法等
  • 最终返回新对象

改变 this 指向 call、apply、bind

第一个参数都是this的指向,且当第一个参数为nullundefined的时候,默认指向window(在浏览器中)
call:后面传入的是一个参数列表(逗号隔开)
apply:第二个参数是函数接受的参数(以数组的形式传入)
bind:后面传入的也是一个参数列表(逗号隔开,可以分多次传入)。不会立即执行,返回一个永久改变this指向的函数

闭包

闭包就是引用了其他函数作用域中变量的函数
优点:变量、方法私有化(避免全局变量污染)
缺点:闭包会增大内存的使用量,不能滥用,否则会影响网页的性能。在IE9中可能会导致内存泄露,但这个导致内存泄露是IE早期的BUG。闭包本身并不会导致内存泄露。

节流、防抖

一、防抖

事件回调函数在一段时间后才执行,如果在这段时间内再次调用,则重新计时,当预定的时间内没有再次调用,则执行事件回调函数。

/**
 * @description: 触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间
 * @param func 执行函数
 * @param wait 延迟时间
 * @param immediate 是否立即执行
 */
function debounce(func, wait = 2000, immediate = true) {
  let timeout, context, args
  return function() {
    context = this
    args = arguments
    if (timeout) clearTimeout(timeout)
    if (immediate) {
      const callNow = !timeout
      timeout = setTimeout(function() {
        timeout = null
      }, wait)
      if (callNow) func.apply(context, args)
    } else {
      timeout = setTimeout(function() {
        func.apply(context, args)
      }, wait)
    }
  }
}

二、节流

事件回调函数在一段时间内连续触发,只会执行一次,到下次开始计时的时候,就会在下一个时间段内再执行一次。

/**
 * @description: 在n秒事件连续触发,函数只能执行一次
 * @param func 执行函数
 * @param wait 延迟时间
 * @param immediate 是否立即执行
 */
function throttle(func, wait = 2000, immediate = true) {
  let timeout, context, args
  return function() {
    context = this
    args = arguments
    if (!timeout) {
      if (immediate) {
        func.apply(context, args)
        timeout = setTimeout(function() {
          timeout = null
        }, wait)
      } else {
        timeout = setTimeout(function() {
          timeout = null
          func.apply(context, args)
        }, wait)
      }
    }
  }
}

作用域、作用域链

一、作用域:

代码中的某些特定部分中变量,函数和对象的可访问性,决定了代码区块中变量和其他资源的可见性

  1. 全局作用域:最外层函数和在最外层函数外面定义的变量、末定义直接赋值的变量、window对象的属性
  2. 函数作用域:函数内部的变量
  3. 块级作用域(es6新增):letconst,所声明的变量在指定块的作用域外无法被访问。在一个函数内部、在一个代码块(由一对花括号包裹)内部

二、作用域链

就是作用域的嵌套行为。在当前作用域中无法查找到某个变量时,就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(全局作用域)为止。这样一条有序的列表,称为作用域链。

原型、原型链

一、原型

JS声明构造函数(用来实例化对象的函数)时,会在内存中创建一个对应的对象,这个对象就是原函数的原型。
构造函数默认有一个prototype属性,prototype的值指向函数的原型。同时原型中也有一个constructor属性,constructor的值指向原函数。
通过构造函数实例化出来的对象,并不具有prototype属性,其默认有一个__proto__属性,该属性指向构造函数的原型对象。

二、原型链

当在实例化的对象中访问一个属性时,首先会在该对象内部寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向上级原型中寻找,直至找到或Object.prototype为止,这种链状过程即为原型链

声明变量 var、let、const 的区别

var:函数作用域、有变量提升、可重复声明、可重复赋值
let:块级作用域、无变量提升、不可重复声明、可重复赋值
const(常量):块级作用域、无变量提升、不可重复声明、不可重复赋值、必需初始化赋值

异步编程 Promise,async、await

异步编程,处理异步操作,确认异步操作完成再执行后面的语句

Promise

三种状态pending 等待,resolved 成功,rejected 失败
链式调用:链式写法本质上是一直往下传递一个新的Promise,也就是说then在下一步接收的是上一步返回的Promise

  • .then 接受两个回调函数,第一个为成功回调,第二个(可选)为失败回调
  • .catch 接受一个失败回调函数
  • Promise.reject 返回一个状态为失败的 Promise 对象
  • Promise.resolve(result) 返回一个状态由给定 result 决定(失败或者成功)的Promise对象
  • Promise.all 将多个 Promise 实例,包装成一个新的 Promise 实例,可以来统一控制输出结果

async、await

async 函数的返回值是一个 promise 对象
await 后是一个 promise 对象,它会"阻塞"后面的代码,直到这个 promise 对象有返回结果,如果是resolve状态,值就是resolve参数,如果是reject状态,会将错误抛出,抛出的错误可以使用try catch捕获
await 后如果不是一个promise对象,那该表达式就是要等待的东西,还是相当于在 then 执行

箭头函数 => 需要注意的地方

  • 箭头函数不能用于构造函数,意味这不能用 new 关键字去调用
  • 箭头函数内部没有 arguments 对象
  • 箭头函数没有自己的 this ,它内部的 this ,并不是调用的时候指向对象,而是定义的时候指向对象。this 的指向是可变的,但是在箭头函数中,它是固定的

Symbol 类型

ES6 中新增的一种数据类型,属于基本数据类型。用来表示独一无二的值。Symbol 里面传入的字符串没有其他意义,只是用来描述的,进行区分。
注意

  • 不能使用 new 关键字调用
  • 做类型转换的时候不能转换成数字,不能做运算
  • 如果把 Symbol 用于对象的属性和方法,一定要用一个变量来存

Set、Map 数据结构

Set

类似于数组,是不包含重复值的列表
属性size 长度
apiadd() 增加一个元素、 delete() 删除一个元素、 has() 查看是否存在某个元素、clear() 清空
应用场景:数组去重

Map

是键与相对应的值的集合。“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
属性size 长度
apiset() 设置键值对、 get() 根据key获取value、 delete() 删除key、 has() 查看是否有key、 clear() 清空
应用场景:储存数据

ES5、ES6的继承

名称描述继承属性继承方法
ES5ES5 通过构造函数和原型prototype,来实现继承使用 call,在子类构造函数中调用父类构造函数。function Child(params) { Parent.call(this,params) }创建父类原型的一个副本,把副本赋值给子类原型。 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child
ES6ES6 提供了类 class 语法糖,类 class 的继承是通过 extends 来实现。class Child extends Parent { }在子类的 constructor 函数中执行 super 函数。constructor(params) { super(params) }使用了 extends 后,子类已经实现了对父类方法的复用