JS面试题整理

188 阅读8分钟

JavaScript 的基本类型有哪些?引用类型有哪些?null 和undefined 的区别?

基本数据类型:Number、String、Boolean、null、undefined,Symbol (ES6),BigInt (ES10)

引用数据类型:Function、Object、Array

如何判断JavaScript的数据类型?

1、typeof

typeof 对于简单数据类型(undefined、null、boolean、number 、string )可以判断的出来,但是对于复杂数据类型就不起作用(Array),因为对于复杂数据类型的判断结果也是object,跟null的判断返回结果相同,因此这个时候判断不出来是复杂数据类型还是null简单数据类型,返回值为该值的数据类型(undefined、null(object)、boolean、number 、string )


var arr = new Array();
console.log(typeof 1);//number
console.log(typeof 'xiaoming');//string
console.log(typeof true);//boolean
console.log(typeof undefined);//undefined
console.log(typeof null);//object
console.log(typeof arr);//object

2、instanceof

instanceof可以判断对象是不是另一个对象实例,主要作用是来判断复杂数据类型的,返回值为布尔值;

    console.log([] instanceof Array);//true
    console.log({} instanceof Object);//true
    console.log(function(){} instanceof Function);//true

3、**Object.prototype.toString.call()

Object.prototype.toString.call()能够准确的判断所有的数据类型;

var a = Object.prototype.toString;
console.log(a.call("xiaoming"));//String
console.log(a.call(1));//Number
console.log(a.call(true));//Boolean
console.log(a.call(null));//Null
console.log(a.call(undefined));//Undefined
console.log(a.call([]));//Array
console.log(a.call(function () { }));//Function
console.log(a.call({}));//Object

4、constructor

constructor也能够判断多种数据类型,这里跟instanceof一样,null和undefined除外


console.log(("xiaoming").constructor === String);//true
console.log((1).constructor === Number);//true
console.log((true).constructor === Boolean);//true
//console.log((null).constructor === Null);
//console.log((undefined).constructor === Undefined);
console.log(([]).constructor === Array);//true
console.log((function () { }).constructor === Function);//true
console.log(({}).constructor === Object);//true

5、Array.isArray

可以判断一个数据类型是否为数组

let arr = [1,2,3]
console.log(Array.isArray(arr)) // true
console.log(Array.isArray({}) // false

3、简述创建函数的几种方式?

函数声明

function fn(){}

函数表达式

let fn = function(){}

构造函数(函数对象方式)

let fn = new function(num:1)
fn.num

4、Javascript 创建对象的几种方式?

1、 字面量创建

var per1 = {
name: '张三',
age: '18', 
eat: function () { console.log('吃东西') } 
}

2、new Object方式创建

var per2 = new Object() 
per2.name = '李四'; 
per2.age = '18';
per2.read = function () { console.log('读书') }

3、通过工厂模式创建

function person(name,age) {
        var obj = new Object();
        obj.name = name;
        obj.age  = age;
        obj.play = function () {
            console.log('玩游戏')
        }
        return obj
    }
    var per3 = person('王五','20')

4、通过构造函数创建,实列对象也指向原型对象,所以可以找到对象原型上的属性

    function animal(name, age) {
        this.name = name;
        this.age = age;
        this.food = function () {
            console.log('骨头')
        }
    }
    var ani1 = new animal('泰迪',3) // ani1称为实例化对象
    console.log(ani1.__proto__ === animal.prototype) // true 两者都是指向构造函数的原型
    console.log(ani1.__proto__.constructor === animal.prototype.constructor) //  true 两者都指向的是构造函数
    console.log(ani1 instanceof animal) // true 以此来判断对象是不是这种数据类型

javascript中宿主对象和原生对象的区别是什么

  • 所有的BOM和DOM对象都是宿主对象,js原生的基本数据类型,引用数据类型等都属于原生对象。

Array数组常用方法

length属性动态获取数组长度

join() 将一个数组转成字符串。返回一个字符串。

reverse() 将数组中各元素颠倒顺序

delete 运算符只能删除数组元素的值,而所占空间还在,总长度没变(arr.length)。

shift() 删除数组中第一个元素,返回删除的那个值,并将长度减1。

pop() 删除数组中最后一个元素,返回删除的那个值,并将长度减1。

unpop() 删除数组中第一个元素

unshift() 往数组前面添加一个或多个数组元素,长度要改变。arrObj.unshift(“a”,“b,“c”)

concat( ) 连接数组

slice( ) 返回数组的一部分

sort( ) 对数组元素进行排序

splice( ) 插入、删除或替换数组的元素

toLocaleString( ) 把数组转换成局部字符串

toString( ) 将数组转换成一个字符串

遍历数组常用方法

1. forEach()

  • 数组里的元素个数有几个,该方法里的回调就会执行几次
  •  第一个参数是数组里的当前元素,第二个参数为数组里当前元素的索引值,第三个参数则是它自己
  • 没有返回值,本质上等同于 for 循环,对每一项执行 function 函数。即map是返回一个新数组,原数组不变,forEach 是改变原数组。
  • 不支持 continue,用 return false 或 return true 代替。
  • 不支持 break,用 try catch/every/some 代替
 private forEach() {
      type itemType = {
        cityId: number,
        cityName: string
      }
      const arr = [
        { cityId: 195, cityName: '深圳'},
        { cityId: 196, cityName: '北京'},
        { cityId: 197, cityName: '上海'}
      ]
      arr.forEach((item: itemType, index: number, arr: any) => {
        console.log(`index:${index},item:${JSON.stringify(item)},arr:${JSON.stringify(arr)}`)
      })
    }

2.map()

  • map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
  • map() 方法按照原始数组元素顺序依次处理元素。
  • 使用比较广泛,但其性能还不如 forEach
  • 不会改变原始数组。
  let arr = [1, 2, 3, 4, 5, 6]
  let newArr = arr.map((item: any) => {
  return item * item
  })
  console.log(newArr)

3.some()

遍历数组,只要有一个以上的元素满足条件就返回 true,否则返回 false ,退出循环

  private some(id: number) {
      const arr = [
        { cityId: 195, cityName: '深圳'},
        { cityId: 196, cityName: '北京'},
        { cityId: 198, cityName: '上海'}
      ]
      let result = arr.some((item: any) => {
        return item.cityId === id
      })
      console.log(`传入:${id},结果:${result}`)
    }

4. every()

遍历数组,每一个元素都满足条件 则返回 true,否则返回 false

 private every() {
      const arr = [1,2,3,4,5]
      let result = arr.every((item: any) => {
        return item > 0
      })
      console.log(`结果:${result}`) // true
    }
        
// ====================================================
  
  private every() {
      const arr = [1,2,3,4,5]
      let result = arr.every((item: any) => {
        return item > 10
      })
      console.log(`结果:${result}`) // false
    }

5. filter()

会筛选后返回一个新的数组,如果不用箭头函数写的话需要写return

var arr = [ 
{ id: 1, text: 'aa', done: true }, 
{ id: 2, text: 'bb', done: false } ]
var arr2 = arr.filter(item => item.done)
console.log(arr2) // { id: 1, text: 'aa',done:true}

6. find()

遍历数组,返回符合条件的第一个元素,如果没有符合条件的元素则返回 undefined

  let arr = [1,2,2,3,3,3,3,4,4,5,6]
  let num = arr.find((item) => {
  return item === 3
  })
  console.log(num) // 3
  
  // ===========
  
  let arr = [1,2,2,3,3,3,3,4,4,5,6]
  let num = arr.findIndex((item) => {
  return item === 10
  })
  console.log(num) // -1
  

7. findIndex()

遍历数组,返回符合条件的第一个元素的索引,如果没有符合条件的元素则返回 -1

  let arr = [1,2,2,3,3,3,3,4,4,5,6]
  let num = arr.findIndex((item:any) => {
  return item === 2
  })
  console.log(num) // 1
  
 // ===================
 
   let arr = [1,2,2,3,3,3,3,4,4,5,6]
  let num = arr.findIndex((item:any) => {
  return item === 10
  })
  console.log(num) // -1

8. for…of…(ES6)

可以在遍历数组的时候将数组中对象的数据结构出来

  const arr = [
        { cityId: 195, cityName: '深圳'},
        { cityId: 196, cityName: '北京'},
        { cityId: 197, cityName: '上海'}
      ]
      for( {cityId}of arr) {
        console.log(cityId) // 195 196 197
      }

9.for

  const arr = [
        { cityId: 195, cityName: '深圳'},
        { cityId: 196, cityName: '北京'},
        { cityId: 197, cityName: '上海'}
      ]
      for(let i = 0; i < arr.length; i++) {
        console.log(arr[i])
      }

10. reduce

arr.reduce(function(prev,cur,index,arr){ ... }, init);

arr :原数组
prev :上一次调用回调时的返回值,或者初始值 init;
cur : 当前正在处理的数组元素;
index :当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;
init :初始值 其实常用的参数只有两个:prev 和 cur。

var arr = [1,2,3,4];

var sum = arr.reduce((prev,cur)=>{
   return prev + cur;
}) // 10
<!-- 设定初始值求和 -->
var sum = arr.reduce((prev,cur)=>{
  return prev + cur;
},10) // 20

方法对于100万的循环时间

20210414155054425.png for最快,但可读性比较差

forEach比较快,能够控制内容

for....of比较慢,香

for...in比较慢,不方便

遍历对象的常用方法

1.for…in…

  • for...in 语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)
  • for in得到对对象的key或数组,字符串的下标
    const arr = [
        { cityId: 195, cityName: '深圳'},
        { cityId: 196, cityName: '北京'},
        { cityId: 197, cityName: '上海'}
      ]
      const obj = { cityId: 195, cityName: '深圳'}
 
      for(const key in arr) {
        console.log(`数组key-${key}`)
      }
      for(const key in obj) {
        console.log(`对象key-${obj[key]}`)
      }

微信图片_20221001114608.png

字符串常用方法

Length 获取字符串的长度。如:var len = strObj.length

toLowerCase() 将字符串中的字母转成全小写。如:strObj.toLowerCase()

toUpperCase() 将字符串中的字母转成全大写。如:strObj.toUpperCase()

charAt(index) 返回指定下标位置的一个字符。如果没有找到,则返回空字符串

substr() 在原始字符串,返回一个子字符串

substring() 在原始字符串,返回一个子字符串

split() 将一个字符串转成数组

charCodeAt( ) 返回字符串中的第n 个字符的代码

concat( ) 连接字符串

fromCharCode( ) 从字符编码创建—个字符串

indexOf( ) 返回一个子字符串在原始字符串中的索引值(查找顺序从左往右查找)。如果没

有找到,则返回-1

lastIndexOf( ) 从后向前检索一个字符串

localeCompare( ) 用本地特定的顺序来比较两个字符串

match( ) 找到一个或多个正则表达式的匹配

replace( ) 替换一个与正则表达式匹配的子串

search( ) 检索与正则表达式相匹配的子串

slice( ) 抽取一个子串

toLocaleLowerCase( ) 把字符串转换小写

toLocaleUpperCase( ) 将字符串转换成大写

toLowerCase( ) 将字符串转换成小写

toString( ) 返回字符串

toUpperCase( ) 将字符串转换成大写

valueOf( )

深拷贝浅拷贝

  • 浅拷贝就是通过直接赋值的方法就可以完成,如果是复杂数据类型直接赋值的赋值的地址的指针,如果改变地址里面的内容两个复杂数据里都会改变,所以如果不希望出现这种问题就要用到深拷贝
  • 深拷贝的话可以使用JSON.parse( JSON.stringify() )序列化和反序列来实现,也可以通过递归的方式手写深拷贝,遍历要拷贝的数据,在里面做判断根据不同复杂数据,进行处理递归处理,如果没有就浅拷贝,也可以使用lodash函数库来完成数据的深拷贝

解决异步问题的方法

  • 可以使用promise函数,回调函数,发布订阅者,es6新增的解决异步编程的方法Generator,还可以使用await/async搭配promise函数使用解决异步编程问题

promise函数

  • Javascript执行环境是单线程的,也就是说JS环境中负责解释和执行的线程只有一个,一次只能完成一项任务,这个任务执行完后才能执行下一个,因此Javascript中存在大量的异步操作和回调函数。 Promise函数是异步编程的一个解决方案,相对传统的解决方案,他更合理和强大。 Promise的本意是“承诺”、“许诺”的意思。 承诺在未来会执行某个操作的函数,就是Promise函数。
  • Promise有三种状态,分别是:**Pending **(进行中), ** Resolved (已完成), Rejected ** (已失败)。Promise从Pending状态开始,如果成功就转到成功态,并执行resolve回调函数;如果失败就转到失败状态并执行reject回调函数。
  • Promis函数有resolve和reject两个参数方法,如果是resolve就进.then并且可以一直.then执行下去,如果失败的话就是reject就进.catch
  • Promis函数的方法有,.all .race .allsettled .finally .any

闭包

  • 闭包的几个特性:函数嵌套函数时发生,一个函数可以访问另一个函数内部的变量,参数和变量不回被垃圾回收机制回收
  • 闭包的好处,可以防止命名冲突,可以提升作用域链的范围,在内存中维持一个变量可以做缓存
  • 闭包的坏处,会造成内存泄露,解决方法是使用完一个变量后我们手动赋值为null将变量清空

原型链

  • 查找机制是先查找自身的原型对象(prototype),如果没有就通过隐式原型(proto)查找上一级原型对象,查到最顶级的话就是object的原型对象,如果还没有,就是null
  • .call() .apply() .bind()可以改变this指向

es6新增的语法

  • let const var
  • 箭头函数没有this,不能new
  • Set 和 Map 主要的应用场景在于 数据重组 和 数据储存。
  • Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构

call apply bind 作用和区别

  • 都可以改变this指向,第一个参数都是this要指向的内容,apply是数组的形式,apply和call都是立即执行,bind不是

数组去重的方法

  • es6新增的set方法 includes()如果判断不包含的情况,然后用新的数组再添加,indexof也可以判断,filter也可以

页面加载慢的话都是怎么优化和解决问题的

遇到大量数据导致页面卡死怎么解决

说一下同源策略

说一下防抖和节流

BOM对象都有哪些