js面试题

112 阅读7分钟

有哪些数据类型 区别

八种 string,number,null,undefined,symbol,object,bigint,boolen 区别 存储位置不同 原始数据在堆中 引用类型在栈中

数据类型检测的方式

  1. typeof 只能识别原始类型
  2. constructor
let a = [] 
a.constructor
  1. instanceof 缺点:多个窗口之间 比如当前是页面A通过iframe嵌入了页面B 此时 页面A的实例 !== B页面产生的构造函数实例 A的数组 instanceof B的Array 有可能返回false 判断对象类型
obj instanceof Type
  1. Object.prototype.toString 缺点:toString是个原型链上的方法 有可能会被重写(知道就行)
Object.prototype.toString = function(){
    return 213
}
Object.prototype.toString.call(123); // "[object Number]"

判断数组的方式

  1. instanceof
  2. constructor
  3. isArray Array.isArray(arr)
  4. Object.prototype.toString.call()
  5. isPrototypeOf() 判断对象是否在另一个对象原型链上
var a = {}
Object.prototype.isPrototypeOf(a)
//true
  1. Object.getPrototypeOf 返回原型对象进行比较
Object.getPrototypeOf(arr) === Array.prototype

null和undefined的区别

null
表示特指对象未设置
是字面量属性 不是全局对象的一个属性
数字计算中null会转为0

undefined
表示缺失值 此处应该有个值但是还未定义
是全局对象的一个属性
数字计算中计算结果会变成NaN

instanceof操作符实现原理及实现

通过查找原型对比

function myInstanceof(left, right) {
    let p = Object.getPrototypeOf(left)
    let l = right.prototype
    while(true){
        if(!p)return false
        if(p===l)return true
        p = Object.getPrototypeOf(p)
    }
}

==操作符的强制类型转换规则

  1. 有一个是布尔 先转为数字
  2. 字符串==数字 布尔 null 字符串转为数字
  3. 对象==字符串 数字 符号 对象转为原始值
    对象转换原始值 先用valueof 如果valueof还是返回对象 在调用tostring方法 如果没有这两个方法 或者返回不是基本类型的值就抛出TypeError
  4. undefined == null 返回true
  5. Nan==Nan false
  6. 0=='非空字符串' 0转为字符串
  7. 其余情况转为数字

Object.is() 、比较操作符 ===、== 的区别

Object.is()===之间的主要区别在于它们如何处理NaN-0

  • 对于NaNObject.is(NaN, NaN)返回true,而NaN === NaN返回false
  • 对于-0+0Object.is(-0, 0)返回false,而-0 === 0返回true

Object.is()==之间的主要区别在于:

  • == 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false 判断为 true

  • Object.is 不会强制转换两边的值。

new操作符

  1. 创建一个新对象
  2. 把这个对象绑定到类的原型链上
  3. 调用类构造函数改变this指向到新对象上
  4. 判断类执行完后有没有返回值 没有的话再返回这个新对象

数组有哪些方法

  1. Array.isArray()
  2. concat()
  3. toString(),join()
  4. pop(),push() pop返回被删除的值,push返回添加后的长度
  5. shift(),unshift() shift返回被删除的值,unshift返回添加后的长度
  6. reverse(),sort() 改变原数组
  7. slice() 返回被切割的值 这个值是从原数组上浅拷贝来的 修改对象里的值会影响到原数组

image.png

image.png 8. splice()会改变原数组 9. indexOf(),includes(),find(),findIndex() 10. forEach(),map(),filter(),every(),some() 11. reduce() reduce第一个参数为回调函数 第二个值为初始值 如果没写第二个值 数组第一个值会被作为初始值 整个回调将从数组第二个值开始 12. flat()

什么是类数组 怎么转为数组

  1. 函数参数对象
  2. 用getElementsByTagNmae/Classname/name获得的HTML节点
  3. 用querySelector获得的NodeList

转为数组

  1. Array.prototype.slice.call(arguments) 用call bind apply方法
  2. Array.from
  3. [...arguments] 扩展运算符
  4. function(...args){} 函数接受参数时使用

为什么变量提升 产生了什么问题

  1. 提升性能 让函数可以在执行时预先分配栈空间
  2. 提高代码容错 让一些不规范的代码也能正常执行

变量提升导致变量作为全局变量 会干扰一些正常的操作

var tmp = new Date();

function fn(){ 
    console.log(tmp); 
    if(false){ 
    var tmp = 'hello world'; 
    } 
} 
fn();// undefined 
/** * 在这个函数中,原本是要打印出外层的tmp变量,
但是因为变量提升的问题
, * 内层定义的tmp被提到函数内部的最顶部,
相当于覆盖了外层的tmp,所以打印结果为undefined。 */
 

箭头函数和普通函数的区别

  1. 定义形式不同
  2. 箭头函数全是匿名函数
  3. this指向不同 普通函数指向调用者 没有就是window 箭头函数始终指向定义时的上下文
  4. 箭头函数没有prototype属性
  5. 箭头函数不能用于构造函数 不能被new new时 需要把新对象的__proto__绑定到prototype上 箭头函数没有 箭头函数无法改变this指向 无法执行构造函数的内容
  6. 箭头函数没有arguments对象

作用域和作用域链

从存储上来说 作用域本质上是一个对象 作用域中的变量可以理解是该对象的成员 作用域就是代码的执行环境 全局作用域就是全局执行环境 局部作用域就是函数的执行环境 都是栈内存

作用域分为全局作用域和局部作用域 局部作用域只包含函数作用域 ES6的const let提供了块级作用域

函数作用域就是函数定义产生出来的作用域 块级作用域 通过const let 声明

  1. 在函数内部
  2. 在代码块 花括号捏 注意点
  3. 不会进行变量提升
  4. 禁止同一声明
  5. for循环()内部 ()之内会建立一个隐藏的作用域 该作用域不属于for后面的{}中 并且只有for后边{}产生的块级作用域才能访问这个隐藏的作用域

作用域链 多个作用域对象连续引用形成的链式结构
使用方面 使用一个变量时 会尝试在当前作用域下去寻找改变量 没找到再往上层作用域寻找 以此类推直到找到或者到全局作用域还没找到 那么就报错

存储方面 作用域链以数组的形式存储 第一个索引对应的是函数本身的执行上下文 就是当前执行代码所在环境的变量对象 下一个索引对应的空间存储是该对象的外部执行环境 以此类推 直到全局执行环境

var a = 10
function fun() {
   console.log(a)
}
function show(f) {
   var a = 20
   (function() {
      f()   //10,而不是20; 函数的作用域是在函数定义的时候就被决定了,与函数在哪里被调用无关
   })()
}
show(fun)
 

变量查找机制

  1. 变量只能在特定的区域内才能被访问 外部环境不能访问内部的任何变量和函数 可以向上搜索但不可以向下搜索
  2. 可以减轻命名压力

浏览器垃圾回收

  1. 标记法
  2. 引用计数法

标记法

  1. 从根节点出发 window 文档dom树
  2. 遍历节点确认那些对象是可达 那些对象不可达
  3. 回收垃圾 整理内存

对象分为临时对象和长期对象

临时对象

  1. 函数内部的声明 块级作用域的变量 当函数执行完事 会销毁里面的对象

长期对象

  1. webapi
  2. window
  3. dom

对于不同的对象 分为主垃圾回收器和副垃圾回收器

主垃圾回收器

  1. 对象占用空间大

  2. 对象存活时间久

  3. 先标记->遍历

  4. 垃圾清理 释放内存空间

  5. 会产生大量不连续的内存碎片 需要进行内存整理

副垃圾回收器 通常只支持1~8m的内容 新生代的垃圾回收 分为两个区域 一半对象区 一半空闲区

  1. 先标记
  2. 把对象区使用中的对象复制到空闲区 做一次有序排列
  3. 复制完后 把空闲区和对象区进行对调

回收时机

  1. cpu空闲时回收
  2. 分代回收 多回收新生代 少回收老生代
  3. 增量回收 把垃圾回收工作分成小块 每次处理一部分 多次处理