2024年前端最全字节跳动最爱考的前端面试题:JavaScript-基础(1),面试的内涵是什么

紧跟潮流

大前端和全栈是以后前端的一个趋势,懂后端的前端,懂各端的前端更加具有竞争力,以后可以往这个方向靠拢。

这边整理了一个对标“阿里 50W”年薪企业高级前端工程师成长路线,由于图片太大仅展示一小部分

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

字符串与数字

转换为数字然后比较

其他类型与布尔类型
  • 先把布尔类型转换为数字,然后继续进行比较
对象与非对象
  • 执行对象的 ToPrimitive(对象)然后继续进行比较
假值列表
  • undefined
  • null
  • false
  • +0, -0, NaN
  • “”

(2)问:了解 this 嘛,bind,call,apply 具体指什么

它们都是函数的方法

call: Array.prototype.call(this, args1, args2]) apply: Array.prototype.apply(this, [args1, args2]) :ES6 之前用来展开数组调用, foo.appy(null, []),ES6 之后使用 … 操作符

  • New 绑定 > 显示绑定 > 隐式绑定 > 默认绑定
  • 如果需要使用 bind 的柯里化和 apply 的数组解构,绑定到 null,尽可能使用 Object.create(null) 创建一个 DMZ 对象

四条规则:

  • 默认绑定,没有其他修饰(bind、apply、call),在非严格模式下定义指向全局对象,在严格模式下定义指向 undefined

function foo() {
console.log(this.a);
}

var a = 2;
foo();
复制代码

  • 隐式绑定:调用位置是否有上下文对象,或者是否被某个对象拥有或者包含,那么隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。而且,对象属性链只有上一层或者说最后一层在调用位置中起作用

function foo() {
console.log(this.a);
}

var obj = {
a: 2,
foo: foo,
}

obj.foo(); // 2
复制代码

  • 显示绑定:通过在函数上运行 call 和 apply ,来显示的绑定 this

function foo() {
console.log(this.a);
}

var obj = {
a: 2
};

foo.call(obj);
复制代码

显示绑定之硬绑定

function foo(something) {
console.log(this.a, something);

return this.a + something;
}

function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments);
};
}

var obj = {
a: 2
}

var bar = bind(foo, obj);
复制代码

New 绑定,new 调用函数会创建一个全新的对象,并将这个对象绑定到函数调用的 this。

  • New 绑定时,如果是 new 一个硬绑定函数,那么会用 new 新建的对象替换这个硬绑定 this,

function foo(a) {
this.a = a;
}

var bar = new foo(2);
console.log(bar.a)
复制代码

(4)问:手写 bind、apply、call

// call

Function.prototype.call = function (context, …args) {
context = context || window;

const fnSymbol = Symbol(“fn”);
context[fnSymbol] = this;

contextfnSymbol;
delete context[fnSymbol];
}
复制代码

// apply

Function.prototype.apply = function (context, argsArr) {
context = context || window;

const fnSymbol = Symbol(“fn”);
context[fnSymbol] = this;

contextfnSymbol;
delete context[fnSymbol];
}
复制代码

// bind

Function.prototype.bind = function (context, …args) {
context = context || window;
const fnSymbol = Symbol(“fn”);
context[fnSymbol] = this;

return function (…_args) {
args = args.concat(_args);

contextfnSymbol;
delete context[fnSymbol];
}
}

复制代码

(3)问:setTimeout(fn, 0)多久才执行,Event Loop

setTimeout 按照顺序放到队列里面,然后等待函数调用栈清空之后才开始执行,而这些操作进入队列的顺序,则由设定的延迟时间来决定

手写题:Promise 原理

class MyPromise {
constructor(fn) {
this.resolvedCallbacks = [];
this.rejectedCallbacks = [];

this.state = ‘PENDING’;
this.value = ‘’;

fn(this.resolve.bind(this), this.reject.bind(this));

}

resolve(value) {
if (this.state === ‘PENDING’) {
this.state = ‘RESOLVED’;
this.value = value;

this.resolvedCallbacks.map(cb => cb(value));
}
}

reject(value) {
if (this.state === ‘PENDING’) {
this.state = ‘REJECTED’;
this.value = value;

this.rejectedCallbacks.map(cb => cb(value));
}
}

then(onFulfilled, onRejected) {
if (this.state === ‘PENDING’) {
this.resolvedCallbacks.push(onFulfilled);
this.rejectedCallbacks.push(onRejected);

}

if (this.state === ‘RESOLVED’) {
onFulfilled(this.value);
}

if (this.state === ‘REJECTED’) {
onRejected(this.value);
}
}
}

复制代码

问:js脚本加载问题,async、defer问题

  • 如果依赖其他脚本和 DOM 结果,使用 defer
  • 如果与 DOM 和其他脚本依赖不强时,使用 async

参考资料

问:如何判断一个对象是不是空对象?

Object.keys(obj).length === 0

手写题:在线编程,getUrlParams(url,key); 就是很简单的获取url的某个参数的问题,但要考虑边界情况,多个返回值等等

问: <script src=’xxx’ ’xxx’/>外部js文件先加载还是onload先执行,为什么?

onload 是所以加载完成之后执行的

问:怎么加事件监听,两种

onclick 和 addEventListener

问:事件传播机制(事件流)

冒泡和捕获

(4)问:说一下原型链和原型链的继承吧

  • 所有普通的 [[Prototype]] 链最终都会指向内置的 Object.prototype,其包含了 JavaScript 中许多通用的功能
  • 为什么能创建 “类”,借助一种特殊的属性:所有的函数默认都会拥有一个名为 prototype 的共有且不可枚举的属性,它会指向另外一个对象,这个对象通常被称为函数的原型

function Person(name) {
this.name = name;
}

Person.prototype.constructor = Person
复制代码

  • 在发生 new 构造函数调用时,会将创建的新对象的 [[Prototype]] 链接到 Person.prototype 指向的对象,这个机制就被称为原型链继承

  • 方法定义在原型上,属性定义在构造函数上

  • 首先要说一下 JS 原型和实例的关系:每个构造函数 (constructor)都有一个原型对象(prototype),这个原型对象包含一个指向此构造函数的指针属性,通过 new 进行构造函数调用生成的实例,此实例包含一个指向原型对象的指针,也就是通过 [[Prototype]] 链接到了这个原型对象

  • 然后说一下 JS 中属性的查找:当我们试图引用实例对象的某个属性时,是按照这样的方式去查找的,首先查找实例对象上是否有这个属性,如果没有找到,就去构造这个实例对象的构造函数的 prototype 所指向的对象上去查找,如果还找不到,就从这个 prototype 对象所指向的构造函数的 prototype 原型对象上去查找

  • 什么是原型链:这样逐级查找形似一个链条,且通过  [[Prototype]] 属性链接,所以被称为原型链

  • 什么是原型链继承,类比类的继承:当有两个构造函数 A 和 B,将一个构造函数 A 的原型对象的,通过其 [[Prototype]] 属性链接到另外一个 B 构造函数的原型对象时,这个过程被称之为原型继承。

标准答案更正确的解释

什么是原型链?

当对象查找一个属性的时候,如果没有在自身找到,那么就会查找自身的原型,如果原型还没有找到,那么会继续查找原型的原型,直到找到 Object.prototype 的原型时,此时原型为 null,查找停止。 这种通过 通过原型链接的逐级向上的查找链被称为原型链

什么是原型继承?

一个对象可以使用另外一个对象的属性或者方法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样根据原型链的规则,如果查找一个对象属性且在自身不存在时,就会查找另外一个对象,相当于一个对象可以使用另外一个对象的属性和方法了。

参考链接

问:说下对 JS 的了解吧

是基于原型的动态语言,主要独特特性有 this、原型和原型链。

JS 严格意义上来说分为:语言标准部分(ECMAScript)+ 宿主环境部分

语言标准部分

2015 年发布 ES6,引入诸多新特性使得能够编写大型项目变成可能,标准自 2015 之后以年号代号,每年一更

宿主环境部分

  • 在浏览器宿主环境包括 DOM + BOM 等
  • 在 Node,宿主环境包括一些文件、数据库、网络、与操作系统的交互等

问:数组能够调用的函数有那些?

  • push
  • pop
  • splice
  • slice
  • shift
  • unshift
  • sort
  • find
  • findIndex
  • map/filter/reduce 等函数式编程方法
  • 还有一些原型链上的方法:toString/valudOf

问:如何判断数组类型

Array.isArray

问: 函数中的arguments是数组吗?类数组转数组的方法了解一下?

是类数组,是属于鸭子类型的范畴,长得像数组,

  • … 运算符
  • Array.from
  • Array.prototype.slice.apply(arguments)

问:用过 TypeScript 吗?它的作用是什么?

为 JS 添加类型支持,以及提供最新版的 ES 语法的支持,是的利于团队协作和排错,开发大型项目

问:PWA使用过吗?serviceWorker的使用原理是啥?

渐进式网络应用(PWA)是谷歌在2015年底提出的概念。基本上算是web应用程序,但在外观和感觉上与原生app类似。支持PWA的网站可以提供脱机工作、推送通知和设备硬件访问等功能。

Service Worker是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门。 现在,它们已包括如推送通知和后台同步等功能。 将来,Service Worker将会支持如定期同步或地理围栏等其他功能。 本教程讨论的核心功能是拦截和处理网络请求,包括通过程序来管理缓存中的响应。

参考链接

问:ES6 之前使用 prototype 实现继承

Object.create() 会创建一个 “新” 对象,然后将此对象内部的 [[Prototype]] 关联到你指定的对象(Foo.prototype)。Object.create(null) 创建一个空 [[Prototype]] 链接的对象,这个对象无法进行委托。

function Foo(name) {
this.name = name;
}

Foo.prototype.myName = function () {
return this.name;
}

// 继承属性,通过借用构造函数调用
function Bar(name, label) {
Foo.call(this, name);
this.label = label;
}

// 继承方法,创建备份
Bar.prototype = Object.create(Foo.prototype);

// 必须设置回正确的构造函数,要不然在会发生判断类型出错
Bar.prototype.constructor = Bar;

// 必须在上一步之后
Bar.prototype.myLabel = function () {
return this.label;
}

var a = new Bar(“a”, “obj a”);

a.myName(); // “a”
a.myLabel(); // “obj a”
复制代码

问:如果一个构造函数,bind了一个对象,用这个构造函数创建出的实例会继承这个对象的属性吗?为什么?

不会继承,因为根据 this 绑定四大规则,new 绑定的优先级高于 bind 显示绑定,通过 new 进行构造函数调用时,会创建一个新对象,这个新对象会代替 bind 的对象绑定,作为此函数的 this,并且在此函数没有返回对象的情况下,返回这个新建的对象

(3)箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?

  • 普通函数通过 function 关键字定义, this 无法结合词法作用域使用,在运行时绑定,只取决于函数的调用方式,在哪里被调用,调用位置。(取决于调用者,和是否独立运行)
  • 箭头函数使用被称为 “胖箭头” 的操作 => 定义,箭头函数不应用普通函数 this 绑定的四种规则,而是根据外层(函数或全局)的作用域来决定 this,且箭头函数的绑定无法被修改(new 也不行)。
  • 箭头函数常用于回调函数中,包括事件处理器或定时器
  • 箭头函数和 var self = this,都试图取代传统的 this 运行机制,将 this 的绑定拉回到词法作用域
  • 没有原型、没有 this、没有 super,没有 arguments,没有 new.target
  • 不能通过 new 关键字调用
  • 一个函数内部有两个方法:[[Call]] 和 [[Construct]],在通过 new 进行函数调用时,会执行 [[construct]] 方法,创建一个实例对象,然后再执行这个函数体,将函数的 this 绑定在这个实例对象上
  • 当直接调用时,执行 [[Call]] 方法,直接执行函数体
  • 箭头函数没有 [[Construct]] 方法,不能被用作构造函数调用,当使用 new 进行函数调用时会报错。

function foo() {
return (a) => {
console.log(this.a);
}
}

var obj1 = {
a: 2
}

var obj2 = {
a: 3
}

var bar = foo.call(obj1);
bar.call(obj2);
复制代码

参考资料

问:知道 ES6 的 Class 嘛?Static 关键字有了解嘛

为这个类的函数对象直接添加方法,而不是加在这个函数对象的原型对象上

(3)问:事件循环机制 (Event Loop)

事件循环机制从整体上告诉了我们 JavaScript 代码的执行顺序 Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

先执行宏任务队列,然后执行微任务队列,然后开始下一轮事件循环,继续先执行宏任务队列,再执行微任务队列。

  • 宏任务:script/setTimeout/setInterval/setImmediate/ I/O / UI Rendering
  • 微任务:process.nextTick()/Promise

上诉的 setTimeout 和 setInterval 等都是任务源,真正进入任务队列的是他们分发的任务。

优先级

  • setTimeout = setInterval 一个队列
  • setTimeout > setImmediate
  • process.nextTick > Promise

for (const macroTask of macroTaskQueue) {  
handleMacroTask();    
for (const microTask of microTaskQueue) {    
handleMicroTask(microTask);  
}
}
复制代码

参考链接

(2)手写题:数组扁平化

function flatten(arr) {
let result = [];

for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result = result.concat(arr[i]);
}
}

return result;
}

const a = [1, [2, [3, 4]]];
console.log(flatten(a));

复制代码

手写题:实现柯里化

预先设置一些参数

学习分享,共勉

题外话,毕竟我工作多年,深知技术改革和创新的方向,Flutter作为跨平台开发技术、Flutter以其美观、快速、高效、开放等优势迅速俘获人心

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

arr.length; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result = result.concat(arr[i]);
}
}

return result;
}

const a = [1, [2, [3, 4]]];
console.log(flatten(a));

复制代码

手写题:实现柯里化

预先设置一些参数

学习分享,共勉

题外话,毕竟我工作多年,深知技术改革和创新的方向,Flutter作为跨平台开发技术、Flutter以其美观、快速、高效、开放等优势迅速俘获人心

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-lfEnSABo-1715532120650)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值