2023前端大厂面试题之JavaScript篇(4)

本文是2023前端大厂面试题系列的JavaScript篇,涵盖严格模式、this关键字、call、apply和bind、模块化规范、延迟加载、内存管理等核心知识点,帮助面试者全面掌握JavaScript核心技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章:
2023前端大厂面试题之JavaScript篇(1)
2023前端大厂面试题之JavaScript篇(2)
2023前端大厂面试题之JavaScript篇(3)
2023前端大厂面试题之JavaScript篇(4)
2023前端大厂高频面试题之CSS篇
2023前端大厂高频面试题之Vue篇(1)
2023前端大厂高频面试题之Vue篇(2)
2023大厂高频面试题之HTTP篇
2023前端大厂高频面试题之浏览器篇
2023大厂高频面试题之操作系统篇
2023大厂高频面试题之计算机网络篇
2023大厂高频面试题之项目篇

📒博客首页:若年封尘
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
🙏作者水平很有限,如有不妥,不吝赐教!
❤️期待一起交流,共同进步!

严格模式use strict

use strict放在脚本文件的第一行,整个脚本都将以严格模式运行。放在函数体的第一行,则整个函数以严格模式运行。

取消this的自动绑定,绑定到undefined。
正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。v = 1; // 报错,v未声明
不能对只读属性赋值,会报错。例如:设置字符串的length属性,会报错。‘abc’.length = 5。
不能使用with语句。

JavaScript this 关键字

函数中的this的指向问题,一句话:运行时this永远指向最后调用它的那个对象
最常见的四种情况:
1.最简单的,如果单独使用,this 指向全局对象window

function test(){
  console.log(this); // window
}
test();

2.方法调用: 当函数作为对象的方法调用时,this 指向调用该方法的对象

const obj = {
  name: 'obj',
  func: function(){
  console.log(this); // obj
  }
}
obj.func();

3.构造函数中调用: 当使用 new 关键字创建对象实例时,this 指向新创建的对象实例

function Person(name){
  this.name = name;
}
const person = new Person('张三');
console.log(person.name); // 张三

4.绑定调用:使用call/apply/bind方法绑定this指向。

const obj = {
  name: 'obj'
};
function func(){
  console.log(this.name);
}
func.call(obj); // obj
  • 在函数中,在严格模式下,this 是undefined,因为严格模式下不允许默认绑定。
  • 箭头函数没有自己的this,继承外层上下文绑定的this。

call、apply和bind

fun.apply(thisArg, [argsArray])
第一个参数:是一个对象。 Function 的调用者,函数运行时this将会指向这个对象。如果不传,则默认为全局对象 window。
第二个参数:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。

fun.call(thisArg[, arg1[, arg2[, …]]])
第一个参数:和apply一样。
第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。但是如果将所有的参数作为数组传入,它们会作为一个整体映射到 Function 对应的第一个参数上,之后参数都为空。
call和apply类似,区别就是传入的第二个参数类型不同,call() 接受一个参数列表,而 apply() 接受一个参数的单数组。

fun.bind(thisArg[, arg1[, arg2[, …]]])
bind 和 call/apply 用处是一样的,但是 bind 会返回一个新函数!不会立即执行!而call/apply改变函数的 this 并且立即执行。

var name = 'zagiee';
function fun() {
  console.log (this.name);
}

var obj= {
  name: 'jake'
};
fun(); // zagiee
fun.call(obj);//jake
fun.apply(obj);//jake
fun.bind(obj)();//jake

js模块化规范es6moudle和CommonJS

模块化就是将系统分离成独立功能的模块,需要什么功能,就加载什么功能。
ES6 模块
语法:ES6 模块使用 import 和 export 关键字进行导入和导出。
异步加载:ES6 模块支持异步加载,可以使用动态 import() 来实现。
静态分析:模块依赖关系在编译时是静态的,这使得工具可以更容易地进行代码优化和分析。
严格模式:ES6 模块总是在严格模式下运行,无需手动添加 “use strict”;。
单一实例:每个 ES6 模块只会被加载一次,即使在多个地方导入,也只会执行一次。

CommonJS
语法:CommonJS 使用 require 和 module.exports 进行导入和导出。
同步加载:CommonJS 模块是同步加载的,导致在浏览器环境中可能会有性能问题。
运行时加载:CommonJS 模块的依赖关系是在运行时动态解析的,这使得工具难以进行静态分析。
非严格模式:CommonJS 模块默认不运行在严格模式下,需要手动添加 “use strict”;。
新实例:每次 require 一个模块时,都会返回一个新的实例,不同于 ES6 模块。

  • CommonJS 方案,它通过 require 来引入模块,通过 module.exports定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,读取非常快,所以以同步加载。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载。
  • AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。
  • CMD 方案,CMD是在AMD的基础上改进的一种规范,解决了AMD对依赖模块的执行时机的问题。
  • ES6中的Module模块,export用于暴露接口,import用于引入模块。

import 引入的是原始值还是引用

import 语句引入的是从模块中导出的值的引用,而不是它们的原始值。修改会影响到被引入的模块。

这种行为与 CommonJS 模块系统不同,CommonJS 模块系统会返回一个新的实例,每次 require 一个模块时都会生成一个新的对象。

map 和 weakMap 的区别

对象Object,是由key:value集合组成的,但key只能是字符串,有很大的使用限制。当我们需要其他类型的数据做key值时,就需要用到数据结构Map它支持把各种类型的值,当做键。
WeakMap与Map类似,但有几点区别:
1、WeakMap只接受对象作为key,如果设置其他类型的数据作为key,会报错。
2、WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。
3、由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。
4、没有clear()方法
5、不能遍历

js 延迟加载的方式

同步加载<script src="http://zagiee.com/script.js"></script>
同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像)、渲染、代码执行。
延迟加载:js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。JS延迟加载,也就是等页面加载完成之后再加载 JavaScript 文件,有助于提高页面加载速度。一般有以下几种方式:

  • 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
  • 给 js 脚本添加 defer属性<script src="test1.js" defer></script>(只适用于外部脚本文件),并行加载延迟执行,加载完成后等待 DOM 解析完成后执行加载的资源,不会打断 DOM 解析。
  • 给 js 脚本添加 async属性<script src="test1.js" async></script>,并行加载,加载完成立即执行加载的资源,会打断 DOM 解析。如果有多个 defer 脚本,会按照它们在页面出现的顺序加载;多个 async 脚本不能保证加载顺序。
  • 动态创建 script 标签(Script DOM Element),对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。在这里插入图片描述
  • 使用setTimeout延迟加载
  • onload 时的异步加载:它不是立即开始异步加载 js ,而是在 onload(页面的所有资源都加载完毕(包括图片)。浏览器的载入进度在这时才停止。)时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。
  • XHR 异步加载JS
    参考

JavaScript多态

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。比如同样属于动物类的猫和狗会有不同的叫声。

JavaScript中常见设计模式

一、单例模式
二、策略模式
三、代理模式
四、迭代器模式
五、发布—订阅模式,如vue
六、命令模式
七、组合模式
八、模板方法模式
九、享元模式
十、职责链模式
十一、中介者模式
十二、装饰者模式
十三、状态模式
十四、适配器模式
十五、外观模式
详细链接

设计原则

单一职责、最少知识、开放-封闭

冒泡和捕获

事件的传播过程(DOM事件流)的 3 个阶段:
1.先是捕获阶段:从 Window向下走近元素。
2.目标阶段:事件到达目标元素。
3.冒泡阶段:事件从元素上开始冒泡。

冒泡:当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
几乎所有事件都会冒泡,但像focus,blur,change,submit,reset,select等事件不会冒泡。
阻止冒泡:e.stopPropagation();

捕获:
从外向内发生:事件先从最不具体的document对象触发,然后逐级向内捕获到最具体的元素。

使用场景
使用事件冒泡的场景:
父元素需要处理子元素的事件,例如点击子元素时父元素也需要反应。
需要给多个元素绑定同一个事件处理函数,利用冒泡实现。
表单验证,点击提交按钮时从内向外验证各个表单项。
拖拽元素,鼠标松开时需要判断松开位置是否在元素内。
弹出框、下拉菜单等,需要判断点击位置是否在目标元素外关闭。

使用事件捕获的场景:
需要在事件到达目标元素前进行处理,例如阻止默认行为。
事件处理需要从外向内逐级进行,例如模态框遮罩层。
给多个无嵌套关系的元素绑定同一事件处理器。
需要知道事件最初触发的元素,而不是最后触发的元素。
需要控制事件在DOM树中的传播顺序和方向。
给document或其他上层元素绑定事件,提前处理事件。
需要同时给一个元素设置冒泡和捕获阶段的事件处理程序。

addEventListener(event,function,useCapture)可以监听事件的触发,来达到绑定事件的目的,原理是监听,当有事件触发的时候它就会做出相应的动作。
参数:
event:字符串,表示需要监听的事件,事件前面不用加on。例如:单击事件直接可以写成click。
function:回调函数,表示事件触发后要执行的函数。
useCapture:布尔值 true 或 false 不传的话默认为false(事件冒泡是事件在DOM树中的标准流程)。true表示监听事件的捕获阶段,false表示监听事件的冒泡阶段。没有返回值。

事件委托
要给<ul>下所有的<li>标签绑定onclick事件,除了可以用循环解决,更简单的方式是给这些<li>共同的父元素<ul>添加onclick事件,那么里面的任何一个<li>标签触发onclick事件时,都会通过冒泡机制,将onclick事件传播到<ul>上进行处理。

JS 单线程的好处

  • JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
  • 切换开销小,操作DOM必然会涉及资源的竞争。而设计成单线程,并辅以完善的异步队列来实现,那么运行成本就会比多线程的设计要小很多了。
  • 可以避免频繁的上下文切换。

路由懒加载

整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块。

按需去加载路由对应的资源,提高首屏加载速度(tip:首页不用设置懒加载,而且一个页面加载过后再次访问不会重复加载)。

实现原理:将路由相关的组件,不再直接导入了,而是改写成异步组件的写法,只有当函数被调用的时候,才去加载对应的组件内容。
具体写法

js 垃圾回收机制

自动垃圾回收机制(GC:Garbage Collecation)原理:垃圾收集器会定期(周期性)找出那些不再继续使用的变量,然后释放其内存。

垃圾回收有两种方法:标记清除、引用计数
1、标记清除

这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

2、引用计数

另一种不太常见的垃圾回收策略是引用计数。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

js释放变量内存

一旦数据不再有用,最好通过将其值设置为null来释放其引用-----这个做法叫做解除引用。这一做法适用于大多数全局变量和全局变量的属性。局部变量会在他们离开执行环境时自动被解除引用。

在Javascript是可以使用“delete”来手动删除变量,通过这样的方法让GC(Garbage collection)来回收内存,但在JS中并不是所有的变量都可以被删除的。
delete 不能删除var 关键字声明的全局变量;delete 能删除未使用var 关键字声明的全局变量;delete 能删除对象中的可读写属性。

哪些操作会造成内存泄漏

内存泄漏:应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收。
1.意外的全局变量;
JS中对没有声明的变量会默认定义在全局变量windoow上,只有关闭窗口或者刷新页面的时候,全局变量才会被释放,如果一个没有声明的变量上保存了大量的数据,这些数据就会保存在全局变量身上,如果这些数据没有及时释放就会引起内存泄露。
在 JavaScript 文件头部加上 ‘use strict’,可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。

function foo(arg) { 
    bar = "this is a hidden global variable"; //window.bar
    //函数 foo 内部忘记使用 var ,意外创建了一个全局变量。
} 
function foo() { 
    this.variable = "potential accidental global"; 
}  
// foo 调用自己,this 指向了全局对象(window) 
// 而不是 undefined 
foo(); 

2.闭包的不当使用;
3.未被清空的定时器,未被销毁的事件监听,被遗忘的回调函数;
4.脱离 DOM 的引用:获取一个DOM元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。

推荐阅读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

若年封尘

你的鼓励将是我创作的最大动力!

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

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

打赏作者

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

抵扣说明:

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

余额充值