
本文翻译自:
https://dev.to/macmacky/70-javascript-interview-questions-5gfi
前面我们分享了 70 个 JavaScript 面试题的前两个部分,没有阅读过的朋友可以先看看:
接下来我们分享第三部分,主要涉及到this、prototype、IIFE等等。
21. JavaScript中的虚假值是什么?
const falsyValues = ['', 0, null, undefined, NaN, false];
虚假值是当转换为布尔值时变为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",
yearBought: 2005,
getName() {
return this.name;
},
isRegistered: true
};
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, [1, 2, 3, 4, 5]); // returns 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 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}
/>
</>
)
}
}
(文本完)
点赞和分享就是最大的支持❤️




