前端JS面试题汇总 Part 1(事件委托/this关键字/原型链/AMD与CommonJS/自执行函数)

原文:https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questions.md

最近将持续翻译JavaScript面试题,希望对各位有所帮助。  

(文章中斜体字部分为译者添加)

 

目录:

Part 1(事件委托/this关键字/原型链/AMD与CommonJS/自执行函数)

Part 2 (null与undefined/闭包/foreach与map/匿名函数/代码组织)

Part 3 (宿主对象与原生对象/函数调用方式/call与apply/bind/document.write)

 

  1、解释事件委托

  事件委托是一种向父级元素增加事件监听器,而不用向子级元素一个个添加的技术方案。监听器将在会DOM事件冒泡到父级元素时被触发。事件委托有以下优势:

  • 因为我们只需要在父级元素上添加一个监听器,而不用在每个子元素上添加,所以JS的内存占用会降低很多。
  • 当子元素有移除(或新增)时,我们不用单独移除(或添加)其事件监听器。(注:也就是我们常说的动态绑定)

  参考文档:

  https://davidwalsh.name/event-delegate

  https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation

 

  2、解释JavaScript中的this工作原理

   关于this其实没有一个统一的解释,它算是JavaScript中最让人困惑的一个概念了。一种通俗的解释就是,this的取值依赖于函数被谁调用。我在网上看过很多关于this的解释,其中Arnav Aggrawal 的解释应该是最为清楚的,以下是他的观点:

  • 如果是使用new关键字来调用函数,那么函数内部的this就是一个全新的对象。
  • 如果使用apply、call或者bind来调用一个函数,函数内部的this就将指向传入的第一个参数。(注:使用这几个方法可以改变this的指向)
  • 如果函数被作为一个方法进行调用,比如:obj.method() --- 那么this就该函数的调用者。(注:样例中的obj)
  • 如果函数被独立调用,也就是没有被上述的几种情况调用。(比如:method())这种情况,this将指向于一个全局对象,在浏览器中是window对象。(nodejs环境中是global)如果是使用严格模式的话,那么全部对象将会是undefined。
  • 如果使用了上述多条规则的话,那么排序靠前的将优先控制this的指向。
  • 在ES2015中的箭头函数,将会忽略上述的所有规则,而将取决与函数创建时的作用域。(箭头函数的作用域在创建时就已经确定,不能更改。想想真是问题终结者...)

关于this更深一层的介绍,请参考:article on Medium

  引用文档:

  https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3

      https://stackoverflow.com/a/3127440/1751946

 

  3、解释原型链的原理

  这是一个在面试中经常被问到的问题。JavaScript中的所有对象一个叫prototype的属性,这个属性指向于另一个对象。当需要访问一个对象上的某个属性时,如果在自身对象上没有找到该属性的话,JS引擎会在对象的prototype上进行查找,找不到的话会继续在prototype的prototype对象,依次类推直至在某个prototype上找到该属性,或者到达原型链尽头时停止。JS的这种行为和传统的继承概念很像,但原型链的内容远不止于此,更多内容请查看:delegation than inheritance

  引用文档:

  https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson

  https://davidwalsh.name/javascript-objects

 

  4、如果看待AMD和CommonJS

  这两个都可以实现模块系统,模块系统在ES2015没有发布之前并没有被原生JavaScript所支持。CommonJS是同步的,AMD(Asynchronous Module Definition)是相对异步的。CommonJS的设计初衷是应用在服务端,而AMD主要用在客户浏览器端,因为它可以支持异步加载模块。

  另外在语法层面,我也发现AMD相对比较轻量化,而CommonJS更接近别的开发语言中导入模块的写法。大多数时候,AMD还是比较冗余的,因为它会导入所有的JavaScript代码到你的入口文件中,这样反而使我们不能从异步加载中受益。(比如我只需要lodash/map,但AMD会把整个lodash加载进来) CommonJS的语法更为接近Nodejs模块的编写方式,在服务端和客户端开发时,使用方式区别并不大。

  值得庆幸的是ES2015的到来,它可以同时支持同步与异步模块加载,使得我们有了一个统一的解决方案。但目前ES2015的模块系统还没有被完全支持,所以我们需要使用一些转换器将ES6编译为ES5。

  引用文档:

  https://auth0.com/blog/javascript-module-systems-showdown/

  https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs

 

  5、function foo(){}(); 为什么这个表达是不是一个IIFE,要如何修改?

  IIFE的意思是自执行函数表达式。JavaScript引擎将会把上面的代码看着是function foo(){}和(),也就是说前面是一个函数声明,后面的()是尝试去执行这个函数,但是由于没有执行的函数名,所以这里会抛出异常:Uncaught SyntaxError: Unexpected token )。

  有两个通过增加括号的方法去修复上面的问题:(function foo(){}()) 以及  (function foo(){}())。这种函数并不会暴露在全局作用域中,你甚至还可以把函数名也去掉,只保留函数体本身。(即匿名函数:(function (){}()) )

  引用文档:

  http://lucybain.com/blog/2014/immediately-invoked-function-expression/

  

posted @ 2018-03-01 14:41  大魔王萨格拉斯  阅读(872)  评论(0编辑  收藏  举报