JavaScript
基础面试题
JS 有哪些数据类型?区别?存放位置?

基本类型有七种null、undefined、boolean、number、string、symbol、bigint

引用类型有一种: object(包含 array,function,date,regexp,map,set,weakmap,weakset)

基本类型值存放在栈内存中, 引用类型值存放在中, 栈中存放指向堆内存的指针

ℹ️

区别

基本类型和引用数据类型的区别

栈和堆的区别

如何判断一个 js 变量是数组类型
1. Array.isArray()
2. [] instanceof Array
3. Object.prototype.toString.call([]);//"[object Array]"
判断数据类型的方法有哪些?
typeof (可以基本数据类型, 除了 null ,引用类型都返回 object)
instanceof (只能判断引用类型)
Object.prototype.toString (可以判断所有类型)
根据对象的构造器 constructor 进行判断
Array.isArray (只能判断数组), NaN.isNaN(只能判断 NaN), Number.isInteger() (只能判断整数)

其中最通用的办法是运用 Object.prototype.toString.call()

typeof 返回哪些数据类型

typeof 操作符用于返回一个字符串表示值的数据类型, 无法判断引用类型。 number、string、boolean、symbol、bigint、object、function、undefined

typeof {}   // 'object'
typeof null // 'object'
typeof function // 'function'
typeof true // 'boolean'
typepf undefined // 'undefined'
️🚫

typeof null 返回 "object", 这是 JavaScript 的一个历史遗留问题, 不是一个 bug. null 是一个基本类型, 值为 null, 不是对象

0.1+0.2!=0.3,浮点数精度问题

JS 采用 IEEE 754 编码, 即用 64 位二进制数表示数字, 符号位 sign1 位, 指数偏移值 exponent bias11 位, 剩余的 53 位 fraction 用来表示小数, 而像 0.1 这种小数, 转化成二进制是无限循环的, 只能保留 53 位, 所以在 ES6 前, JS 最大的数字为 2^53-1, 即 9007199254740991。

JS 的 number 都是浮点型的, 可以采用 parseFloat((0.1+0.2).toFixed(10)) ===0.3 来判断

‘1’.toString()为什么可以调用?

包装类型, 自动拆包/装包

  • 原始类型是没有函数可以调用的, 但是其对应的对象类型 StringtoString 方法, 所以可以调用。
  • '1' 已经不是原始类型了, 而是被强制转换成了 String 类型也就是对象类型, 所以可以调用 toString 函数
[1,2,3].map(parseInt)的结果?

结果是:[1,NaN,NaN]

解题过程:https://github.com/keqing77/fe_interview/issues/1 (opens in a new tab)

让 if( a==1 && a==2 && a==3 )成立

重写 valueOf()方法, 读取的时候进行累加操作

const a = {
  valueOf() {
    this.value++;
    return this.value;
  },
};

解题过程:https://github.com/keqing77/fe_interview/issues/2 (opens in a new tab)

在 JS 中有哪些会被隐式转换? 隐式转换的规则?

Undefined、null、布尔值 false、NaN、零、空字符串

== 和 === 的区别是什么?

举例说明。===会自动进行类型转换, ==不会

JS 关于对 this 的理解? this 的绑定规则 ?

this 优先级 new > 显式绑定 (call,apply,bind) > 隐式绑定 (obj.fn()) > 默认绑定 (fn())

  1. 默认绑定
  2. 隐式绑定
  3. 显式绑定
  4. new 绑定

特殊情况: 1. 箭头函数 2. 严格模式 warning this 绑定丢失

// 隐式绑定     obj调用,this指向obj
obj.method();
 
// 默认绑定     this绑定丢失 this指向window
var method1 = obj.method();
method1();
 
// 可以用bind()强绑定this,this指向不会再改变
JS 继承的 6 种方法?
  1. 原型链继承
  2. 借用构造函数继承
  3. 组合继承(原型+借用构造)
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承
new 操作符具体干了什么呢?
  1. 创建一个空对象, 并且 this 变量引用该对象, 同时还继承了该函数的原型。
  2. 属性和方法被加入到 this 引用的对象中。
  3. 新创建的对象由 this 所引用, 并且最后隐式的返回 this 。
var obj = {};
obj.**proto** = Base.prototype;
Base.call(obj);
什么是 JSON ? 如何比较两个 JSON 是否相等?

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它是基于 JavaScript 的一个子集。数据格式简单, 易于读写, 占用带宽小

//将 json 转换为 JavaScript 对象, 使用 JSON.parse() 方法将 JSON 字符串解析为对象
function compareJSON(json1, json2) {
  const obj1 = JSON.parse(json1);
  const obj2 = JSON.parse(json2);
 
  return deepCompare(obj1, obj2);
}
 
function deepCompare(obj1, obj2) {
  // 首先检查对象类型
  if (typeof obj1 !== typeof obj2) {
    return false;
  }
 
  // 检查对象的键数量
  if (Object.keys(obj1).length !== Object.keys(obj2).length) {
    return false;
  }
 
  // 递归比较对象的属性
  for (let key in obj1) {
    if (obj1.hasOwnProperty(key)) {
      if (typeof obj1[key] === "object") {
        if (!deepCompare(obj1[key], obj2[key])) {
          return false;
        }
      } else {
        if (obj1[key] !== obj2[key]) {
          return false;
        }
      }
    }
  }
 
  return true;
}
 
// 示例用法
const json1 = '{"name": "John", "age": 30}';
const json2 = '{"name": "John", "age": 30}';
 
console.log(compareJSON(json1, json2)); // 输出 true
什么是闭包(closure), 为什么要用?

闭包: 有权访问另一个函数作用域中变量的函数, 函数 A 内部有一个函数 B, 函数 B 可以访问到函数 A 中的变量, 那么函数 B 就是闭包。

  • 优点: 保护函数内的变量安全,保护私有变量, 实现数据封装, 防止变量名污染作用域
  • 缺点: 保持着对变量的引用, 作用域链得不到释放, 容易造成内存泄露
function counter() {
  let count = 0;
 
  return function () {
    count++;
    console.log(count);
  };
}
 
const increment = counter();
increment(); // 输出 1
increment(); // 输出 2
JS 的作用域和作用域链
  • 变量作用的范围叫作作用域
  • 当前作用域不存在的变量和引用, 就沿着作用域链继续寻找
JS 的原型和原型链

新建一个对象, 会发现除了自己添加的还有很多属性, 这些都是从原型上继承下来的 每个 JS 对象都有 proto 属性(浏览器实现, 非标准), 这个属性指向了原型, 实例对象.proto === 原型对象.constructor 当在实例对象上找不到时, 就会去原型上找, 原型也有自己的原型, 查找的过程就形成一条了原型链。

Function 和 Object, 构造函数和原型对象的关系
  • Object 是所有对象的底层原型, 所有对象都可以通过 proto 找到它
  • Function 是所有函数的顶层爸爸, 所有函数都可以通过 proto 找到它
  • 函数的 prototype 是一个对象

  • 在 new 关键词后面的函数就是 构造函数
  • 原型的 constructor 属性指向构造函数, 构造函数又通过 prototype 属性指回原型
数组哪些原生方法, 列举一下?
Array.concat( ) 连接数组
Array.join( ) 将数组元素连接起来以构建一个字符串
Array.length 数组的大小
Array.pop( ) 删除并返回数组的最后一个元素
Array.push( ) 给数组添加元素
Array.reverse( ) 颠倒数组中元素的顺序
Array.shift( ) 将元素移出数组
Array.slice( ) 返回数组的一部分
Array.sort( ) 对数组元素进行排序
Array.splice( ) 插入、删除或替换数组的元素
Array.toLocaleString( ) 把数组转换成局部字符串
Array.toString( ) 将数组转换成一个字符串
Array.unshift( ) 在数组头部插入一个元素
Object 对象的常用方法
Object.hasOwnProperty( ) 检查属性是否被继承
Object.isPrototypeOf( ) 一个对象是否是另一个对象的原型
Object.propertyIsEnumerable( ) 是否可以通过 for/in 循环看到属性
Object.toLocaleString( ) 返回对象的本地字符串表示
Object.toString( ) 定义一个对象的字符串表示
Object.valueOf( ) 指定对象的原始值