电话:400-718-6889
关闭
您当前的位置:首页 > 职场资讯 > 前端面试秘籍

JavaScript面试题相关专题

来源:商丘人才网_商丘招聘网_商丘人才市场_豫商人才网招聘平台 时间:2021-05-31 作者:Admin 浏览量:

(1)什么是闭包: 闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。

(2)为什么要用: 匿名自执行函数:我们知道所有的变量,如果不加上 var 关键字,则默认的会添加到全 局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误 用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链 上遍历的)。除了每次使用变量都是用 var 关键字外,我们在实际情况下经常遇到这样一 种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。 结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象, 每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函 数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如 果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部 的引用,从而函数内部的值可以得以保留。 封装:实现类和继承等。

(3)闭包形成的条件:

1.函数的嵌套

2.内部函数引用外部函数的局部变量,延长外部函数的变量生命周期

(4)闭包的应用:

1、模仿块级作用域。

2、保存外部函数的变量。

3、封装私有变量。闭包的两个场景,闭包的两大作用:保存/保护

(5)闭包的优点: 延长局部变量的生命周期,

(6)闭包的缺陷:

1.闭包的缺点就是常驻内存会增大内存使用量,并且使用不当容易造成内存泄漏

2.如果不是因为某些特殊任务而需要闭包,在没有必要的情况下,在其它函数中创建函数是不明智的,因为闭包对脚本性能具有负面影响,包括处理速度和内存消耗。

说一下类的创建和继承

(1)类的创建(es5):new 一个 function,在这个 function 的 prototype 里面增加属性和 方法。 下面来创建一个 Animal 类: // 定义一个动物类

function Animal (name) {

// 属性

this.name = name || 'Animal';

// 实例方法

this.sleep = function(){

console.log(this.name + '正在睡觉!');

}

}

// 原型方法

Animal.prototype.eat = function(food) {

console.log(this.name + '正在吃:' + food);

};

这样就生成了一个 Animal 类,实力化生成对象后,有方法和属性。

(2)类的继承——原型链继承 --原型链继承

function Cat(){ }

Cat.prototype = new Animal();

Cat.prototype.name = 'cat';

// Test Code

var cat = new Cat();

console.log(cat.name);

console.log(cat.eat('fish')); console.log(cat.sleep());

console.log(cat instanceof Animal); //true

console.log(cat instanceof Cat); //true

介绍:在这里我们可以看到 new 了一个空对象,这个空对象指向 Animal 并且 Cat.prototype 指向了这个空对象,这种就是基于原型链的继承。 特点:基于原型链,既是父类的实例,也是子类的实例 缺点:无法实现多继承

(3)构造继承:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给 子类(没用到原型)


function Cat(name){

Animal.call(this);

this.name = name || 'Tom';

}

// Test Code

var cat = new Cat();

console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false

console.log(cat instanceof Cat); // true

特点:可以实现多继承 缺点:只能继承父类实例的属性和方法,不能继承原型上的属性和方法。

(4)实例继承和拷贝继承 实例继承:为父类实例添加新特性,作为子类实例返回 拷贝继承:拷贝父类元素上的属性和方法 上述两个实用性不强,不一一举例。

(5)组合继承:相当于构造继承和原型链继承的组合体。通过调用父类构造,继承父 类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Cat(name){

Animal.call(this);

this.name = name || 'Tom';

}

Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; // Test Code

var cat = new Cat();

console.log(cat.name);

console.log(cat.sleep());

console.log(cat instanceof Animal); // true

console.log(cat instanceof Cat); // true

特点:可以继承实例属性/方法,也可以继承原型属性性/方法 缺点:调用了两次父类构造函数,生成了两份实例

(6)寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的 构造的时候,就不会初始化两次实例方法/属性


function Cat(name){

Animal.call(this);

this.name = name || 'Tom';

}

(function(){

// 创建一个没有实例方法的类

var Super = function(){};

Super.prototype = Animal.prototype;

//将实例作为子类的原型

Cat.prototype = new Super();

})();

// Test Code

var cat = new Cat();

console.log(cat.name); console.log(cat.sleep());

console.log(cat instanceof Animal); // true

console.log(cat instanceof Cat); //true

说说前端中的事件流

HTML 中与 javascript 交互是通过事件驱动来实现的,例如鼠标点击事件 onclick、页面 的滚动事件 onscroll 等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。 想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。 什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2 级事件流包括下面几个 阶段。

事件捕获阶段

处于目标阶段

事件冒泡阶段

addEventListener:addEventListener 是 DOM2 级事件新增的指定事件处理程序的操作, 这个方法接收 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最 后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示 在冒泡阶段调用事件处理程序。 IE 只支持事件冒泡。


如何让事件先冒泡后捕获

在 DOM 标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对 于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓 执行,直到冒泡事件被捕获后再执行捕获之间。


说一下事件委托

事件委托指的是,不在事件的发生地(直接 dom)上设置监听函数,而是在其父 元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判 断事件发生元素 DOM 的类型,来做出不同的响应。


JS 的 new 操作符做了哪些事情

new 操作符新建了一个空对象,这个对象原型指向构造函数的 prototype,执行构造函数 后返回这个对象。


改变函数内部 this 指针的指向函数(bind,apply,call 的区别)

通过 apply 和 call 改变函数的 this 指向,他们两个函数的第一个参数都是一样的表示要 改变指向的那个对象,第二个参数,apply 是数组,而 call 则是 arg1,arg2…这种形式。通 过 bind 改变 this 作用域会返回一个新的函数,这个函数不会马上执行。


JS 拖拽功能的实现

首先是三个事件,分别是 mousedown,mousemove,mouseup 当鼠标点击按下的时候,需要一个 tag 标识此时已经按下,可以执行 mousemove 里面的 具体方法。 clientX,clientY 标识的是鼠标的坐标,分别标识横坐标和纵坐标,并且我们用 offsetX 和 offsetY 来表示元素的元素的初始坐标,移动的举例应该是: 鼠标移动时候的坐标-鼠标按下去时候的坐标。 也就是说定位信息为: 鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的 offetLeft. 还有一点也是原理性的东西,也就是拖拽的同时是绝对定位,我们改变的是绝对定位条 件下的 left 以及 top 等等值。 补充:也可以通过 html5 的拖放(Drag 和 drop)来实现


Ajax 解决浏览器缓存问题

在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,“0”)。 在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,“no-cache”)。 在 URL 后面加上一个随机数: “fresh=” + Math.random()。 在 URL 后面加上时间搓:“nowtime=” + new Date().getTime()。 如果是使用 jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有 ajax 都会执行这条语句就是不需要保存缓存记录。


JS 的节流和防抖

防抖:短时间内多次触发同一个事件,只执行最后一次,或者在开始时执行,中间不执行。比如公交车上车,要等待最后一个乘客上车


节流:是连续触发事件的过程中以一定时间间隔执行函数。节流会稀释你的执行频率,比如每间隔1秒钟,只会执行一次函数,无论这1秒钟内触发了多少次事件

参考回答: http://www.cnblogs.com/coco1s/p/5499469.html


JS 中的垃圾回收机制

1.JS具有自动垃圾收集的机制

2.JS的内存生命周期(变量的生命) 1.分配你所需要的空间 var a = 20 2.使用分配带的内存(读写) alert(a + 10)

3.不适用的时候,释放内存空间 a = null 3.JS的垃圾收集器每隔固定的时间就执行一次释放操作,通用的是通过标记清除的算法

4.在局部作用域中,垃圾回收器很容易做出判断并回收,全局比较难,因此应避免全局变量 标记清除算法:js最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将他标记为’进入环境’, 当变量离开(函数执行完后),就其标记为’离开环境’。垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记, 然后去掉环境中的变量以及被环境中该变量所引用的变量(闭包)。在这些完成之后仍存在标记的就是要删除的变量了


如何理解前端模块化

前端模块化就是复杂的文件编程一个一个独立的模块,比如 JS 文件等等,分成独立的 模块有利于重用(复用性)和维护(版本迭代),这样会引来模块之间相互依赖的问题, 所以有了 commonJS 规范,AMD,CMD 规范等等,以及用于 JS 打包(编译等处理)的 工具 webpack。

对象深度克隆的简单实现

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 是对象还是数组。


==和===、以及 Object.is 的区别

(1)==主要存在:强制转换成 number,null==undefined ==0 //true

“0==0 //true

!=“0” //true

123==123” //true

null==undefined //true

(2)Object.js 主要的区别就是+0!=-0 而 NaN==NaN (相对比===和==的改进)


自己实现一个 bind 函数

原理:通过 apply 或者 call 方法来实现。

(1)初始版本


Function.prototype.bind=function(obj,arg){

var arg=Array.prototype.slice.call(arguments,1);

var context=this;

return function(newArg){

arg=arg.concat(Array.prototype.slice.call(newArg));

return context.apply(obj,arg);

}

}


(2) 考虑到原型链 为什么要考虑?因为在 new 一个 bind 过生成的新函数的时候,必须的条件是要继承原 函数的原型


Function.prototype.bind=function(obj,arg){

var arg=Array.prototype.slice.call(arguments,1);

var context=this;

var bound=function(newArg){

arg=arg.concat(Array.prototype.slice.call(newArg));

return context.apply(obj,arg);

}

var F=function(){} //这里需要一个寄生组合继承

F.prototype=context.prototype; bound.prototype=new F();

return bound;

}

分享到:
下一篇: web端网络安全
相关推荐
暂无相关推荐
微信公众号
手机浏览

Copyright © 2019 All Rights Reserved 版权所有 豫商人才网 豫ICP备19004769

地址:河南省商丘市睢阳区神火大道176号联合大厦13楼 EMAIL:postmaster@37jobs.com

用微信扫一扫