【建议收藏】2024大厂JavaScript面试题汇总,持续更新中~

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个

函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域

闭包的特性:

函数内再嵌套函数

内部函数可以引用外层的参数和变量

参数和变量不会被垃圾回收机制回收

优点:能够实现封装和缓存等

缺点:消耗内存、使用不当会内存溢出,

解决方法:在退出函数之前,将不使用的局部变量全部删除

3.说说你对作用域链的理解


作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到 window对象即被终止,作用域链向下访问变量是不被允许的。

简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期

4.JavaScript原型,原型链 ? 有什么特点?


每个对象都会在其内部初始化一个属性,就是 prototype (原型),当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这个prototype 又会有自己的 prototype ,于是就这样一直找下去,也就是我们平时所说的原型链的概念

关系: instance.constructor.prototype = instance.proto

特点:JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变当我们需要一个属性的时, Javascript 引擎会先看当前对象中是否有这个属性, 如果没有的,就会查找他的 Prototype 对象是否有这个属性,如此递推下去,一直检索到 Object内建对象

5.Javascript如何实现继承?


  • 构造继承

  • 原型继承

  • 实例继承

  • 拷贝继承

原型 prototype 机制或 apply 和 call 方法去实现较简单,建议使用构造函数与原型混合方式

function Parent(){

this.name = ‘wang’;

}

function Child(){

this.age = 28;

}

Child.prototype = new Parent();//继承了Parent,通过原型

var demo = new Child();

alert(demo.age);

alert(demo.name);//得到被继承的属性

6.JS中的垃圾回收机制


必要性:由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

这段话解释了为什么需要系统需要垃圾回收,JS不像C/C++,他有自己的一套垃圾回收机制(Garbage Collection)。JavaScript的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。例如:

var a=“hello world”;

var b=“world”;

var a=b;

//这时,会释放掉"hello world",释放内存以便再引用

垃圾回收的方法:标记清除、计数引用。

标记清除

这是最常见的垃圾回收方式,当变量进入环境时,就标记这个变量为”进入环境“,从逻辑上讲,永远不能释放进入环境的变量所占的内存,永远不能释放进入环境变量所占用的内存,只要执行流程进入相应的环境,就可能用到他们。当离开环境时,就标记为离开环境。

垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都加),然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记),删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被删除,最后垃圾回收器,完成了内存的清除工作,并回收他们所占用的内存。

引用计数法

另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值没引用的次数,当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引用次数为1,;相反的,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用值引用次数就减1,当这个值的引用次数为0的时候,说明没有办法再访问这个值了,因此就把所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为0的这些值。

用引用计数法会存在内存泄露,下面来看原因:

function problem() {

var objA = new Object();

var objB = new Object();

objA.someOtherObject = objB;

objB.anotherObject = objA;

}

在这个例子里面,objA和objB通过各自的属性相互引用,这样的话,两个对象的引用次数都为2,在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,因为计数不为0,这样的相互引用如果大量存在就会导致内存泄露。

特别是在DOM对象中,也容易存在这种问题:

var element=document.getElementById(’‘);

var myObj=new Object();

myObj.element=element;

element.someObject=myObj;

这样就不会有垃圾回收的过程。

7.函数柯里化


在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 预置通用参数,供多次重复调用。

const add = function add(x) {

return function (y) {

return x + y

}

}

const add1 = add(1)

add1(2) === 3

add1(20) === 21

8.js的防抖


防抖(Debouncing)

防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

通俗一点来说,看看下面这个简化的例子:

// 简单的防抖动函数

function debounce(func, wait, immediate) {

// 定时器变量

var timeout;

return function() {

// 每次触发 scroll handler 时先清除定时器

clearTimeout(timeout);

// 指定 xx ms 后触发真正想进行的操作 handler

timeout = setTimeout(func, wait);

};

};

// 实际想绑定在 scroll 事件上的 handler

function realFunc(){

console.log(“Success”);

}

// 采用了防抖动

window.addEventListener(‘scroll’,debounce(realFunc,500));

// 没采用防抖动

window.addEventListener(‘scroll’,realFunc);

上面简单的防抖的例子可以拿到浏览器下试一下,大概功能就是如果 500ms 内没有连续触发两次 scroll 事件,那么才会触发我们真正想在 scroll 事件中触发的函数。

上面的示例可以更好的封装一下

// 防抖动函数

function debounce(func, wait, immediate) {

var timeout;

return function() {

var context = this, args = arguments;

var later = function() {

timeout = null;

if (!immediate) func.apply(context, args);

};

var callNow = immediate && !timeout;

clearTimeout(timeout);

timeout = setTimeout(later, wait);

if (callNow) func.apply(context, args);

};

};

var myEfficientFn = debounce(function() {

// 滚动中的真正的操作

}, 250);

// 绑定监听

window.addEventListener(‘resize’, myEfficientFn);

9.js节流


防抖函数确实不错,但是也存在问题,譬如图片的懒加载,我希望在下滑过程中图片不断的被加载出来,而不是只有当我停止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。

这个时候,我们希望即使页面在不断被滚动,但是滚动 handler 也可以以一定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另一种技巧,称为节流函数(throttling)。

节流函数,只允许一个函数在 X 毫秒内执行一次。

与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件 handler。

与防抖相比,节流函数多了一个 mustRun 属性,代表 mustRun 毫秒内,必然会触发一次 handler ,同样是利用定时器,看看简单的示例:

// 简单的节流函数

function throttle(func, wait, mustRun) {

var timeout,

startTime = new Date();

return function() {

var context = this,

args = arguments,

curTime = new Date();

clearTimeout(timeout);

// 如果达到了规定的触发时间间隔,触发 handler

if(curTime - startTime >= mustRun){

func.apply(context,args);

startTime = curTime;

// 没达到触发间隔,重新设定定时器

}else{

timeout = setTimeout(func, wait);

}

};

};

// 实际想绑定在 scroll 事件上的 handler

function realFunc(){

console.log(“Success”);

}

// 采用了节流函数

window.addEventListener(‘scroll’,throttle(realFunc,500,1000));

上面简单的节流函数的例子可以拿到浏览器下试一下,大概功能就是如果在一段时间内 scroll 触发的间隔一直短于 500ms ,那么能保证事件我们希望调用的 handler 至少在 1000ms 内会触发一次。

10.说一下Commonjs、AMD和CMD


一个模块是能实现特定功能的文件,有了模块就可以方便的使用别人的代码,想要什么功能就能加载什么模块。

Commonjs:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的作用域,模块输出,modules.exports,模块加载require()引入模块。

AMD:中文名异步模块定义的意思。

requireJS实现了AMD规范,主要用于解决下述两个问题。

1.多个文件有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器

2.加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应的时间越长。

语法:requireJS定义了一个函数define,它是全局变量,用来定义模块。

requireJS的例子:

//定义模块

define([‘dependency’], function(){

var name = ‘Byron’;

function printName(){

console.log(name);

}

return {

printName: printName

};

});

//加载模块

require([‘myModule’], function (my){

my.printName();

}

requirejs定义了一个函数define,它是全局变量,用来定义模块:

define(id?dependencies?,factory)

在页面上使用模块加载函数:

require([dependencies],factory);

总结AMD规范:require()函数在加载依赖函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块加载成功,才会去执行。

因为网页在加载js的时候会停止渲染,因此我们可以通过异步的方式去加载js,而如果需要依赖某些,也是异步去依赖,依赖后再执行某些方法。

由于篇幅有限,只能分享部分面试题,更多面试题及答案可以【点击我】阅读下载哦~无偿分享给大家,算是一个感恩回馈吧

11.请解释什么是事件委托/事件代理


事件代理( Event Delegation ),又称之为事件委托。是 JavaScript 中常用的绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能

可以大量节省内存占用,减少事件注册,比如在 table 上代理所有 td 的 click 事件就非常棒

可以实现当新增子对象时无需再次对其绑定

12.事件模型


W3C 中定义事件的发生经历三个阶段:捕获阶段( capturing )、目标阶段

( targetin )、冒泡阶段( bubbling )

冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发

捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发

DOM 事件流:同时支持两种事件模型:捕获型事件和冒泡型事件

阻止冒泡:在 W3c 中,使用 stopPropagation() 方法;在IE下设置 cancelBubble =true

阻止捕获:阻止事件的默认行为,例如 click - a 后的跳转。在 W3c 中,使用preventDefault() 方法,在 IE 下设置 window.event.returnValue = false

13.new操作符具体干了什么呢?


创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型

属性和方法被加入到 this 引用的对象中

新创建的对象由 this 所引用,并且最后隐式的返回 this

14.Ajax原理


Ajax 的原理简单来说是在用户和服务器之间加了—个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后用 javascript来操作 DOM 而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据

Ajax 的过程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。 XMLHttpRequest 是ajax的核心机制

15.对象深度克隆的简单实现


function deepClone(obj){

var newObj= obj instanceof Array ? []:{};

for(var item in obj){

var temple= typeof obj[item] == ‘object’ ? deepClone(obj[item]):obj[item];

newObj[item] = temple;

}

return newObj;

}

ES5的常用的对象克隆的一种方式。注意数组是对象,但是跟对象又有一定区别,所以我们一开始判断了一些类型,决定newObj是对象还是数组~

16.将原生的ajax封装成promise


var myNewAjax=function(url){

return new Promise(function(resolve,reject){

var xhr = new XMLHttpRequest();

xhr.open(‘get’,url);

xhr.send(data);

xhr.onreadystatechange=function(){

if(xhr.status200&&readyState4){

var json=JSON.parse(xhr.responseText);

resolve(json)

}else if(xhr.readyState==4&&xhr.status!=200){

reject(‘error’);

}

}

})

}

17.实现一个once函数,传入函数参数只执行一次


function ones(func){

var tag=true;

专业技能

一般来说,面试官会根据你的简历内容去提问,但是技术基础还有需要自己去准备分类,形成自己的知识体系的。简单列一下我自己遇到的一些题

最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。

其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等

由于文章篇幅有限,仅展示部分内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值