暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

70 个 经典 JavaScript 面试题及答案

前端新世界 2022-02-16
1127
喜欢就关注我们吧


本文翻译自:
https://dev.to/macmacky/70-javascript-interview-questions-5gfi

前面我们分享了 70 个 JavaScript 面试题的前两个部分,没有阅读过的朋友可以先看看:

70 个 JavaScript 面试题及答案集锦(一) 

70 个 JavaScript 面试题及答案集锦(二)

接下来我们分享第三部分,主要涉及到this、prototype、IIFE等等。

21. JavaScript中的虚假值是什么?

const falsyValues = [''0nullundefinedNaNfalse];

虚假值是当转换为布尔值时变为false的值。

22. 如何检查值是否是虚假值?

使用Boolean
函数或双非运算符!!
即可。

23. ”use strict”有什么作用?

use strict
是JavaScript中的一个ES5功能,可使我们的代码在函数或整个脚本中进入严格模式。严格模式可以帮助我们避免代码早期出现的错误,并为其添加限制。

严格模式给我们的限制

分配或访问未声明的变量:

function returnY({
    "use strict";
    y = 123;
    return y;
}

将值分配给只读或不可写的全局变量:

"use strict";
var NaN = NaN;
var undefined = undefined;
var Infinity = "and beyond";

删除不可删除的属性:

"use strict";
const obj = {};

Object.defineProperty(obj, 'x', {
    value'1'
});

delete obj.x;

重复参数名称:

"use strict";

function someFunc(a, b, b, c{

}

使用eval
函数创建变量:

"use strict";

eval("var x = 1;");

console.log(x); //Throws a Reference Error x is not defined

this
的默认值为undefined

"use strict";

function showMeThis({
    return this;
}

showMeThis(); //returns undefined

当然,严格模式下的限制不仅仅这些,其他还有很多。

24 .JavaScript中this的值是什么?

基本上,this指当前正在执行或正在调用函数的对象的值。我之所以要说“当前”,是因为this的值会根据我们使用它的上下文以及我们在哪里使用它而改变。

const carDetails = {
    name"Ford Mustang",
    yearBought2005,
    getName() {
        return this.name;
    },
    isRegisteredtrue
};

console.log(carDetails.getName()); // logs Ford Mustang

通常这就是我们所期望的,因为getName
方法返回this.name
,this在此上下文中指向的对象是carDetails
对象,该对象当前是执行函数的“所有者”对象。

好的,让我们添加一些代码使其变得复杂起来。在console.log
语句下面,添加以下三行代码:

var name = "Ford Ranger";
var getCarName = carDetails.getName;

console.log(getCarName()); // logs Ford Ranger

第二个console.log
语句输出单词Ford Ranger
,这很奇怪,因为在我们的第一个console.log
语句中,它输出的是Ford Mustang
。原因就是getCarName
方法有了一个不同的“所有者”对象,即window
对象。在全局作用域中使用var
关键字声明变量会将属性附加到与变量名称相同的window
对象中。请记住,当不使用use strict
时,this在全局作用域中引用的是window
对象。

  console.log(getCarName === window.getCarName); //logs true
  console.log(getCarName === this.getCarName); // logs true

在此示例中,this
window
引用的是相同的对象。

解决此问题的一种方法是在函数中使用apply
call
方法。

console.log(getCarName.apply(carDetails)); //logs Ford Mustang
console.log(getCarName.call(carDetails));  //logs Ford Mustang

apply
call
方法期望第一个参数是一个对象,该对象将是该函数内部this的值。

IIFE或Immediately Invoked Function Expression,在全局作用域内声明的函数,对象内部方法中的匿名函数和内部函数的this默认值均指向window
对象。

(function ({
    console.log(this);
})(); //logs the "window" object

function iHateThis({
    console.log(this);
}

iHateThis(); //logs the "window" object  

const myFavoriteObj = {
    guessThis() {
        function getThis({
            console.log(this);
        }
        getThis();
    },
    name'Marko Polo',
    thisIsAnnoying(callback) {
        callback();
    }
};


myFavoriteObj.guessThis(); //logs the "window" object
myFavoriteObj.thisIsAnnoying(function ({
    console.log(this); //logs the "window" object
});

如果要获取myFavoriteObj
对象中的name
属性的值,即Marko Polo
,可以有两种方法解决此问题。

首先,我们将this
的值保存在一个变量中。

const myFavoriteObj = {
    guessThis() {
        const self = this//saves the this value to the "self" variable
        function getName({
            console.log(self.name);
        }
        getName();
    },
    name'Marko Polo',
    thisIsAnnoying(callback) {
        callback();
    }
};

我们保存了this
的值,此处也就是myFavoriteObj
对象。因此,我们可以在内部函数getName
的内部中访问它。

其次,我们使用ES6箭头函数。

const myFavoriteObj = {
    guessThis() {
        const getName = () => {
            //copies the value of "this" outside of this arrow function
            console.log(this.name);
        }
        getName();
    },
    name'Marko Polo',
    thisIsAnnoying(callback) {
        callback();
    }
};

箭头函数没有它自己的this。它复制了封闭作用域中this的值,或者在此示例中,复制到了内部函数getName
之外this的值,即myFavoriteObj
对象。我们还可以根据函数的调用方式确定this的值。

25. 何为对象的prototype?

用最简单的术语来说,prototype是对象的模型。如果属性和方法不存在于当前对象中,那么prototype被用作属性和方法的备选方案。这是在对象之间共享属性和方法的方式。这是围绕JavaScript原型继承的核心概念。

const o = {};
console.log(o.toString()); // logs [object Object] 

即使o
对象中不存在o.toString
方法,它也不会抛出错误,而是返回字符串[object Object]
。当属性确实不存在于对象中时,程序将查看其原型,并且如果仍然不存在,则将查看原型的原型,依此类推,直到在原型链中找到具有相同属性的属性为止。原型链的末尾是Object.prototype

console.log(o.toString === Object.prototype.toString); // logs true
// which means we we're looking up the Prototype Chain and it reached 
// the Object.prototype and used the "toString" method.

26. IIFE是什么,以及它的用途?

IIFE或Immediately Invoked Function Expression是在创建或声明后将被调用或执行的函数。创建IIFE的语法是,将function (){}
包装在圆括号()
或分组运算符中,以便于可以将函数视为表达式,然后再用另一对圆括号()
调用它。因此,IIFE看起来是这样的(function(){})()

(function ({

}());

(function ({

})();

(function named(params{

})();

(() => {

})();

(function (global{

})(window);

const utility = (function ({
    return {
        //utilities
    };
})();

这些示例都是有效的IIFE。第二个到最后一个示例显示我们可以将参数传递给IIFE函数。最后一个示例表明,我们可以将IIFE的结果保存到变量中,以便稍后引用。

IIFE的最佳用途是进行初始化设置功能,并避免与全局作用域内的其他变量命名冲突或污染全局名称空间。让我们举个例子。

<script src="https://cdnurl.com/somelibrary.js"></script>

假设我们有一个指向库somelibrary.js
的链接,该库公开了一些我们可以在代码中使用的全局函数,但是该库有两个我们不使用的方法,createGraph
drawGraph
方法,因为这些方法中有bug。而我们想要实现我们自己的createGraph
drawGraph
方法。

解决此问题的一种方法是更改脚本的结构:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function createGraph({
      // createGraph logic here
   }
   function drawGraph({
      // drawGraph logic here
   }
</script>

当我们使用此解决方案时,我们将覆盖该库提供给我们的那两个方法。

解决此问题的另一种方法是更改我们自己的辅助函数的名称:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function myCreateGraph({
      // createGraph logic here
   }
   function myDrawGraph({
      // drawGraph logic here
   }
</script>

当使用此解决方案时,我们也要更改那些函数调用为新的函数名称。

还有一种方法是使用IIFE:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   const graphUtility = (function ({
      function createGraph({
         // createGraph logic here
      }
      function drawGraph({
         // drawGraph logic here
      }
      return {
         createGraph,
         drawGraph
      }
   })();
</script>

在此解决方案中,我们将创建一个utility程序变量,该变量是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
元素时,我们想要console.log(i)
的值。

但是我们在此代码中却行不通。无论单击哪个li
元素,输出都为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); // returns 'Hello World!'

此方法的功能类似于Function.prototype.call
,唯一的区别是传递参数的方式。在apply
中,我们将参数作为数组传递。

const person = {
  name"Marko Polo"
};

function greeting(greetingMessage{
  return `${greetingMessage} ${this.name}`;
}

greeting.apply(person, ['Hello']); // returns "Hello Marko Polo!"

28. Function.prototype.call方法的用途是什么?

call
在调用时会调用一个函数,指定this或此函数的“所有者”对象。

const details = {
  message'Hello World!'
};

function getMessage(){
  return this.message;
}

getMessage.call(details); // returns 'Hello World!'

此方法的功能类似于Function.prototype.apply
,唯一的区别是传递参数的方式。在call
中,我们直接传递用逗号,
来分隔的参数,对每一个参数都是如此。

const person = {
  name"Marko Polo"
};

function greeting(greetingMessage{
  return `${greetingMessage} ${this.name}`;
}

greeting.call(person, 'Hello'); // returns "Hello Marko Polo!"

29. Function.prototype.apply和Function.prototype.call有什么区别?

apply
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, [12345]); // returns 15
reduceAdd.call(obj2, 12345); // returns 15

30. Function.prototype.bind的用法是什么?

bind
方法返回一个被绑定到特定this
值或“所有者”对象的新函数,以便稍后可以在代码中使用。call
apply
方法立即调用函数,而不是像bind
方法那样返回新的函数。

import React from 'react';

class MyComponent extends React.Component {
     constructor(props){
          super(props); 
          this.state = {
             value : ""
          }  
          this.handleChange = this.handleChange.bind(this); 
          // Binds the "handleChange" method to the "MyComponent" component
     }

     handleChange(e){
       //do something amazing here
     }

     render(){
        return (
              <>
                <input type={this.props.type}
                        value={this.state.value}
                     onChange={this.handleChange}                      
                  />

              </>

        )
     }
}

(文本完)

点赞和分享就是最大的支持❤️

文章转载自前端新世界,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论