Web前端面试题集锦。根据一位进BAT前辈的分享。很干,慎入!持续更新。。。

前端入坑。可以说互联网的三巨头是大多数互联网从业者向往的地方,我也是,从撸代码开始,目标就是BAT。嗯,就这样,前一阵子,网上看到一位前辈的帖子,很有感触。@brickspert
话不多说,全干货,上题!
1.事件代理:
只指定一个事件处理程序,就可以管理某一类型的所有事件。
原理是利用事件冒泡。
好处:减少dom操作,减少内存占用,提高浏览器性能。
如果要指定某个元素标签触发,可以利用event.target来表示当前事件操作的dom,但这个不是真正操作dom。
可以结合switch-case使用。
适合用事件代理的事件:click,mouse down,mouseup,keydown,keyup,keypress

2.解释JavaScript中this是如何工作的
this永远指向函数运行时所在的对象,,而不是函数创建时所在的对象
匿名函数和不处于任何对象中的函数,this指向window
call,apply,bind指定this是谁就是谁
普通函数,函数被谁调用,this就指向谁
使用new操作符,this指向对象实例

3.闭包
闭包就是能够读取其他函数内部变量的函数,可以理解成定义在一个函数内部的函数。
用途:
(1)读取函数内部的变量
(2)让这些变量的值始终保持在内存中
注意:
(1)会导致内存泄漏
(2)闭包会修改内部变量的值,所以在使用闭包作为对象的公用方法时要谨慎

4.JavaScript继承
创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。所有属性和方法都是公用的。因此子类可直接访问这些方法。子类还可添加超类没有的属性和方法,也可以覆盖超类的属性和方法。
继承方式:
(1)对象冒充:构造函数使用this关键字给所有属性和方法赋值。因为构造函数只是一个函数,所以可使classA构造函数成为classB的方法,这样classB就有了classA定义的属性和方法。
所有新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法!
注:对象冒充可以继承多个超类。
(2)call()和apply()方法
(3)原型链:把classB的prototype属性设置成classA的实例
注意:调用classA的构造函数,没有给他传递参数,这在原型链中是标准做法,要确保构造函数没有任何参数。
子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除 弊端是不支持多重继承。
原型链的原理是用另一类型的对象重写类的prototype属性
(5)混合方式:用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法

5.JavaScript模块化
模块化设计用于模拟类的概念(因为js本身不支持类),因此我们可以将共有和私有方法和变量存储在单个对象中。
实现方法:
(1)匿名闭包
(function(){
//代码
}())
这种方法的好处在于,你可以在此函数内部使用局部变量,而不会意外覆盖现有的全局变量,但仍然可以访问全局变量
(2)全局导入
(3)接口对象
(4)解开模块模式
(5)CommonJS:采用服务器优先方式并同步加载模块。只支持对象模块
(6)AMD:在后台加载(以非阻塞的方式)。采用的是浏览器优先的方法和异步行为来完成加载工作。支持多种模块
(7)UMD:判断不同环境,使用不同的格式封装模块
(8)ES6
优点:
可维护性;命名空间;可重用性

6.立即执行函数(IIFE)
(function(){
//代码
}())
解析器对代码进行解析的时候,先碰到了(),然后碰到了function关键字,就会自动将()里面的代码识别为函数表达式而不是函数声明。
特点: 具有私有性
避免了向全局作用域中添加变量和函数
IIFE中定义的任何变量和函数都会在执行结束时被销毁。这样可以减少闭包占用的内存问题。

7.null和undefined的区别
Null表示没有对象,即该处不应该有值。典型用法是:
(1)作为函数的参数,表示该函数的参数不是对象。
(2)作为对象原型链的终点
Undefined表示缺少值,就是此处应该有一个值,但是还没有定义。典型用法:
(1)变量被声明了,但没有赋值时,就等于undefined
(2)调用函数时,应该提供的参数没有提供,该参数等一undefined
(3)对象没有赋值的属性,该属性的值为undefined
(4)函数没有返回值时,默认返回undefined

8.匿名函数
定义函数的方式有两种,一种是函数声明,一种是函数表达式(匿名函数)

9.宿主对象 (host objects) 和原生对象 (native objects)
原生对象(包含内置对象):独立于宿主环境的ECMAScript实现提供的对象。在运行过程中动态创建的对象,需要new。
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、Global
内置对象:Global(全局对象)、Math
宿主对象:ECMAScript官方未定义的对象都属于宿主对象,

10.call,apply,bind
三者都是改变this的指向
Apply和call作用是一样的只是接受的参数有区别。Call是把参数一个一个传递进去,apply则是将多个参数包装成数组或对象当成一个参数传入。
两者都是立即执行的
用法:
(1)可以让类(伪)数组使用数组方法
(2)验证一个对象的类型
Object.prototype.toString.call(obj)
Bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为this,传入bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
用bind()方法并不会立即执行,而是创建一个新函数,如果要直接调用可以:
Var func = bar.bind(foo)()
在JavaScript中,多次bind()是无效的。

11.new操作符
使用new操作符创建一个实例,会经历以下四个步骤:
(1)创建一个对象
(2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
(3)执行构造函数中的代码(为这个新对象添加属性)
(4)返回新对象
Var obj = new Object()
实际上是:
Var obj = {}
Obj.proto = Object.prototype
Object.call(obj)

12.document.write()
他能够直接在文档流中写入字符串,如果文档流是关闭状态,它会重新开启文档流并写入,此时原来的文档流会被清空,浏览器将重新构建DOM并渲染新的页面
不到迫不得已不要使用

13.ajax原理
通过XMLHttpRequest对象来向服务器发送异步请求,从服务器获得数据,然后用JavaScript来操作DOM而更新页面。
IE使用的是ActiveXObject对象
首先,new一个XMLHttpRequest对象xmlhttp
Xmlhttp.open(“GET还是POST”,”请求URL”,是否异步)
这是准备阶段,然后调用send()方法发送出去
在这两个方法之间可以设置请求头setRequestHeader()
服务器收到请求进行处理,通过response对象发回去。
客户端根据xmlhttp的readyState和status属性判断请求是否成功。然后调用xmlhttp里的resposeText来拿到返回内容。
其中,response类型有两种,xml和字符串

14.跨域
(1)通过jsonp跨域
原理是在页面上引入不同域上的js脚本文件。Js文件载入成功后会执行我们在url参数中指定的函数,并且会把需要的json数据作为参数传入,所以jsonp是需要服务器端的页面进行相应的配合。
URL?callback=dosomething
在获取数据的地址后面加一个callback参数
(2)window.postMessage方法
可以使用它来向其他的window对象发送消息。
调用postMessaage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串,第二个参数是限定接收消息的域
(3)使用window.name来进行跨域
(4)图像ping
(5)Cors:通过这种机制设置一系列的响应头,这些响应头允许浏览器与服务器进行交流,实现资源共享。
(6)Document.domain
(7)服务器代理

15.变量声明提升
在JavaScript中,因为没有块级作用域,函数和变量的声明都将被提升到函数的最顶部。
在代码执行之前,会对作用域链中所有变量和函数声明进行处理。
总结来说,就是声明被提升,而赋值或运算会停在原地

16.冒泡机制
当子元素事件触发,事件会沿着包含关系,往上级传递,每一级都可以感知到事件
阻止冒泡的方法:
(1)在相应的处理函数中,加入event.stopPropagation(),终止事件广播
(2)event.target引用了产生此event对象的dom节点,而event.currentTarget则引用了当前处理节点,可以通过两个target是否相等来决定是否处理事件。
利用事件委托和switch-case结合可以很好的处理事件冒泡

17.attribute和property的区别
Attribute是HTML标签上的特性,它的值只能够是字符串。如:class,id,title
Property是DOM的属性,是js里的对象。如:childNodes,firstChild
Attribute的值改变会同步到property中,反之则不会。
两者的值发生改变,都会将更新映射到HTML页面中
常用的Attribute,例如id、class、title等,已经被作为Property附加到DOM对象上,可以和Property一样取值和赋值。但是自定义的Attribute,就不会有这样的特殊优待
用setAttribute()和getAttribute()来操作
Property用”.“就可以了

18.document load 和 document DOMContentLoaded
当onload事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
当DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。
DOM文档加载步骤:
(1)解析HTML结构
(2)加载外部脚本和样式表文件
(3)解析并执行脚本代码
(4)DOM树构建完成。//DOMContentLoaded
(5)加载图片等外部文件
(6)页面加载完成。//load
在用jquery的时候,我们一般都会将函数调用写在ready方法内,就是页面被解析后,我们就可以访问整个页面的所有dom元素,可以缩短页面的可交互时间,提高整个页面的体验。

19.为什么将css放在头部,将js文件放在尾部可以优化页面?
因为浏览器生成DOM树的时候是一行一行读HTML代码的,script标签会阻塞DOM的解析,放在最后面就不会影响前面的页面的渲染

20.同源策略(same-origin policy)
同源是指文档的来源相同,主要包括:协议,主机(域名),载入文档的URL端口
同源策略主要是出于安全性考虑。
注意:同源策略限制的是脚本嵌入的文本来源,而不是脚本本身。
非同源,共有三种行为受到限制:
(1)cookie,localStorage和IndexDB无法读取
(2)DOM无法获取
(3)AJAX请求不能发送

21.strict严格模式
使用严格模式可以:
消除代码运行的一些不合理,不严谨之处,减少一些怪异行为
提高编译器效率,增加运行速度
为未来新版本的js做铺垫
严格模式的限制:
不允许使用未声明的变量(对象也是一个变量)
不允许删除变量或对象
不允许删除函数
不允许变量重名
不允许使用八进制
不允许使用转义字符
不允许对只读属性赋值
不允许对一个使用getter方法读取的属性进行赋值
不允许删除一个不允许删除的属性
变量名不能使用”eval“”arguments”字符串
禁止this关键字指向全局对象
“use strict”指令只允许出现在脚本或函数的开头

22.什么是单页面应用,以及如何使其对搜索引擎友好seo-friendly
就是指一个系统只加载一次资源,之后的操作交互,数据交互是通过路由,ajax来进行的,页面没有刷新。
方法:
(1)注重TDK的写法:既然页面单一,那么就更需要注重网站标题,关键词和描述的写法。标题要简单明确,包含主要关键字。描述要提炼精髓。关键字用绝对与内容相关的词。
(2)合理运用网站标签:比如h标签,strong标签
(3)代码优化:能用CSS就尽量不用JS,毕竟JS对搜索引擎并不友好。
(4)图片优化
(5)内容为王

23.Promise
Promise相当于一个中间层,第一方业务和第三方库都由promise来调用,进而在promise中解决异步编程中可能出现的各种问题。
resolve/reject:在异步操作完成时由第三方库调用,从而改变promise的状态
Fulfilled(成功)/rejected(失败)/peding(进行中):标识了一个promise当前的状态
then/done:作为promise暴露给第一方代码的接口

24.JavaScript调试工具
(1)alert()
(2)conssole.log()
(3)Debugger关键字暂停代码执行
(4)Chrome开发者工具

25.对象遍历和数组遍历
数组遍历:
(1)for循环(可以使用变量将长度缓存起来,避免重复获取长度,数组很大时优化明显)
(2)ForEach:数组自带的循环
(3)Map遍历:用法与foreach类似
(4)for-of遍历:ES6新增功能
对象遍历:
(1)for-in
(2)Object.keys().forEach()
(3)Object.getOwnPropertyNames().forEach()

26.可变对象和不可变对象
可变对象:把对象a赋值给对象b,更改对象b的属性值,被引用的对象a也随之改变,这就是可变对象。
JavaScript中7种基本数据类型:number,string,Boolean,null,undefined,object,以及ES6戏赠的symbol。除了object时引用类型,其余都是基本类型。
基本类型将值存在栈中,引用类型将值存在堆中,栈中的值是一个指向堆的指针。
对于基本类型,即使赋值了两者改变互不影响。而对于引用类型,赋值的只是指针,两者其实都是指向堆中相同的内容,所以改变会同步。这也就是浅拷贝。
所以为了使两个引用类型互不影响,一般我们不直接把旧的obj赋值给新的obj,而是生成新的引用类型变量,然后将原先对象的属性值一个个复制填充到新的引用类型变量里,这就是所谓的深拷贝。

不可变对象
Js提供了一些原生的方法将可变对象变成不可变对象:
object.preventExtensions(不可扩展),object.seal(密封),object.freeze(冻结)

27.什么是事件循环(event loop)
Js代码是在执行栈中运行的,运行过程中,如果遇到异步代码(如setTimeout,Promise,ajax等),浏览器会将这些代码放到一个幕后线程中去等待,不阻塞主线程的执行,主线程继续走,当幕后线程准备好了,会将它的回调函数放到任务队列中等待执行。当主线程执行完所有代码后,它就会回头检查任务队列是否有任务要执行,如果有就将任务放到执行栈中执行,如果没有,它就会进入循环等待任务到来。这就是事件循环
当任务队列有多个任务,执行顺序问题:
其实js有两个任务队列:
Macrotask Queue:主要是处理宏任务(如seTimeout,setInterval,用户交互操作,UI渲染等)
Microtask Queue:主要处理微任务(如Promise,process.nextTick(nodejs))
执行顺序是先检查两个队列,先从Macrotask队列执行等待最长的任务,然后执行Microtask队列等待最长的,依次循环执行。

28.var let const
Var :定义的变量可以修改,如果不初始化会输出undefined,不会报错,变量声明会提升
Let:是仿了一个块级作用域,函数内部使用let定义,对函数外部无影响,而且不会提升变量声明
Const:主要用来定义常量,意思是定义的变量不可以修改,而且必须进行初始化

29.数组的方法大全
(1)创建数组的两种方式:Array构造函数,数组字面量
(2)Join:将数组的元素组成一个字符串,默认以都好分隔,可以接收一个分隔符作为参数
(3)Push():末尾添加,返回数组长度
(4)pop():末尾移除,返回移除项
(5)Shift():删除原数组第一项,返回删除元素
(6)Unshift():将参数添加到数组开头,返回数组长度
(7)Sort():按升序排列数组(一般接受一个比较函数作为参数)
(8)reverse():反转数组项的排序
(9)Concat():将参数添加到原数组中,
(10)Slice():返回从原数组中指定开始下标到结束下标之间的项组成的新数组
(11)Splice():可以实现删除,插入和替换。当有两个参数时,就是删除。插入需要至少三个参数:起始位置,删除0项,插入数据。替换也至少三个参数:起始位置,要删除的项,要插入的项
(12)index Of()和lastIndexOf()
(13)forEach()
(14)Map()
(15)Filter()
(16)Every():判断数组中每一项都是否满足条件,只有所有项都满足条件才会返回true
(17)Some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true
(18)Reduce()和reduceRight():这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。

30.Web worker
Web worker时运行在后台的JavaScript,不会影响页面的性能。作用就是为JavaScript创造多线程环境,允许主线程创建worker线程,将一些任务分配给后者运行。在主线程运行的同时,worker线程在后台运行,两者互不干扰。
注意点:
(1)同源限制:分配给worker线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM限制:worker线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的DOM对象,也无法使用document,window,parent这些对象,但可以使用navigator对象和location对象。
(3)通信联系:worker线程和主线程不在同一个上下文环境,他们不能直接通信,必须通过消息完成。
(4)脚本限制:worker线程不能执行alert()和confirm()方法,但可以使用XMLHttpRequest对象发出ajax请求
(5)文件限制:无法读取本地文件,它所加载的脚本必须来自网络

31.柯里化
即Currying的音译,是编译原理层面实现多参函数的一个技术。
为实现多参函数提供了一个递归降解的实现思路–把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数而且返回结果的新函数。
作用:参数复用,延迟计算,动态生成函数

32.三种创建对象的方式
(1)字面量:Var person = {}
(2)构造函数:function Person(){};Var person = new Person()
(3)object方式创建。Var person = new Object(); person.name = “”

33.深拷贝和浅拷贝
实现深拷贝:
(1)通过递归
Function deepCLone(a){
Let b ;
For(key in a){
If(a.hasOwnProperty(key){
If(a[key]&&typeof a[key] === “object”{
B[key] = deepClone(a[key])
}else{
B[key] = a[key]
}
}
}
}

(2)借用JSON对象的parse和stringify:b=JSON.parse(JSON.stringify(a))
(3)Jquery的extend方法

34.箭头函数和普通函数的区别
箭头函数:
相当于匿名函数,并且简化了函数定义;
不能作为构造函数,不能用new
不绑定arguments,用rest参数…替代
不绑定this,会捕获其所在的上下文的this值,作为自己的this值
通过call()或apply()调用函数,只传入一个参数,无法改变this指向。
没有原型属性

你可能感兴趣的:(JS面试题)