JS 面试题答案整理
来自于某个git上的题,我忘了题从哪来的了。公司整理面试题答案的时候,下面是我负责的部分
1. 请解释事件代理(event delegation)
描述:当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。也可以称为事件委托。
这主要得益于DOM2中的事件冒泡机制(最好要答出)
益处:需要创建的以及驻留在内存中的事件处理器少了,节省内存,提升性能。
追问:用过吗?是怎么使用的?获取当前目标元素?
2. 请解释 JavaScript 中 this
是如何工作的
具体分为几种情形
函数被调用,被谁调用那函数中的 this 就是谁,没有调用者就是 window。所以自执行函数的 this 也是 window
function fn() { console.log(this) } fn() // this -> window const obj = { name: 'xxx', getName: function () { console.log(this); return this.name } } obj.getName() // this-> obj
箭头函数中的 this 是外部作用域的 this,解决了之前要缓存 this 的弊端。
const outer = function outer () { console.log(this); return function inner () { console.log(this) } } const obj = { outer } obj.outer()()
const outer = function outer () { console.log(this); return () => console.log(this) } const obj = { outer } obj.outer()()
给元素绑定事件方法,方法中的this是当前绑定的元素。
构造函数中,this绑定到当前创建的对象实例。
使用 apply 或 call 调用 this 将会被显式设置为函数调用的第一个参数。bind 也可,属于预处理 this
3. 请解释原型继承(prototypal inheritance)的原理
答:主要用到了原型链查找。
每个函数F都有一个原型对象 F.prototype
,原型会定义F类的公用方法或者属性。
当我们new F 创建一个实例o的时候,会给o添加一个__proto__属性,通过__protp__会找到 F.prototype,也就是所属类的原型。
当我们通过o访问一个属性的时候,比如o.name,会先在实例o上查找,没有的话js会通过__proto__去类的原型上找,由于原型也是一个对象,它也有__proto__属性,默认会找到Object的原型。所以,当我们的Child类想通过继承访问Super类上的属性/方法,可以通过设置Child的原型,能访问到Super的原型,就可以访问Super类的公用属性和方法了。
例如,
Child.prototype = new Super;
把Child的原型指向Super的一个实例s,Child的实例o通过访问原型,能找到s,s通过__proto__可以找到它所属类的原型,也就是Super.prototype ,也就能访问Super类上的共有属性/方法。
当然这只是一种方式,还有其他方式,这里不再赘述。
4. 你怎么看 AMD vs. CommonJS?
答:
1、CommonJS 规范是为了解决 JavaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行。该规范的主要内容是,模块必须通过 module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中,module标识模块本身。
(function (module, exports){})(module, module.exports)
require是全局定义的,立即执行函数提供 module 和 exports 两个外部变量,模块就放在这个立即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。
nodejs就是参照CommonJS的规范实现了自己的模块化系统,不过它的自执行函数提供外部变量被内置了,所以我们是看不到的。
当我们需要用到某个依赖的模块的时候,直接require进来,就可以用了,也就是“依赖就近”,用到再引入模块。
另外由于commonJS更多的是运行在服务器端,代码都在本地,直接从硬盘读取,所以它是同步加载模块代码的,速度不会受影响。
2、服务端的模块概念形成,浏览器客户端的模块,在“不能采用同步加载”,“需要异步加载”的背景下,ADM规范诞生了。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。
当我们写的当前模块要依赖某些模块的时候,就需要文件开头这么写,先引入依赖的模块,也就是“依赖前置”。
5. 请解释为什么接下来这段代码不是 IIFE(立即调用的函数表达式):function foo(){ }();
,要做哪些改动使它变成 IIFE?
答:这个 foo 函数会被污染。没有独立隔绝的定义域,并不能防止全局命名空间被污染。
立即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但写在内使得整个表达式看起来更像一个整体,因此推荐这么做。
(function foo (){ 'use strict'; //code }())
如果你想引用全局变量或者是外层 IIFE 的变量,可以通过下列方式传参:
(function($, w, d){ 'use strict'; $(function() { w.alert(d.querySelectorAll('div').length); }); }(jQuery, window, document));
6. 描述以下变量的区别:null
,undefined
或 undeclared
?该如何检测它们?
答:
null 变量声明了然后赋值为null,是一个空的指针引用
undefined 是变量声明了但是还没有赋值,使用的时候就是undefined
undeclared 是没有声明,也就是没使用var关键字,如果赋值的话,会被创建于global object中,没赋值的话直接报语法错误,not defined。不同null,undefined是语言类型, undeclared是语法错误。
检测,typeof 就够用了。typeof(variable) === 'null' | typeof(variable) === 'undefined',undeclared 属于语法错误,'use strict' 下会避免此类语法错误。
7.什么是闭包(closure),如何使用它,为什么要使用它?
答:
闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。
通过js中函数拥有独立作用域的特性,在内部定义的变量不会被外部侵染,但是我们可以通过暴露接口,使外部可以访问,以及修改,当外部引用此函数内部的变量的时候,这个函数是不会被销毁收回的。
一种使用例子
var name = 'window' function closure() { var name = 'init'; return { get_name: function() { return name }, set_name: function(value) { name = value; return name; } } } var result = closure() console.log(result.get_name()); result.set_name('syj'); console.log(result.get_name());
为什么?js 语言特有的 链式作用链,函数内部可以直接访问外部变量,但是在函数外部无法访问函数内部变量,所以闭包的主要作用就是间接访问函数的内部数据。
8. 请举出一个匿名函数的典型用例?
function func() { return function() { } }
const func = function(){}
(function(){ })()
9. 你是如何组织自己的代码?是使用模块模式,还是使用经典继承的方法?
答:open。 模块模式,一个IIFE就算是一个模块,互相之间不影响。
var jspy = (function() { var _count = 0; var incrementCount = function() { _count++; } var getCount = function() { return _count; } return { incrementCount: incrementCount, getCount: getCount }; })();
继承在react跟node中也是比较常用的。
10.请指出 JavaScript 宿主对象 (host objects) 和原生对象 (native objects) 的区别?
答:
宿主对象是指DOM和BOM等由宿主框架通过某种机制注册到JavaScript引擎中的对象。原生对象是Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、Math等ECMAScript官方定义的对象。
11.请指出以下代码的区别:function Person(){}
、var person = Person()
、var person = new Person()
?
答:
第一个是定义了一个函数Person
第二个是把Person当普通函数执行,并把返回值赋值给person。
第三个是当做构造函数,通过new关键字创建一个实例对象,赋值给person
12..call
和 .apply
的区别是什么?
答:传的参数不一样。比如一个函数 func 通过 call 绑定 this,并给 func 传参,是这样
func.call(this,arg1,arg2,arg3,...)
apply 的话,第二个参数支持数组,以及类数组,apply会把数组的每一项都以参数的形式传给func,并绑定this为第一个参数,执行。
更多的是apply的应用方面,不过es6的...
也可以起到把数组每项me一个个传参的作用
13. 请解释 Function.prototype.bind
?
答:Function原型上定义的方法,所有函数都可以访问使用。主要应用场景在绑定函数执行的this,并返回一个新函数,可以在我们要调用的时候才执行。原理是函数的预处理思想,把this当做参数预置。
14. 在什么时候你会使用 document.write()
?
答:
加载需要配合 JS 脚本使用的外部 CSS 文件
<scirpt> document.write('<link rel="stylesheet" href="style_neads_js.css">'); </script>
将所有需要用到JS的样式都放到这个外部样式表中,如果浏览器不禁用JS,那么该样式表就会被顺利加载,否则页面就不会使用该样式。
在新的窗口中写入新的页面数据时
由于document.write会重写整个页面,异步调用会影响本页面的文档,如果在新窗口空白页调用,就没影响了。新开一个窗口,把本页面取到的数据在新窗口展示。。
document.open(); document.write('anthing') document.close();
由于带来的弊端较多,一般不建议使用。要对DOM进行操作时,还是应当使用安全且对DOM的友好的API方法,以避免不必要的问题出现。
15. 请指出浏览器特性检测,特性推断和浏览器 UA 字符串嗅探的区别?
特性检测更适合针对实现了特定特性的浏览器进行操作。UA字符串由于被浏览器厂商可以随意修改因此不太靠谱。
16. 请尽可能详尽的解释 Ajax 的工作原理
Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像—些数据验证和数据处理等都交给Ajax引擎自己来做, 只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。
Ajax 其核心有JavaScript、XMLHTTPRequest、DOM对象组成,通过XmlHttpRequest对象来向服务器发异步请求,然后xhr对象提供了一系列属性,可以监听响应的情况,然后从服务器获得数据,再用JavaScript来操作DOM而更新页面。
17.使用 Ajax 都有哪些优劣?
优势
- 无刷新在页面与服务器通信,更新页面,用户体验好。
- 异步与服务器通信,不需要打断用户的操作,具有更加迅速的响应能力。
- 前端和后端负载平衡。可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
- 界面与应用分离Ajax使WEB中的界面与应用分离(也可以说是数据与呈现分离),有利于分工合作、减少非技术人员对页面的修改造成的WEB应用程序错误、提高效率、也更加适用于现在的发布系统。
- 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
缺点:
- AJAX干掉了Back和History功能,即对浏览器机制的破坏。
- AJAX的安全问题
Ajax技术就如同对企业数据建立了一个直接通道,这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。Ajax也难以避免一些已知的安全弱点,诸如跨站点脚步攻击、SQL注入攻击和基于Credentials的安全漏洞等等 - 对搜索引擎支持较弱。
- 客户端过肥,太多客户端代码造成开发上的成本。
- 违背URL和资源定位的初衷,采用了Ajax技术,也许你在该URL地址下面看到的和我在这个URL地址下看到的内容是不同的。
18. 请解释 JSONP 的工作原理,以及它为什么不是真正的 Ajax
答:JSONPS 是利用 <script> 标签没有同源策略的限制(算是一个漏洞),来达到与第三方通信,从而实现跨域。
我们通常使用js代码动态创建一个script标签,src引用第三方api的地址,并提供一个回调函数的function name,例如http://www.smartstudy.com/api?callback=functionName
,query的key是约定好的,比如这里就叫callback,functionName是我们前端定义好的函数,后端通过query取到函数名,并把你所需要的数据已参数的形式传给这个函数,返回给浏览器。浏览器解析服务器返回数据,functionName函数,参数为资源数据。
JSONP并不使用XMLHttpRequest对象加载资源,而是通过script标签把资源当做普通的javascript脚本来加载,所以不存在跨域问题,也不是真正的AJAX。
19. 你使用过 JavaScript 模板系统吗?
如有使用过,请谈谈你都使用过哪些库?
答:top5
- Mustache
- Underscore Templates
- Embedded JS Templates(ejs)
- HandlebarsJS
- Jade templating
20.请解释变量声明提升 (hoisting)
作用域内所有变量声明都被提到顶部,被提升的变量初始值为 undefined,执行到所在行时才真正赋值。
21.请描述事件冒泡机制 (event bubbling)。
W3C在DOM2 中添加了一个事件模型,DOM 事件流存在三个阶段,事件捕获,目标阶段,冒泡阶段。也就是当事件触发的时候,浏览器会从根节点开始,又外到内的进行事件传播,直到目标元素,如果在此捕获阶段绑定事件,都会触发。然后从目标元素,事件又会沿着当前元素的包含关系,由内而外的传递,每一级都可以感知到事件触发。通过 DOM2 中的 addEventListener(event, listner, useCapture),第三个参数默认为 false,也就是不绑定捕获阶段。如果想禁止事件在冒泡阶段传播,只需要在要禁止的目标元素的事件方法里,加上 e.stopPropagation 方法
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: react 进阶之高阶组件
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在什么时候你会使用
document.write()
?答:什么时候都不会用
document.write()