Javascript面试题及详细答案150道之(001-015)

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

1. JavaScript的数据类型有哪些?

JavaScript有七种基本数据类型:Number(数字)、String(字符串)、Boolean(布尔值)、Null(空值)、Undefined(未定义)、Symbol(符号)、BigInt(大整数),还有一种引用数据类型Object(对象),数组、函数等都属于对象类型。示例代码如下:

let num = 10; // Number
let str = "hello"; // String
let bool = true; // Boolean
let n = null; // Null
let u; // Undefined
let sym = Symbol('unique'); // Symbol
let big = 12345678901234567890n; // BigInt
let obj = {name: 'John'}; // Object

2. typeof null 的结果是什么?为什么?

typeof null的结果是'object'。这是JavaScript早期设计的一个缺陷,当初JavaScript采用32位系统,null表示为全0的地址,而对象在底层存储也是类似的内存地址形式,所以typeof判断时将null误判为object

3. 如何判断一个变量是否为数组?

可以使用以下几种方法:

  • Array.isArray(arr):这是最推荐的方法,它会直接返回一个布尔值,表示参数是否为数组。
  • Object.prototype.toString.call(arr) === '[object Array]':通过调用Object原型上的toString方法,能准确判断数据类型。
  • arr instanceof Array:判断变量是否是Array构造函数的实例,但在跨框架或跨窗口环境下可能不准确。

示例代码:

let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
console.log(Object.prototype.toString.call(arr) === '[object Array]'); // true
console.log(arr instanceof Array); // true

4. == 和 === 有什么区别?

==会在比较时进行类型转换,只判断值是否相等。例如'1' == 1会将字符串'1'转换为数字1,然后比较,结果为true。而===不会进行类型转换,要求值和类型都必须相等。如'1' === 1结果为false,因为类型不同。

5. 什么是闭包?请举例说明。

闭包是函数和其词法作用域的组合。它允许函数访问其外部函数作用域中的变量,即使外部函数已经执行完毕。例如:

function outer() {
    let count = 0;
    function inner() {
        count++;
        console.log(count);
    }
    return inner;
}

const counter = outer();
counter(); // 输出1
counter(); // 输出2

在这个例子中,inner函数就是一个闭包,它可以访问outer函数作用域中的count变量。

6. 什么是作用域链?

当访问一个变量时,JavaScript会从当前作用域开始查找,如果找不到,就会向上一级作用域查找,直到找到该变量或者到达全局作用域,这样形成的链式查找结构就是作用域链。例如:

let globalVar = 'global';
function outer() {
    let outerVar = 'outer';
    function inner() {
        let innerVar = 'inner';
        console.log(globalVar); // 从当前作用域找不到,向上在全局作用域找到
        console.log(outerVar); // 从当前作用域找不到,向上在外层函数作用域找到
        console.log(innerVar); // 在当前作用域找到
    }
    inner();
}
outer();

7. 什么是原型和原型链?

每个对象都有一个__proto__属性(现代浏览器中),它指向其构造函数的prototype对象。原型链是由多个对象的原型依次连接形成的链条,当访问对象的某个属性或方法时,如果对象自身没有,就会沿着原型链向上查找,直到找到为止或者到达null。例如:

function Person(name) {
    this.name = name;
}
Person.prototype.sayName = function() {
    console.log(this.name);
};
let person = new Person('Alice');
person.sayName(); // 从person对象自身找不到sayName方法,沿原型链在Person.prototype中找到

8. 什么是 this?它是如何绑定的?

this是JavaScript中的一个关键字,它的值取决于函数的调用方式。具体如下:

  • 普通函数调用时,在非严格模式下,this指向全局对象(浏览器中是window),严格模式下为undefined
  • 作为对象的方法调用时,this指向该对象。
  • 使用new关键字调用构造函数时,this指向新创建的实例对象。
  • 箭头函数没有自己的this,它会继承外层作用域的this
  • 使用callapplybind方法可以显式绑定this

示例代码:

function normalFunc() {
    console.log(this);
}
normalFunc(); // 非严格模式下指向window

let obj = {
    method: function() {
        console.log(this);
    }
};
obj.method(); // 指向obj

function Constructor() {
    this.name = 'constructor';
}
let instance = new Constructor(); // this指向instance

let arrowFunc = () => {
    console.log(this);
};
arrowFunc(); // 继承外层的this,若在全局作用域,非严格模式下指向window

function func() {
    console.log(this.name);
}
let anotherObj = {name: 'another'};
func.call(anotherObj); // this指向anotherObj

9. var、let、const 有何区别?

  • var是函数作用域,在函数内部声明的var变量在整个函数内都可访问。它存在变量提升现象,会被提升到作用域顶部,且可以重复声明。例如:
function test() {
    console.log(x); // 输出undefined,var存在变量提升
    var x = 10;
}
test();
  • let是块作用域,只在声明它的代码块内有效。它也存在变量提升,但会形成暂时性死区,在声明之前访问会报错,且不允许重复声明。例如:
{
    let y = 20;
    console.log(y); // 输出20
}
console.log(y); // 报错,y不在此作用域内

let z = 30;
// let z = 40; // 报错,不能重复声明
  • const也是块作用域,用于声明常量,一旦声明就不能重新赋值,声明时必须初始化。例如:
const PI = 3.14;
// PI = 3.15; // 报错,不能重新赋值

10. new 操作符的原理是什么?

new操作符主要做了以下几件事:

  • 创建一个空对象。
  • 将空对象的__proto__属性指向构造函数的prototype属性。
  • 将构造函数中的this绑定到新创建的对象上,执行构造函数中的代码。
  • 如果构造函数没有返回非原始类型的值,则返回新创建的对象。

示例代码:

function Person(name) {
    this.name = name;
}
let person = new Person('Bob');
console.log(person.name); // Bob

11. 手写 call、apply、bind 简化版

  • call方法:
Function.prototype.myCall = function(ctx, ...args) {
    ctx = ctx || window;
    ctx.fn = this;
    const result = ctx.fn(...args);
    delete ctx.fn;
    return result;
};
function add(a, b) {
    return a + b;
}
let obj = {name: 'obj'};
let sum = add.myCall(obj, 3, 5);
console.log(sum); // 8
  • apply方法:
Function.prototype.myApply = function(ctx, args) {
    ctx = ctx || window;
    ctx.fn = this;
    const result = ctx.fn(...(args || []));
    delete ctx.fn;
    return result;
};
let sum2 = add.myApply(obj, [4, 6]);
console.log(sum2); // 10
  • bind方法:
Function.prototype.myBind = function(ctx, ...args) {
    const self = this;
    return function(...innerArgs) {
        return self.apply(ctx, [...args,...innerArgs]);
    };
};
let boundAdd = add.myBind(obj, 2);
let sum3 = boundAdd(8);
console.log(sum3); // 10

12. 常见数组方法有哪些?

  • 遍历类:forEach(遍历数组,执行回调函数,无返回值)、map(遍历数组,返回新数组,回调函数返回值组成新数组)、filter(过滤数组,返回符合条件的元素组成的新数组)、reduce(将数组缩减为单个值,通过回调函数累加等操作)。
  • 查找类:find(查找第一个符合条件的元素)、includes(判断数组是否包含某个元素)、indexOf(查找元素在数组中的索引,不存在返回-1)。
  • 变异类:push(向数组末尾添加元素,返回新长度)、pop(删除数组末尾元素,返回删除的元素)、splice(删除、插入或替换数组元素,返回删除的元素数组)、sort(对数组排序,会修改原数组)。
  • 不变类:slice(截取数组部分元素,返回新数组,不修改原数组)、concat(合并数组,返回新数组,不修改原数组)。

示例代码:

let arr = [1, 2, 3, 4, 5];
// forEach
arr.forEach(item => console.log(item));
// map
let newArr = arr.map(item => item * 2);
// filter
let filteredArr = arr.filter(item => item > 2);
// reduce
let sum = arr.reduce((acc, cur) => acc + cur, 0);
// find
let found = arr.find(item => item === 3);
// includes
let has = arr.includes(4);
// indexOf
let index = arr.indexOf(2);
// push
let length = arr.push(6);
// pop
let popped = arr.pop();
// splice
let removed = arr.splice(1, 2);
// sort
arr.sort((a, b) => a - b);
// slice
let slicedArr = arr.slice(0, 2);
// concat
let combinedArr = arr.concat([7, 8]);

13. 如何实现数组去重?

可以使用Set数据结构或filter方法等。示例如下:

  • 使用Set
let arr = [1, 2, 2, 3, 3, 4];
let uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4]
  • 使用filter
let arr2 = [1, 2, 2, 3, 3, 4];
let uniqueArr2 = arr2.filter((item, index) => arr2.indexOf(item) === index);
console.log(uniqueArr2); // [1, 2, 3, 4]

14. JS如何判断变量是对象?

可以使用typeof结合null判断,或者Object.prototype.toString.call方法。示例代码:

let obj = {key: 'value'};
let num = 10;
// 方法一
console.log(typeof obj === 'object' && obj!== null); // true
console.log(typeof num === 'object' && num!== null); // false
// 方法二
console.log(Object.prototype.toString.call(obj) === '[object Object]'); // true
console.log(Object.prototype.toString.call(num) === '[object Object]'); // false

15. JSON.stringify 中不能序列化哪些?

JSON.stringify不能序列化undefinedfunctionSymbol以及包含循环引用的对象。示例代码:

let obj = {
    prop1: undefined,
    prop2: function() {console.log('function')},
    prop3: Symbol('sym'),
    prop4: {}
};
obj.prop4.circular = obj.prop4; // 循环引用
console.log(JSON.stringify(obj)); 
// 输出{"prop4": {"circular": "[Circular]"}},忽略了undefined、function、Symbol,循环引用会特殊处理

二、150道面试题目录列表

文章序号Javascript面试题150道
1Javascript面试题及答案150道(001-015)
2Javascript面试题及答案150道(016-030)
3Javascript面试题及答案150道(031-045)
4Javascript面试题及答案150道(046-060)
5Javascript面试题及答案150道(061-075)
6Javascript面试题及答案150道(076-090)
7Javascript面试题及答案150道(091-105)
8Javascript面试题及答案150道(106-120)
9Javascript面试题及答案150道(121-135)
10Javascript面试题及答案150道(136-150)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是大剑师兰特

打赏一杯可口可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值