【前端面试1】基础JS的面试题

文章目录


前言

作者最近在往中级前端进发,持续学习以保证自身竞争力,因此刷了一些面试题,有难有易,因此更一篇博文,与大家共同成长。


1. 隐式类型转换

题目:请问输出什么?为什么?

js 复制代码
console.log([] == ![]);

结果:true

解释:

!的优先级比==高,所以先计算!,右边得到false(对象转布尔永远是true,取反得到false)

现在计算[] == false,js进行比较时要先转换为原始值,左边转换:[].tostring() = ''

现在变成'' == false

在[]情况下,''和false都是0,所以得到true

2.this 指向

题目:请问输出什么?

js 复制代码
const User = {
  name: '张三',
  sayName: function() {
    console.log(this.name);
  },
  sayNameArrow: () => {
    console.log(this.name);
  }
};

User.sayName();      
User.sayNameArrow(); 

输出:

张三

undefined

解释:

这是一个this的指向性问题,sayName由于是function函数,因此它的this指向User,所以打印张三

而sayNameArrow是箭头函数,箭头函数没有自己的this,所以指向全局,全局对象是没有name的,因此打印undefined


3.闭包实战

面试场景:面试官问"你写过防抖吗?"这不仅仅是考代码,是考你对闭包的应用。

场景描述:用户在搜索框疯狂打字,每打一个字就发一次 API 请求,服务器炸了。你需要做一个机制:等用户停手 500ms 后再发请求。

javascript 复制代码
function debounce(fn, delay) {
  let timer = null; 
  return function(...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

闭包的定义:debounce 函数执行完了,按理说 timer 变量应该被垃圾回收销毁。

神奇之处:但因为返回的那个新函数引用了 timer,所以 timer 被迫留在了内存里。

实际效果:每次你打字调用这个返回的函数,它们访问的都是同一个 timer。这就是闭包最大的作用------保存状态。

4.事件冒泡

场景:你需要给一个无限滚动的长列表(比如 10,000 个商品)绑定点击事件,点击跳转详情。

javascript 复制代码
ul.addEventListener('click', function(e) {
  // e.target 是你实际点击的那个小元素(比如 li 里面的 span)
  // 核心:利用事件冒泡,往上找,看看是不是 li
  if (e.target.tagName === 'LI') {
    console.log('点击了', e.target.innerText);
  }
});

事件在 DOM 中像泡泡一样,从最底层的元素(Target)一直往上冒(Bubble)到父元素。 我们只需要像"守门员"一样站在父元素门口,不管下面谁被点了,泡泡总会冒上来被我抓到。

5.let和var

题目:下面代码打印什么

javascript 复制代码
var name = 'World';
(function() {
  if (typeof name === 'undefined') {
    var name = 'Jack'; // 注意这里用了 var
    console.log('Goodbye ' + name);
  } else {
    console.log('Hello ' + name);
  }
})();

结果:Goodbye Jack

var赋值的变量会被提升到顶层,此时它在函数内部,但是声明被提升到了顶层,因此name在外面就变成了undefined,所以进入了if分支,重新被赋值,从而打印出Goodbye Jack。

6. 连等赋值

题目:下面的代码输出什么?

javascript 复制代码
let a = { n: 1 };
let b = a;
a.x = a = { n: 2 };

console.log(a.x);
console.log(b.x);

结果:

undefined

{ n: 2 }

js中.的优先级高于=,因此a.x 先准备好,指向了堆内存中 {n:1} 这个对象的一个新属性 x(此时为undefined),然后a = { n: 2 } 执行,变量 a 的指针变了,指向了新对象。最后执行赋值,把 {n:2} 赋给第一步中准备好的那个 x。但是b 依然指向旧对象(现在旧对象变成了 {n:1, x: {n:2}}),而 a 指向了新对象 {n:2}(新对象里没有 x)。

7.类型转换与运算

题目:下面代码会打印什么

javascript 复制代码
console.log(1 + "1");
console.log(1 - "1");

结果:

11

0

解释:加号 (+):如果有一方是字符串,它就变成字符串拼接。1 变成了 "1"。

减号 (-):它会尽最大努力把两边都转成数字进行数学运算。"1" 变成了数字 1。

8.JSON键的理解

题目:下面代码会打印什么

javascript 复制代码
var a = {}, b = '123', c = 123;
a[b] = 'b';
a[c] = 'c';
console.log(a[b]);

结果:c

解释:因为json的键名只能是字符串,所以a[c]实际上是添加了一个'123':'c'。打印自然也是c

9. 原型链

题目:下面的代码打印什么

javascript 复制代码
function Foo() {
  Foo.a = function() { console.log(1) }; // 静态方法
  this.a = function() { console.log(2) }; // 实例方法
}
Foo.prototype.a = function() { console.log(3) }; // 原型方法

Foo.a = function() { console.log(4) }; // 覆盖静态方法

Foo.a();
let obj = new Foo();
obj.a();

结果:

4

2

解释:Foo.a():调用的是构造函数上的静态属性。它先被定义打印 1,后被覆盖打印 4。

obj.a():new 出来的实例 obj。查找属性遵循:实例自身 -> 原型链。因为构造函数里 this.a 给实例自身加了方法,所以直接执

行,打印 2。如果构造函数里没写 this.a,才会去原型上找打印 3。

10.数组拍平

场景:把数组[1, [2, [3, 4]]]变为:[1, 2, 3, 4]

javascript 复制代码
const arr = [1, [2, [3, 4]]];

function flatten(ary) {
  return ary.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, []);
}
console.log(flatten(arr));

解释:这里结合了递归。如果当前项是数组,就递归调用 flatten,否则直接拼接到结果里。

注:ES2019 直接出了 arr.flat(Infinity),但在面试中手写递归依然是加分项。

总结

今天的面试题就到这里,作为作者的学习记录,也希望能帮到大家。

相关推荐
cyforkk2 小时前
07、Java 基础硬核复习:面向对象编程(进阶)的核心逻辑与面试考点
java·开发语言·面试
wjhx2 小时前
在Qt Design Studio中进行页面切换
前端·javascript·qt
钱多多先森2 小时前
【Dify】使用 python 调用 Dify 的 API 服务,查看“知识检索”返回内容,用于前端溯源展示
开发语言·前端·python·dify
霍理迪2 小时前
JS—数组
开发语言·前端·javascript
Surplusx2 小时前
运用VS Code前端开发工具完成微博发布案例
前端·html
哪里不会点哪里.2 小时前
Nginx 详解:高性能 Web 服务器与反向代理
服务器·前端·nginx
Ulyanov2 小时前
超越平面:用impress.js打造智能多面棱柱演示器
开发语言·前端·javascript·平面
HWL56792 小时前
vue抽离自定义指令的方法
前端·javascript·vue.js
2601_949575862 小时前
Flutter for OpenHarmony艺考真题题库+个人信息管理实现
java·前端·flutter