前端面试题 ===> 【JavaScript - 基础】

2,992 阅读15分钟

JavaScript - 基础 面试题总结

1. JS的数据类型有哪些,区别是什么?

  1. 基本数据类型
    • number、string、boolean、undefined、null、symbol、bigint
      • symbol:创建对象的唯一属性名,因此可以用来防止属性名冲突,保证属性名的唯一;
      • bigint:大整数相加;
  2. 引用数据类型
    • Object、Array、Function;
  3. 区别
    • 基本数据类型是存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据;
    • 引用数据类型是存储在堆内存中,占据空间大,引用数据类型在栈中存储了地址,该地址指向堆中该实体的起始地址,通过检索栈中的地址,取得地址后在堆中获得实体;

2. undefined 和 null 的区别

  1. 数据类型不同;
  2. 意义不同:
    • undefined:
      • 声明变量未赋值;
    • null:
      • 声明了一个变量,并且赋值了,但赋的值是一个null;
      • 表示准备用来保存对象,还没有真正保存对象的值,从逻辑角度看,null值表示一个空对象指针;
  3. 转数字结果不同(Number()):
    • undefined:NaN;
    • null:0;
  4. 产生场景不同:
    • undefined:
      • 声明变量未赋值;
      • 数组没有某个元素;
      • 对象没有某个属性;
      • 函数没有返回值;
      • 函数调用的时候没有传递参数并且声明函数的时候没有设置默认值;
    • null:
      • 作为原型链的终点;
null == undefined // true
null === undefined // false

3. 如何判断JS的数据类型?

  1. typeof
    • 可以判断除null之外的基本数据类型;
    • 可以判断函数;
  2. instanceof
    • 依据:判断构造函数的原型是否出现在实例的原型链上;
    • ❗ 注意:
      • 不能判断基本数据类型;
        • 使用 let、const、var 声明的基本数据类型;
        const num = 11;
        num instanceof Number; // false
        
        const str = '11';
        str instanceof String; // false
        
        // num、str并不是实例
        
        // 数字对象类型
        const num1 = new Number(5);
        num1 instanceof Number; // true
        
      • 可以判断引用数据类型;
        const num = new Number();
        num instanceof Number; // true
        
        const str = new String();
        str instanceof String; // true
        
      • 使用方法得到的结果并不一定是可靠的,因为在ES7规范中可以通过自定义的 Symbol.hasInstane 方法来覆盖默认行为;
  3. Object.prototype.toString.call()
    • 需要让toString函数内部的this指向当前检测的变量;
    • 返回值:[object 类型]
    • ❗ 注意:
      • 该方法不能区分数字类型和数字对象类型,字符串类型和字符串对象类型,布尔类型和布尔对象类型;
      • 数字类型:通过const、let、var(❌)声明得到得;
      • 数字对象类型:通过 new Number()构造函数的得到的;

4. 创建对象的方式

  1. 简单对象的创建,使用对象字面量方式({})创建;
  2. Object构造函数;
    const obj = new Object();
    obj.name = '禁止摆烂_才浅';
    
  3. 用函数来模拟Class;
    • 方式一:
      • 无参构造函数;
      function Obj() {
          this.name = '禁止摆烂_才浅';
      }
      const obj = new Obj();
      console.log(obj.name); // 禁止摆烂_才浅
      
    • 方式二:
      • 有参构造函数;
      function Obj(name, age) {
          this.name = name;
          this.age = age;
      }
      const obj = new Obj('禁止摆烂_才浅', 24);
      console.log(obj.anme); // 禁止摆烂_才浅
      
  4. 使用对象原型的关键字创建;
    function Obj() {}
    const obj = new Obj();
    Obj.prototype.name = '禁止摆烂_才浅';
    // 访问的时候,首先去obj本身上查找,此时,obj本身上没有该属性,就会去obj的原型上查找
    console.log(obj.name); // 禁止摆烂_才浅
    

5. 创建函数的方式

  1. 声明式函数;
    function add(num1, num2) { return num1 + num2 }
    
  2. 函数表达式;
    const add = (num1, num2) => (num1 + num2);
    
  3. ❌ 构造函数(new Function());
    const add = new Function('num1', 'num2', 'return num1 + num2');
    

6. JS常用的内置对象,并列举该对象常用的方法

  1. Number ➡ 数值对象:

    方法或属性描述
    Number.toFixed()采用定点计数法格式化数字(保留几位小数)
    Number.toString()将一个数字转换成字符串
    Number.isInterger()判断指定数字是否为整数
    Number.MAX_VALUE最大数值
    Number.MIN_VALUE最小数值
    Number.NaN特殊的非数字值(NaN属于数字)
    Number.NEGATIVE_INFINITY负无穷大
    Number.POSITIVE_INFINITY正无穷达
    Number.valueOf()返回原始数值
  2. String ➡ 字符串对象:

    方法或属性描述
    length获取字符串的长度
    substring()截取字符串,返回指定部分的字符串
    split()将字符串分隔成数组
    startsWith()检测是否以某段字符开头
    endsWith()检测是否以某段字符结尾
    includes()判断一个字符串是否包含在另一个字符串中
    replace()替换字符串中指定的字符
    toUpperCase()将字符串转换为大写
    toLowerCase()将字符串转换为小写
  3. Boolean ➡ 布尔对象:

    方法或属性描述
    Boolean.toString()将布尔值转换成字符串
    Boolean.valueOf()Boolean对象的布尔值
  4. Math ➡ 数学对象:

    方法或属性描述
    Math.PI圆周率
    Math.abs()绝对值
    Math.floor()向下取整
    Math.ceil()向上取整
    Math.round()四舍五入
    Math.random()0~1的随机数
    Math.pow(x, y)求x的y次方
    Math.sqrt()求平方根
  5. Array ➡ 数组:

    方法或属性描述
    length数组的长度
    shift()删除数组的第一个元素,并返回被删除的元素
    pop()删除数组的最后一个元素,返回被删除的元素
    unshift()给数组的最前面插入一个或多个元素,返回插入元素之后的数组长度
    push()给数组的最后面添加一个或多个元素,返回添加元素之后的数组长度
    splice()插入、删除、替换数组的元素,以数组的形式返回被修改的内容
    sort()对数组元素进行排序,并返回排序好的数组
    reverse()将数组中元素的位置颠倒,并返回颠倒元素之后的数组
    forEach()遍历数组,无返回值
    map()迭代数组,遍历原数组,把原数组中的每一项数据加工改造,形成一个新数组返回
    filter()过滤原数组中的数据,把满足条件的数据放在一个新数组中返回
    reduce()累加器,返回函数累计处理的结果
    find()返回数组中满足条件的第一个元素
    join()数组转换成字符串,返回一个字符串
    some()判断数组中是否至少有一个元素满足条件,返回布尔值
    every()判断数组中的所有元素是否都满足条件,返回布尔值
    concat()合并数组,返回一个新数组
    slice()获取指定的元素,返回一个含有指定元素的数组
    includes()查看数组中是否有每一项数据,返回布尔值
    indexOf()正向查看数组里卖弄指定数据的索引
    flat()数组降维,返回一维数组
  6. Object ➡ 基础对象:

    方法或属性描述
    Object.constructor对象的构造函数
    Object.keys()将对象的所有属性放到数组中
    Object.values()将对象的所有属性值放到数组中
    Object.hasOwnProperty()检查属性是否被继承
    Object.isPrototypeOf()一个对象是否是另一个对象的原型
    Object.propertyIsEnumerable()是否可以通过 for/in 循环看到属性
    Object.toLocaleString()返回对象的本地字符串表示
    Object.toString()定义一个对象的字符串表示
    Object.valueOf()指定对象的原始值
  7. Date ➡ 日期对象:

    方法或属性描述
    Date.getDate()返回一个月中的某一天
    Date.getDay()返回一周中的某一天
    Date.getFullYear()返回 Date 对象的年份字段
    Date.getHours()返回 Date 对象的小时字段
    Date.getMilliseconds()返回 Date 对象的毫秒字段
    Date.getMinutes()返回 Date 对象的分钟字段
    Date.getMonth()返回 Date 对象的月份字段
    Date.getSeconds()返回 Date 对象的秒字段
    Date.getTime()返回 Date 对象的毫秒表示
  8. RegExp ➡ 正则表达式对象:

    方法或属性描述
    RegExp.exec()通用的匹配模式
    RegExp.global正则表达式是否全局匹配
    RegExp.ignoreCase正则表达式是否区分大小写
    RegExp.lastIndex下次匹配的起始位置
    RegExp.source正则表达式的文本
    RegExp.test()检测一个字符串是否匹配某个模式
    RegExp.toString()把正则表达式转换成字符串
  9. Function ➡ 函数构造器:

    方法或属性描述
    Function.apply()将函数作为一个对象的方法调用
    Function.arguments[]传递给函数的参数
    Function.call()将函数作为对象的方法调用
    Function.caller调用当前函数的函数
    Function.length已声明的参数的个数
    Function.prototype对象类的原型
    Function.toString()把函数转换成字符串
  10. Arguments ➡ 函数参数集合:

    方法或属性描述
    Arguments[]函数参数的数组
    Arguments一个函数的参数和其他属性
    Arguments.callee当前正在运行的函数
    Arguments.length传递给函数的参数个数
  11. Error ➡ 异常对象:

    方法或属性描述
    Error.message可以读取的错误消息
    Error.name错误的类型
    Error.toString()把 Error 对象转换成字符串
    EvalError不正确使用 eval()时抛出
    SyntaxError抛出该错误用来通知语法错误
    RangeError在数字超出合法范围时抛出
    ReferenceError在数字超出合法范围时抛出
    TypeError当一个值的类型错误时,抛出该异常
    URIError由 URl 的编码和解码方法抛出

7. Object.is()、==、=== 的区别

  1. Object.is():
    • 是ES6中的一个新方法,它用于判断两个值是否为同一个值
    • 语法
      Object.is(val1, val2);
      
    • 对于基本数据类型,如果它们的值和数据类型相等,则返回 true
    • 对于引用数据类型,仅当它们的地址相同时才会返回 true
  2. ==(等值符):
    • 只比较值;
    • 数据类型相同:
      • 去比较两边的值是否相等;
    • 数据类型不同:
      • 会尝试自动转换为相同类型,能转换就比较值,转换不了就是 false
  3. ===(等同符):
    • 既要判断值相等,也要判断数据类型相同;
    • 数据类型相同:
      • 直接比较值是否相等;
    • 数据类型不同:
      • 直接返回false;
  4. Object.is()=== 区别:
    • 主要区别:
      • 它们如何处理 NaN-0
    • 对于NaN:
      Object.is(NaN, NaN); // true
      NaN === NaN // false
      
    • 对于-0+0
      Object.is(-0, +0); // false
      -0 === +0 // true
      
  5. Object.is()== 区别:
    • ==运算符在判断相等前对两边的变量进行类型的强制转换;
      • 这种情况会将''、0、false判断为 true
    • Object.is() 不会进行类型的强制转换;

8. 如何区分数组和对象?

  1. Array.isArray()
    • 判断数组最准确的方法,使用该方法,可以准确判断该数据一定是有数组的结构的,一定是有数组的方法和属性的,一定是在Array的原型上的;
    Array.isArray([]); // true
    Array.isArray({}); // false
    

    下面三种方法都能通过一些字段去修改默认值;

  2. instanceof
    • 判断构造函数的原型是否出现在实例的原型链上;
    [] instanceof Array; // true
    {} instanceof Array; // false
    
  3. constructor
    • 找到创建当前实例的构造函数;
    [].constructor; // Array
    {}.constructor; // Object
    
  4. Object.prototype.toString.call()
    • 通过call、apply去改变 this 的指向;
    Object.prototype.toString.call([]);	// '[object Array]'
    Object.prototype.toString.call({});	// '[object Object]'
    

9. 多维数组降维

  1. 使用 flat()方法:
    • 可以给该方法传递参数,一种是传递当前数组的维数,还有一种就是直接传递Infinity(不管是几维数组,最后都会降为一维);
  2. 递归:
    const arr = [1, 2, [2, 3, [4, 5, [6, 7, [1, 2]], [{a: 1}], {c: 12}]], 2, 3];
    const flatArr = (arr) => {
        let newArr = [];
        for(const item of arr) {
            if (!Array.isArray(item)) {
                newArr.push(item);
                continue;
            }
            newArr.push(...flatArr(item));
        }
        return newArr;
    };
    
  3. ❌ 先使用 数组的 join() 方法将数组转换为字符串,再使用 字符串的 split()方法:
    • 缺点
      • 如果元素为数字,转换之后是字符串了;
      • 不能转换对象类型的元素; image.png
    const arr = [[1, 2, 3], [4, 5, 6]];
    const str = arr.join(); // 字符串 => 1, 2, 3, 4, 5, 6
    const newArr = str.split(','); // ['1', '2', '3', '4', '5', '6']
    

10. 怎么判断两个对象相等?

  1. ES6中有一个方法判断两个对象是否相等,这个方法判断的是两个对象的地址是否一致:
    const obj1 = { a: 1 };
    const obj2 = { a: 1 };
    const obj3 = obj1;
    console.log(Object.is(obj1, obj2)); // false
    console.log(Object.is(obj1, obj3)); //true
    
    • 当前需求是比较两个对象内容是否一致时就没用了;
  2. 想要比较两个对象内容是否一致,思路是要遍历对象的所有键名和键值是否都一致:
const obj1 = { a: 1, b: { c: 2 } };
ocnst obj2 = { b: { c: 3 }, a: 1 };

function isObjectValueEqual(a, b) {
    // 判断两个对象是否指向同一内存,指向同一内存返回 true
    if (a === b) return true;
    // 获取两个对象键值数组
    let aProps = Object.getOwnPropertyNames(a);
    let bProps = Object.getOwnPropertyNames(b);
    // 判断两个对象键值数组长度是否一致,不一致返回 false
    if (aProps.length !== bProps.length) return false;
    // 遍历对象的键值
    for (let prop in a) {
        // 判断 a 的键值,在 b 中是否存在,不存在,返回 false
        if (b.hasOwnProperty(prop)) {
            // 判断 a 的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
            if (typeof a[prop] === 'object') {
                if (!isObjectValueEqual(a[prop], b[prop])) return false;
            } else if (a[prop] !== b[prop]) {
                return false;
            }
        } else {
            return false;
        }
    }
    return true;
}

console.log(isObjectValueEqual(obj1, obj2)); // false

11. 列举 强制转换类型 和 隐式类型转换

  1. 强制类型转换:
    • 转数字:
      • Number()、parseInt()、parseFloat()
      Number(null); // 0
      Number(''); // 0
      Number(); // 0
      parseInt('11.11'); // 11
      parseInt('11s1.ss'); // 11
      parseFloat('11.11'); // 11.11
      parseFloat('11s1.ss'); // 11
      
    • 转字符串:
      • String()、toString()
    • 转布尔:
      • Boolean()
  2. 隐式类型转换:
    • +、-、*、/、%、==、>=、<=

12. JS中获取当前日期月份

const date = new Date();
console.log(`当前年份 - ${date.getFullYear()}`);
// 月份是从0开始的,当前月份 = 得到的月份 + 1
console.log(`当前月份 - ${date.getMonth() + 1}`);
console.log(`当前日期 - ${date.getDate()}`);
console.log(`当前周几 - ${date.getDay()}`);
console.log(`当前小时 - ${date.getHours()}`);
console.log(`当前分钟 - ${date.getMinutes()}`);
console.log(`当前秒 - ${date.getSeconds()}`);

13. 什么是伪数组,怎么转换为真数组

  1. 伪数组
    • 具有索引、长度、不能使用数组方法;
    • 典型的伪数组:document.children、document.childnodes、document.uerySelectorAll()、
  2. 转换为真数组
    • Array.from();
    • 展开运算符;
    • 声明一个新数组,循环伪数组,将伪数组的每一项push进新数组;

14. 真数组 和 伪数组 的区别

  1. 共同点:
    • 都可以通过索引取值,都有length属性;
  2. 不同点:
    • 原型不同:
      • 数组原型:
        • Array.prototype
      • 伪数组原型:
        • 是一种集合,不具备数组的方法;

15. 谈谈对变量的理解

  1. 变量就是一个用来存储数据的容器;
  2. 本质:
    • 内存里的一块空间,用来存储数据;
  3. 初始化:
    • 声明变量并及进行赋值操作;
  4. 命名规则:
    • 只能是数字、英文字母、美元符、下划线组成;
    • 开头不能是数字;
  5. 声明变量:let
  6. 使用:
    • 必须是先声明再使用;

16. let、const、var的区别

  • let / const
    1. ES6新增的;
    2. 不存在变量提升;
    3. 有块作用域;
    4. 必须先声明再使用;
    5. 不能声明重复变量;
    6. let
      • 声明变量;
      • 有暂时性死区;
    7. const
      • 声明常量;
      • 声明的时候必须进行初始化;
  • var
    1. 存在变量提升;
    2. 没有块作用域;
    3. 既可以声明变量也可以声明常量;
    4. 可以先声明后使用(变量提升);

17. for-in 与 for-of 的区别

  1. for-in
    • 适用:
      • 对象、数组、伪数组、字符串;
    • 遍历当前对象可枚举属性及其原型链上的属性
    • 遍历的是属性或索引;
    • 得到的索引是字符串类型;
  2. for-of
    • 适用:
      • 数组、伪数组、字符串;
    • 遍历的是(可迭代的数据)数组/伪数组的元素、字符串的值
    • 得到的索引是数字类型;
    • 如果遍历数组的时候,要使用数组的索引,可以使用arr.entries()方法(该方法的返回值是一个由给定对象自有的可枚举字符串键属性的键值对组成的数组,每个键值对都是一个包含两个元素的数组,第一个元素是属性的键(始终是字符串),第二个元素是属性值);
      • image.png

18. 具名函数 和 匿名函数

  • 具名函数
    1. 可以先使用后声明;
    2. 不能作为事件处理函数;
    3. 可以用于构造函数;
  • 匿名函数
    1. 必须先声明后使用;
    2. 可作为事件处理函数使用;
    3. 使用方式:
      • 函数表达式;
      • 立即执行函数;
        • 立即执行函数的格式:
        !(function(形参) { 函数体 })(实参);
        !(function(形参) { 函数体 } (实参));
        // ❗ 立即执行函数的最后面必须加分号
        
    • 不能作为构造函数使用;

19. 手写冒泡排序

使用双重for循环 外层for循环控制排列好的个数(个数 - 1) 内存for循环控制排序好一个数字需要比较的次数

let arr = [2, 46, 32, 86, 45, 98]

// [(2), 46, 32, 86, 45, 98]  0 5
// [2, (32), 86, 45, 46, 98]  1 4
// [2, 32, (45), 46, 86, 98]  2 3
// [2, 32, 45, (46), 86, 98]  3 2
// [2, 32, 45, 46, (86), 98]  4 1

for (let i = 0; i < arr.length - 1; i++) {
  for (let j = 0; j < arr.length - i - 1; j++) {
    if (arr[j] > arr[j + 1]) {
      [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
    }
  }
}
console.log(arr);

20. 请指出JavaScript 宿主对象 和 原生对象 的区别?

  1. 原生对象
    • “独立于宿主环境” 的 ECMAScript 实现提供的对象;
    • 包含:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
  2. 内置对象
    • 我们不必明确实例化内置对象,它已经被内部实例化了;
    • 同样是 “独立于宿主环境”。而 ECMA-262 只定义了 两个内置对象,即 Global、Math
  3. 宿主对象
    • BOMDOM都是宿主对象;
    • 因为其对于不同的宿主环境所展示的内容不同;
    • 简单来说,ECMAScript官方未定义的对象都宿主对象,因为其未定义的对象大多数都是自己通过ECMAScript程序创建的对象;

21. 如何遍历对象的属性?

  • 遍历自身可枚举属性(可枚举,非继承属性)Object.keys()
  • 遍历自身的所有属性(可枚举、不可枚举、非继承属性)Object.getOwnPropertyNames()
    • 该方法返回一个由指定对象的所有自身属性组成的数组(包括不可枚举属性但不包括 Symbol 值作为名称的属性)
  • 遍历可枚举的自身属性和继承属性(可枚举、可继承属性)for-in