JS面试必问问题

js   2025-02-05 15:06   124   0  

基础语法与数据类型


  1. 简述 JavaScript 中 null 和 undefined 的区别

  • undefined 表示变量已声明但未赋值,函数没有返回值时也会返回 undefined,访问对象不存在的属性也会得到 undefined

  • null 表示一个空对象指针,是一个原始值,通常用于手动表示某个变量的值为空对象。

let a;
console.log(a); // undefined
let b = null;
console.log(b); // null
  1. 解释一下 JavaScript 中的数据类型,以及如何判断数据类型

  • typeof 操作符:返回一个表示数据类型的字符串,但对于 null 会返回 object,对于 ArrayDate 等引用类型都返回 object

  • instanceof 操作符:用于判断对象是否是某个构造函数的实例。

  • Object.prototype.toString.call():能准确判断所有数据类型。

  • 基本数据类型NumberStringBooleanNullUndefinedSymbol(ES6 新增)、BigInt(ES2020 新增)。

  • 引用数据类型Object(包括 ArrayFunctionDate 等)。

  • 判断方法

let num = 10;
console.log(typeof num); // number
let arr = [];
console.log(arr instanceof Array); // true
let bool = true;
console.log(Object.prototype.toString.call(bool)); // [object Boolean]


  1. 如何实现数组去重

// 方法一:使用 Set
let arr = [1, 2, 2, 3, 4, 4];
let uniqueArr = [...new Set(arr)];
console.log(uniqueArr);

// 方法二:使用 filter 和 indexOf
let uniqueArr2 = arr.filter((item, index) => arr.indexOf(item) === index);
console.log(uniqueArr2);

作用域、闭包和 this 指向


  1. 解释 JavaScript 中的作用域和闭包

  • 作用域:定义了变量和函数的可访问范围,分为全局作用域和函数作用域(ES6 新增块级作用域)。变量和函数的作用域决定了它们在代码中的可见性和生命周期。

  • 闭包:指有权访问另一个函数作用域中变量的函数。即使该函数已经执行完毕,其作用域内的变量也不会被销毁,而是会被闭包引用。


function outer() {
    let num = 10;
    function inner() {
        console.log(num);
    }
    return inner;
}
let closure = outer();
closure(); // 10



  1. 简述 this 关键字在不同场景下的指向

  • 全局作用域中:在浏览器环境中,this 指向 window 对象。

  • 函数内部:默认情况下,this 指向调用该函数的对象;如果是构造函数,this 指向新创建的对象。

  • 使用 callapply 或 bind 方法:可以手动指定 this 的指向。

// 全局作用域
console.log(this === window); // true

// 函数内部
function test() {
    console.log(this);
}
test(); // window

// 构造函数
function Person(name) {
    this.name = name;
}
let person = new Person('John');
console.log(person.name); // John

// 使用 call 改变 this 指向
let obj = { name: 'Alice' };
function greet() {
    console.log(`Hello, ${this.name}`);
}
greet.call(obj); // Hello, Alice

异步编程


  1. 解释 JavaScript 中的异步编程,以及为什么需要异步编程

  • 异步编程:允许代码在执行耗时操作(如网络请求、文件读取等)时不会阻塞后续代码的执行,而是继续执行其他任务,当耗时操作完成后再处理其结果。

  • 原因:JavaScript 是单线程的,如果没有异步编程,在执行耗时操作时,整个程序会被阻塞,用户界面会出现卡顿,影响用户体验。

简述 Promise 的三种状态和常用方法

  • then():用于处理 Promise 成功的结果。

  • catch():用于处理 Promise 失败的结果。

  • finally():无论 Promise 状态如何都会执行。

  • pending(进行中):初始状态。

  • fulfilled(已成功):操作成功完成。

  • rejected(已失败):操作失败。

  • 三种状态

  • 常用方法


let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Success');
    }, 1000);
});
promise.then(result => {
    console.log(result);
}).catch(error => {
    console.error(error);
}).finally(() => {
    console.log('Promise completed');
});



  1. 如何使用 async/await 处理异步操作
    async/await 是 ES8 引入的语法糖,用于更优雅地处理异步操作。async 函数返回一个 Promise 对象,await 只能在 async 函数内部使用,用于等待一个 Promise 完成,并返回其结果。

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Data fetched');
        }, 1000);
    });
}

async function getData() {
    try {
        let data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}
getData();

面向对象编程


  1. 如何在 JavaScript 中实现继承

  • 原型链继承:通过原型对象实现继承。

  • 构造函数继承:在子类构造函数中调用父类构造函数。

  • 组合继承:结合了原型链继承和构造函数继承的优点。

  • 寄生组合继承:在组合继承的基础上进行优化。

  • ES6 类继承:使用 class 和 extends 关键字实现继承。

// ES6 类继承示例
class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        console.log(`${this.name} makes a sound.`);
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);
    }
    speak() {
        console.log(`${this.name} barks.`);
    }
}

let dog = new Dog('Buddy');
dog.speak(); // Buddy barks.


  1. 简述 JavaScript 中的原型和原型链

  • 原型:每个对象都有一个内部属性 [[Prototype]](在浏览器中可以通过 __proto__ 访问),它指向该对象的原型对象。原型对象也是一个对象,它也有自己的原型,以此类推,直到最顶层的原型对象 Object.prototype

  • 原型链:当访问一个对象的属性或方法时,JavaScript 首先会在该对象本身查找,如果找不到,就会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(Object.prototype)。

性能优化和错误处理


  1. 如何优化 JavaScript 代码的性能

  • 减少 DOM 操作:DOM 操作是比较耗时的,尽量批量操作 DOM,减少重排和重绘。

  • 使用事件委托:将事件处理程序绑定到父元素上,利用事件冒泡机制处理子元素的事件,减少事件处理程序的数量。

  • 压缩和合并代码:减少 HTTP 请求和文件大小。

  • 使用节流和防抖:对于频繁触发的事件(如滚动、输入框输入等),使用节流和防抖函数限制事件的触发频率。

// 防抖函数示例
function debounce(func, delay) {
    let timer;
    return function() {
        let context = this;
        let args = arguments;
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(context, args);
        }, delay);
    };
}

function handleInput() {
    console.log('Input event triggered');
}

let input = document.getElementById('input');
input.addEventListener('input', debounce(handleInput, 300));


  1. 如何处理 JavaScript 中的错误

  • try...catch 语句:用于捕获和处理同步代码中的错误。

  • Promise 的 catch() 方法:用于处理 Promise 中的错误。

  • window.onerror 事件:用于捕获全局未处理的错误。

try {
    let result = 1 / 0;
} catch (error) {
    console.error(error.message);
}

let promise = new Promise((resolve, reject) => {
    reject(new Error('Promise error'));
});
promise.catch(error => {
    console.error(error.message);
});

window.onerror = function(message, source, lineno, colno, error) {
    console.error(`Global error: ${message}`);
};


博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。