前端面试 75 道题,看完的人少之又少 (中)

26. 什么是 IIFE , 它的用途是什么?IIFE或立即调用的函数表达式是在创建或声明后将被调用或执行的函数 。 创建IIFE的语法是 , 将function (){}包裹在在括号()内 , 然后再用另一个括号()调用它 , 如:(function(){})()
(function(){...} ());(function () {...})();(function named(params) {...})();(() => {});(function (global) {...})(window);const utility = (function () {return {...}})这些示例都是有效的IIFE 。 倒数第二个救命表明我们可以将参数传递给IIFE函数 。 最后一个示例表明 , 我们可以将IIFE的结果保存到变量中 , 以便稍后使用 。
IIFE的一个主要作用是避免与全局作用域内的其他变量命名冲突或污染全局命名空间 , 来个例子 。
假设我们引入了一个omelibr.js的链接 , 它提供了一些我们在代码中使用的全局函数 , 但是这个库有两个方法我们没有使用:createGraph和drawGraph , 因为这些方法都有bug 。 我们想实现自己的createGraph和drawGraph方法 。
解决此问题的一种方法是直接覆盖:
当我们使用这个解决方案时 , 我们覆盖了库提供给我们的那两个方法 。
另一种方式是我们自己改名称:
当我们使用这个解决方案时 , 我们把那些函数调用更改为新的函数名 。
还有一种方法就是使用IIFE:
在此解决方案中 , 我们要声明了graphUtility 变量 , 用来保存IIFE执行的结果 , 该函数返回一个包含两个方法createGraph和drawGraph的对象 。
IIFE 还可以用来解决一个常见的面试题:
var li = document.querySelectorAll('.list-group > li');for (var i = 0, len = li.length; i < len; i++) {li[i].addEventListener('click', function (e) {console.log(i);})假设我们有一个带有list-group类的ul元素 , 它有5个li子元素 。 当我们单击单个li元素时 , 打印对应的下标值 。 但在此外上述代码不起作用 , 这里每次点击 li 打印 i 的值都是5 , 这是由于闭包的原因 。
闭包只是函数记住其当前作用域 , 父函数作用域和全局作用域的变量引用的能力 。 当我们在全局作用域内使用var关键字声明变量时 , 就创建全局变量i 。 因此 , 当我们单击li元素时 , 它将打印5 , 因为这是稍后在回调函数中引用它时i的值 。
使用 IIFE 可以解决此问题:
var li = document.querySelectorAll('.list-group > li');for (var i = 0, len = li.length; i < len; i++) {(function (currentIndex) {li[currentIndex].addEventListener('click', function (e) {console.log(currentIndex);})})(i);}该解决方案之所以行的通 , 是因为IIFE会为每次迭代创建一个新的作用域 , 我们捕获i的值并将其传递给currentIndex参数 , 因此调用IIFE时 , 每次迭代的currentIndex值都是不同的 。
27. Function.prototype.apply 方法的用途是什么?apply() 方法调用一个具有给定this值的函数 , 以及作为一个数组(或类似数组对象)提供的参数 。
const details = {message: 'Hello World!'};function getMessage(){return this.message;}getMessage.apply(details); // 'Hello World!'call()方法的作用和 apply() 方法类似 , 区别就是call()方法接受的是参数列表 , 而apply()方法接受的是一个参数数组 。
const person = {name: "Marko Polo"};function greeting(greetingMessage) {return `${greetingMessage} ${this.name}`;}greeting.apply(person, ['Hello']); // "Hello Marko Polo!"28. Function.prototype.call 方法的用途是什么?call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数 。
const details = {message: 'Hello World!'};function getMessage(){return this.message;}getMessage.call(details); // 'Hello World!'注意:该方法的语法和作用与 apply() 方法类似 , 只有一个区别 , 就是 call() 方法接受的是一个参数列表 , 而 apply() 方法接受的是一个包含多个参数的数组 。
const person = {name: "Marko Polo"};function greeting(greetingMessage) {return `${greetingMessage} ${this.name}`;}greeting.call(person, 'Hello'); // "Hello Marko Polo!"29. Function.prototype.apply 和 Function.prototype.call 之间有什么区别?apply()方法可以在使用一个指定的 this 值和一个参数数组(或类数组对象)的前提下调用某个函数或方法 。 call()方法类似于apply() , 不同之处仅仅是call()接受的参数是参数列表 。
const obj1 = {result:0};const obj2 = {result:0};function reduceAdd(){let result = 0;for(let i = 0, len = arguments.length; i < len; i++){result += arguments[i];}this.result = result;}reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // 15reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 1530. Function.prototype.bind 的用途是什么?bind() 方法创建一个新的函数 , 在 bind() 被调用时 , 这个新函数的 this 被指定为 bind() 的第一个参数 , 而其余参数将作为新函数的参数 , 供调用时使用 。