JavaScript 面试问题及答案 70 个 JavaScript 面试问题

2025-05-25

JavaScript 面试问题及答案70 个 JavaScript 面试问题

大家好,祝大家有美好的一天,新年快乐🎆🎆🎆!

这篇很长,所以请耐心等待一秒钟甚至一个小时。每个问题的每个答案中都有一个向上箭头↑ 的链接,可以让你返回问题列表,这样你就不用浪费时间上下滚动了。

问题

undefined1.和有什么区别null

undefined在了解和之间的差异之前,null我们必须了解它们之间的相似之处。

  • 它们属于JavaScript 的7 种原始类型。
 let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint'];
Enter fullscreen mode Exit fullscreen mode
  • 它们是Boolean(value)值。使用或将其转换为布尔值时,计算结果为 false 的值!!value
   console.log(!!null); //logs false
   console.log(!!undefined); //logs false

   console.log(Boolean(null)); //logs false
   console.log(Boolean(undefined)); //logs false
Enter fullscreen mode Exit fullscreen mode

好的,让我们来谈谈区别。

  • undefined是尚未赋值变量的默认值。或者是一个没有明确返回值的函数,例如…… console.log(1)。又或者是一个对象中不存在的属性。JavaScript 引擎会为我们完成赋值undefined
  let _thisIsUndefined;
  const doNothing = () => {};
  const someObj = {
    a : "ay",
    b : "bee",
    c : "si"
  };

  console.log(_thisIsUndefined); //logs undefined
  console.log(doNothing()); //logs undefined
  console.log(someObj["d"]); //logs undefined
Enter fullscreen mode Exit fullscreen mode
  • null“表示无值的值”null是已明确定义为变量的值。在此示例中,当方法未引发错误null时,我们会获得 的值。fs.readFile
  fs.readFile('path/to/file', (e,data) => {
     console.log(e); //it logs null when no error occurred
     if(e){
       console.log(e);
     }
     console.log(data);
   });
Enter fullscreen mode Exit fullscreen mode

当比较和时nullundefined我们得到true使用时==false使用时。您可以在此处===阅读原因

   console.log(null == undefined); // logs true
   console.log(null === undefined); // logs false
Enter fullscreen mode Exit fullscreen mode

2. 操作员做什么&&

逻辑与运算&&符“或”在其操作数中找到第一个假值表达式并返回,如果没有找到,则返回最后一个表达式。它使用短路来避免不必要的工作。我曾在一个项目中关闭数据库连接时在代码块中使用过它。catch

   console.log(false && 1 && []); //logs false
   console.log(" " && true && 5); //logs 5
Enter fullscreen mode Exit fullscreen mode

使用if语句。

  const router: Router = Router();

  router.get('/endpoint', (req: Request, res: Response) => {
     let conMobile: PoolConnection;
     try {
        //do some db operations
     } catch (e) {
     if (conMobile) {
      conMobile.release();
     }
  }
});
Enter fullscreen mode Exit fullscreen mode

使用&&运算符。

const router: Router = Router();

router.get('/endpoint', (req: Request, res: Response) => {
  let conMobile: PoolConnection;
  try {
     //do some db operations
  } catch (e) {
    conMobile && conMobile.release()
  }
});
Enter fullscreen mode Exit fullscreen mode

3. 操作员做什么||

逻辑||或运算符or在其操作数中找到第一个真值表达式并返回。它也使用了短路来避免不必要的运算。在ES6支持默认函数参数之前,它曾用于在函数中初始化默认参数值。

console.log(null || 1 || undefined); //logs 1

function logName(name) {
  var n = name || "Mark";
  console.log(n);
}

logName(); //logs "Mark"
Enter fullscreen mode Exit fullscreen mode

4. 使用+或一元加号运算符是将字符串转换为数字的最快方法吗?

根据MDN 文档,+是将字符串转换为数字的最快方法,因为如果值已经是数字,它就不会对值执行任何操作。

5.什么是DOM

DOM代表文档对象模型(Document Object Model),是HTML 和 XML 文档的接口 ( API )。当浏览器首次读取(解析)我们的 HTML 文档,它会创建一个基于 HTML 文档的大型对象,这个大型对象就是DOM。它是一种基于 HTML 文档建模的树状结构。DOM用于与DOM 结构或特定元素或节点进行交互和修改。

想象一下如果我们有一个这样的 HTML 结构。

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>Document Object Model</title>
</head>

<body>
   <div>
      <p>
         <span></span>
      </p>
      <label></label>
      <input>
   </div>
</body>

</html>

Enter fullscreen mode Exit fullscreen mode

DOM等效项将是这样的。
DOM 等效项

JavaScriptdocument中的对象代表DOM 它为我们提供了许多方法,可用于选择元素、更新元素内容等等。

6.什么是事件传播

事件发生在DOM元素上时,该事件并不会完全发生在该元素上。在冒泡阶段事件会向上冒泡,即到达其父元素、祖父元素、祖父元素的父元素,直到到达 。而window捕获阶段,事件会从 开始window向下到达触发事件的元素event.target

事件传播分为三个阶段。

  1. 捕获阶段——事件从开始window然后向下传播到每个元素,直到到达目标元素。
  2. 目标阶段——事件已到达目标元素。
  3. 冒泡阶段– 事件从目标元素冒泡,然后向上传递每个元素,直到到达window

事件传播

7.什么是事件冒泡

事件发生在DOM元素上时,该事件并不会完全发生在该元素上。在冒泡阶段事件会向上冒泡,依次到达其父元素、祖父元素、祖父元素的父元素,直到到达window

如果我们有一个这样的示例标记。

 <div class="grandparent">
    <div class="parent">
      <div class="child">1</div>
    </div>
  </div>
Enter fullscreen mode Exit fullscreen mode

还有我们的 js 代码。

function addEvent(el, event, callback, isCapture = false) {
  if (!el || !event || !callback || typeof callback !== 'function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}

addEvent(document, 'DOMContentLoaded', () => {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');

  addEvent(child, 'click', function (e) {
    console.log('child');
  });

  addEvent(parent, 'click', function (e) {
    console.log('parent');
  });

  addEvent(grandparent, 'click', function (e) {
    console.log('grandparent');
  });

  addEvent(document, 'click', function (e) {
    console.log('document');
  });

  addEvent('html', 'click', function (e) {
    console.log('html');
  })

  addEvent(window, 'click', function (e) {
    console.log('window');
  })

});
Enter fullscreen mode Exit fullscreen mode

addEventListener方法有第三个可选参数useCapture其默认值为:如果false事件发生在捕获阶段,则true该事件将在冒泡阶段发生。如果我们点击元素,它会在控制台上分别打印。这就是事件冒泡childchildparentgrandparenthtmldocumentwindow

8.什么是事件捕获

事件发生在DOM元素上时,该事件并不会完全发生在该元素上。在捕获阶段,事件会从 DOM 元素开始,window一直向下到达触发该事件的元素。

如果我们有一个这样的示例标记。

 <div class="grandparent">
    <div class="parent">
      <div class="child">1</div>
    </div>
  </div>
Enter fullscreen mode Exit fullscreen mode

还有我们的 js 代码。

function addEvent(el, event, callback, isCapture = false) {
  if (!el || !event || !callback || typeof callback !== 'function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}

addEvent(document, 'DOMContentLoaded', () => {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');

  addEvent(child, 'click', function (e) {
    console.log('child');
  }, true);

  addEvent(parent, 'click', function (e) {
    console.log('parent');
  }, true);

  addEvent(grandparent, 'click', function (e) {
    console.log('grandparent');
  }, true);

  addEvent(document, 'click', function (e) {
    console.log('document');
  }, true);

  addEvent('html', 'click', function (e) {
    console.log('html');
  }, true)

  addEvent(window, 'click', function (e) {
    console.log('window');
  }, true)

});
Enter fullscreen mode Exit fullscreen mode

addEventListener方法有第三个可选参数useCapture,其默认值为:如果false事件发生在捕获阶段,则true该事件将在冒泡阶段发生。如果我们点击元素child它会在控制台上分别打印windowdocument这就是事件捕获htmlgrandparentparentchild

event.preventDefault()9.和方法有什么区别event.stopPropagation()

event.preventDefault()方法阻止元素的默认行为。如果在form元素中使用,则会阻止元素提交。如果在anchor元素中使用,则会阻止元素导航。如果在元素中使用,contextmenu则会阻止元素显示。该event.stopPropagation()方法还可以阻止事件的传播,或者阻止事件在冒泡捕获阶段发生。

10.如何知道event.preventDefault()元素中是否使用了该方法?

event.defaultPrevented我们可以在事件对象中使用属性。它返回一个值boolean,指示是否event.preventDefault()在特定元素中调用了该属性。

11.为什么此代码会obj.someprop.x抛出错误?

const obj = {};
console.log(obj.someprop.x);
Enter fullscreen mode Exit fullscreen mode

显然,这会抛出一个错误,因为我们试图访问属性
x中一个somepropundefined值的属性。记住,对象中的属性本身并不存在,其原型的默认值为 ,undefined并且undefined没有属性x

12.什么是event.target

简单来说,event.target是发生事件的元素或触发事件的元素。

示例 HTML 标记。

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
          <button style="margin:10px">
             Button
          </button>
        </div>
    </div>
  </div>
Enter fullscreen mode Exit fullscreen mode

示例 JavaScript。

 function clickFunc(event) {
  console.log(event.target);
}
Enter fullscreen mode Exit fullscreen mode

如果您单击按钮,它将记录按钮标记,即使我们将事件附加在最外层,div它也始终会记录按钮,因此我们可以得出结论,event.target是触发事件的元素。

13.什么是event.currentTarget

event.currentTarget是我们明确附加事件处理程序元素

复制问题 12中的标记
示例 HTML 标记。

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
          <button style="margin:10px">
             Button
          </button>
        </div>
    </div>
  </div>
Enter fullscreen mode Exit fullscreen mode

并稍微改变我们的JS 。

function clickFunc(event) {
  console.log(event.currentTarget);
}
Enter fullscreen mode Exit fullscreen mode

如果您点击该按钮,即使我们点击了它,它也会记录最外层的div标记。在此示例中,我们可以得出结论, event.currentTarget是我们附加事件处理程序的元素。

==14.和有什么区别===

==(抽象相等)===(严格相等)的区别在于,前者在强制转换==比较,而后者不经过强制转换,按类型比较===

让我们深入探讨一下==。首先我们来谈谈强制转换

强制转换是将一个值转换为另一种类型的过程。在本例中,==进行了隐式强制转换==在比较两个值之前, 需要执行一些条件。

假设我们必须比较x == y值。

  1. 如果xy类型相同,则使用===运算符进行比较。
  2. 如果xnullyundefined则返回true
  3. 如果xundefinedynull则返回true
  4. 如果x是类型number并且y是类型string则返回x == toNumber(y)
  5. 如果x是类型string并且y是类型number则返回toNumber(x) == y
  6. 如果x是类型boolean则返回toNumber(x) == y
  7. 如果y是类型boolean则返回x == toNumber(y)
  8. 如果xstringsymbolnumber并且y是类型object则返回x == toPrimitive(y)
  9. 如果xobjectxstringsymbol则返回toPrimitive(x) == y
  10. 返回false

注意: toPrimitive首先使用valueOf方法,然后使用toString对象中的方法来获取该对象的原始值。

让我们举些例子。

x y x == y
5 5 true
1 '1' true
null undefined true
0 false true
'1,2' [1,2] true
'[object Object]' {} true

这些例子都返回true

一个例子符合条件一,因为xy具有相同的类型和值。

第二个例子进入条件四 y,在比较之前转换为number

第三个例子符合第二个条件

第四个例子符合条件七,因为yboolean

第五个示例进入条件八string。使用toString()返回的方法将数组转换为1,2

最后一个例子条件十string。使用toString()返回的方法将对象转换为[object Object]

x y x === y
5 5 true
1 '1' false
null undefined false
0 false false
'1,2' [1,2] false
'[object Object]' {} false

如果我们使用该===运算符,除第一个例子之外的所有比较都将返回,false因为它们不具有相同的类型,而第一个例子将返回,true因为两者具有相同的类型和值。

15. 为什么在 JavaScript 中比较两个相似的对象时会返回false ?

假设我们有下面一个例子。

let a = { a: 1 };
let b = { a: 1 };
let c = a;

console.log(a === b); // logs false even though they have the same property
console.log(a === c); // logs true hmm
Enter fullscreen mode Exit fullscreen mode

JavaScript比较对象原始类型的方式有所不同。原始类型通过比较,对象则通过引用变量在内存中存储的地址比较。这就是为什么第一个console.log语句返回false,而第二个console.log语句返回trueac具有相同的引用,a而 和 则b不同。

16. !!运算符起什么作用?

双重运算符或!!将右侧的值强制转换为布尔值。基本上,这是一种将值转换为布尔值的奇特方法。

console.log(!!null); //logs false
console.log(!!undefined); //logs false
console.log(!!''); //logs false
console.log(!!0); //logs false
console.log(!!NaN); //logs false
console.log(!!' '); //logs true
console.log(!!{}); //logs true
console.log(!![]); //logs true
console.log(!!1); //logs true
console.log(!![].length); //logs false
Enter fullscreen mode Exit fullscreen mode

17. 如何在一行中计算多个表达式的值?

我们可以使用逗号运算符,或逗号运算符在一行中计算多个表达式的值。它从左到右计算,并返回右侧最后一项或最后一个操作数的值。

let x = 5;

x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10);

function addFive(num) {
  return num + 5;
}
Enter fullscreen mode Exit fullscreen mode

如果记录的值,x则为27。首先,我们增加x 的值,则为6,然后我们调用该函数addFive(6)并将 6 作为参数传递,并将结果分配给 的x新值,x11。之后,我们将 的当前值x乘以2,并将其分配给 的x更新值,x22。然后,我们将 的当前值减去x5,并将结果分配给x更新后的值,即17。最后,我们将 的值增加x10,并将更新后的值分配给x现在的 值,x27

18.什么是提升

提升是用于描述将变量函数移动到我们定义该变量或函数的(全局或函数)范围顶部的术语。

为了理解代码提升,我必须解释一下执行上下文执行上下文
当前正在执行的“代码环境”。执行上下文有两个阶段:编译执行

编译- 在此阶段,它获取所有函数声明并将它们提升到其范围的顶部,以便我们稍后可以引用它们,并获取所有变量声明 (使用 var 关键字声明)并将它们提升并赋予它们默认值undefined

执行- 在此阶段,它将值分配给先前提升的变量,并执行调用函数(对象中的方法)

注意:只有函数声明和使用var关键字声明的变量才会被提升,函数表达式箭头函数let关键字则不会const

好的,假设我们在下面的全局范围内有一个示例代码。

console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));

function greet(name){
  return 'Hello ' + name + '!';
}

var y;
Enter fullscreen mode Exit fullscreen mode

此代码分别记录undefined1Hello Mark!

所以编译阶段看起来是这样的。

function greet(name) {
  return 'Hello ' + name + '!';
}

var y; //implicit "undefined" assignment

//waiting for "compilation" phase to finish

//then start "execution" phase
/*
console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));
*/

Enter fullscreen mode Exit fullscreen mode

为了举例,我评论了变量的赋值函数调用

编译阶段完成后,它开始执行阶段,调用方法并为变量赋值。

function greet(name) {
  return 'Hello ' + name + '!';
}

var y;

//start "execution" phase

console.log(y);
y = 1;
console.log(y);
console.log(greet("Mark"));
Enter fullscreen mode Exit fullscreen mode

19.什么是范围

JavaScript 中的作用域是指我们可以有效访问变量或函数的区域。JavaScript 有三种作用域:全局作用域函数作用域块作用域(ES6)

  • 全局范围- 在全局命名空间中声明的变量或函数位于全局范围内,因此可以在我们的代码中的任何地方访问。
   //global namespace
   var g = "global";

   function globalFunc(){
     function innerFunc(){
          console.log(g); // can access "g" because "g" is a global variable
     }
     innerFunc();
   }  
Enter fullscreen mode Exit fullscreen mode
  • 函数作用域- 在函数内声明的变量、函数和参数可在该函数内部访问,但不能在函数外部访问。
    function myFavoriteFunc(a) {
       if (true) {
          var b = "Hello " + a;
       }
       return b;
   }
   myFavoriteFunc("World");

   console.log(a); // Throws a ReferenceError "a" is not defined
   console.log(b); // does not continue here 
Enter fullscreen mode Exit fullscreen mode
  • 块范围-在块内声明的变量letconst{}只能在块内访问。
 function testBlock(){
   if(true){
     let z = 5;
   }
   return z; 
 }

 testBlock(); // Throws a ReferenceError "z" is not defined
Enter fullscreen mode Exit fullscreen mode

作用域也是一组用于查找变量的规则。如果变量在当前作用域中不存在,它会外部作用域中查找,如果仍然不存在,它会再次查找,直到到达全局作用域。如果变量存在,则可以使用它;如果不存在,则会抛出错误。它会搜索最近的变量,一旦找到,就会停止搜索查找。这称为作用域链

   /* Scope Chain
   Inside inner function perspective

   inner's scope -> outer's scope -> global's scope
  */


  //Global Scope
  var variable1 = "Comrades";   
  var variable2 = "Sayonara";

  function outer(){
  //outer's scope
    var variable1 = "World";
    function inner(){
    //inner's scope
      var variable2 = "Hello";
      console.log(variable2 + " " + variable1);
    }
    inner();
  }  
  outer(); 
// logs Hello World 
// because (variable2 = "Hello") and (variable1 = "World") are the nearest 
// variables inside inner's scope.
Enter fullscreen mode Exit fullscreen mode

范围

20.什么是闭包

这可能是所有问题中最难的一个,因为闭包本身就是一个有争议的话题。所以我会根据我的理解来解释一下。

闭包简单来说就是函数在声明时,通过作用域链记住其当前作用域、父函数作用域、父函数的父函数作用域直至全局作用域中变量和参数的引用的能力本质上,它是函数声明时创建的作用域

例子是解释闭包的好方法。

   //Global's Scope
   var globalVar = "abc";

   function a(){
   //testClosures's Scope
     console.log(globalVar);
   }

   a(); //logs "abc" 
   /* Scope Chain
      Inside a function perspective

      a's scope -> global's scope  
   */ 
Enter fullscreen mode Exit fullscreen mode

在这个例子中,当我们声明a函数时,全局范围是a's 闭包的一部分

a 的闭包

图中变量globalVar没有值的原因是,该变量的值会根据函数调用的位置时间而a变化。
但在上面的例子中,该globalVar变量的值是abc

好的,让我们来看一个复杂的例子。

var globalVar = "global";
var outerVar = "outer"

function outerFunc(outerParam) {
  function innerFunc(innerParam) {
    console.log(globalVar, outerParam, innerParam);
  }
  return innerFunc;
}

const x = outerFunc(outerVar);
outerVar = "outer-2";
globalVar = "guess"
x("inner");
Enter fullscreen mode Exit fullscreen mode

复杂的
这会打印“guess outer inner”。原因在于,当我们调用outerFunc函数并将函数的返回值赋给innerFunc变量时x即使我们为变量赋了新值outer-2,outerParam的值仍然是outer,因为 重新赋值发生在函数调用之后,而此时我们调用函数时会作用域链中查找 的值, 的值是“outer”。现在,当我们调用引用 的变量时, 的值将是inner,因为这是我们在调用时传递的值,而变量的值将是guess ,因为在调用变量之前我们为 赋了一个新值,而在调用时,作用域链的 的值guessouterVar
outerouterFuncouterVarouterVarxinnerFunc
innerParamglobalVarxglobalVarxglobalVar

我们有一个例子,说明了没有正确理解闭包的问题。

const arrFuncs = [];
for(var i = 0; i < 5; i++){
  arrFuncs.push(function (){
    return i;
  });
}
console.log(i); // i is 5

for (let i = 0; i < arrFuncs.length; i++) {
  console.log(arrFuncs[i]()); // all logs "5"
}
Enter fullscreen mode Exit fullscreen mode

由于闭包的原因,这段代码无法按预期工作
关键字var创建一个全局变量,当我们推送一个函数时,
我们返回这个全局变量i。因此,当我们在循环后调用该数组中的某个函数时,它会记录日志,5因为我们获得了
的当前值,并且我们可以访问它,因为它是一个全局变量。这是因为闭包在创建时保留的是该变量的引用,而不是它的值。我们可以使用IIFES或将关键字更改为 来实现块作用域来解决这个问题。i5varlet

21. JavaScript中的值是什么?


 const falsyValues = ['', 0, null, undefined, NaN, false];
Enter fullscreen mode Exit fullscreen mode

falsy值是转换为布尔值后变为false 的

22.如何检查一个值是否为

使用布尔函数或双重非运算符!!

23. 做什么"use strict"

↑是 JavaScript"use strict"中的 ES5 特性,它使我们的代码函数整个脚本中处于严格模式严格模式帮助我们在代码早期避免错误,并为其添加限制。

严格模式给我们的限制

  • 分配或访问未声明的变量。
 function returnY(){
    "use strict";
    y = 123;
    return y;
 }
Enter fullscreen mode Exit fullscreen mode
  • 为只读或不可写的全局变量赋值;
   "use strict";
   var NaN = NaN;
   var undefined = undefined;
   var Infinity = "and beyond";
Enter fullscreen mode Exit fullscreen mode
  • 删除不可删除的属性。
   "use strict";
   const obj = {};

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

   delete obj.x;
Enter fullscreen mode Exit fullscreen mode
  • 参数名称重复。
   "use strict";

   function someFunc(a, b, b, c){

   }
Enter fullscreen mode Exit fullscreen mode
  • 使用eval函数创建变量。
 "use strict";

 eval("var x = 1;");

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

Enter fullscreen mode Exit fullscreen mode
  • 默认值为undefined
  "use strict";

  function showMeThis(){
    return this;
  }

  showMeThis(); //returns undefined
Enter fullscreen mode Exit fullscreen mode

严格模式中的限制远不止这些。

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
Enter fullscreen mode Exit fullscreen mode

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

好的,让我们添加一些代码让它变得奇怪一些。在console.log语句下面添加以下三行代码

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

   console.log(getCarName()); // logs Ford Ranger
Enter fullscreen mode Exit fullscreen mode

第二条console.log语句打印出了单词Ford Ranger,这很奇怪,因为在第一个console.log语句中打印的是Ford Mustang。这是因为该getCarName方法有一个不同的“所有者”对象,即对象本身。在全局范围内window使用关键字 声明变量会将对象中与变量同名的属性附加到全局范围内。记住,在全局范围内,不使用 时,指的是对象本身。varwindowthiswindow"use strict"

  console.log(getCarName === window.getCarName); //logs true
  console.log(getCarName === this.getCarName); // logs true
Enter fullscreen mode Exit fullscreen mode

thiswindow本例中指的是同一个对象。

解决这个问题的一种方法是使用函数中的applycall方法。

   console.log(getCarName.apply(carDetails)); //logs Ford Mustang
   console.log(getCarName.call(carDetails));  //logs Ford Mustang
Enter fullscreen mode Exit fullscreen mode

方法期望第一个参数是一个对象,该对象将是该函数内部的applycallthis

IIFE立即调用函数表达式,在全局范围内声明的函数、匿名函数和对象内部方法中的内部函数都默认具有指向窗口对象的this

   (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
   });
Enter fullscreen mode Exit fullscreen mode

如果我们想获取对象中Marko Poloname属性的值,有两种方法可以解决这个问题。myFavoriteObj

首先,我们将的值保存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();
     }
   };
Enter fullscreen mode Exit fullscreen mode

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();
     }
   };
Enter fullscreen mode Exit fullscreen mode

箭头函数没有自己的this。它会复制封闭词法作用域的 的值,或者在本例中,复制内部函数外部(即对象)this的值。我们还可以根据函数的调用方式来确定 的值thisgetNamemyFavoriteObjthis

25.prototype物体的“是什么”?

prototype简单来说,A是一个对象的蓝图。如果当前对象中存在 A,它将作为属性方法的后备。A 是对象之间共享属性和功能的方式。它是 JavaScript原型继承的核心概念。

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

即使该o.toString方法在对象中不存在,o它也不会抛出错误,而是返回一个字符串[object Object]。当对象中不存在某个属性时,它会查找其原型,如果仍然不存在,它会查找原型的原型,依此类推,直到在原型链中找到具有相同属性的属性。原型链的末尾Object.prototypenull之后

   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.
Enter fullscreen mode Exit fullscreen mode

26.什么是IIFE,它有什么用?

IIFE 立即调用函数表达式)是指在创建或声明后立即调用或执行的函数。创建 IIFE 的语法将 括function (){}在括号()分组运算符中,将函数视为表达式,然后用另一个括号 调用它()。IIFE如下所示(function(){})()

(function () {

}());

(function () {

})();

(function named(params) {

})();

(() => {

})();

(function (global) {

})(window);

const utility = (function () {
   return {
      //utilities
   };
})();
Enter fullscreen mode Exit fullscreen mode

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

IIFE的最佳用途是实现初始化设置功能,并避免与全局范围内的其他变量发生命名冲突或污染全局命名空间。我们来看一个例子。

<script src="https://cdnurl.com/somelibrary.js"></script>
Enter fullscreen mode Exit fullscreen mode

假设我们有一个指向某个库的链接somelibrary.js,它公开了一些我们可以在代码中使用的全局函数,但这个库中有两个方法我们没有用到createGraphdrawGraph因为这些方法本身存在 bug。我们想实现自己的createGraphdrawGraph方法。

  • 解决这个问题的一种方法是改变脚本的结构。
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function createGraph() {
      // createGraph logic here
   }
   function drawGraph() {
      // drawGraph logic here
   }
</script>
Enter fullscreen mode Exit fullscreen mode

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

  • 解决这个问题的另一种方法是更改​​我们自己的辅助函数的名称。
<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function myCreateGraph() {
      // createGraph logic here
   }
   function myDrawGraph() {
      // drawGraph logic here
   }
</script>
Enter fullscreen mode Exit fullscreen mode

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

  • 另一种方法是使用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>
Enter fullscreen mode Exit fullscreen mode

在这个解决方案中,我们创建了一个实用变量,它是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);
   })
}
Enter fullscreen mode Exit fullscreen mode

假设我们有一个带有list-groupul类的元素,它有 5个子元素。我们想要在单击单个元素时获取的值 但我们想要的代码中的行为不起作用。相反,它会记录对元素的任何单击。我们遇到的问题是由于闭包的工作方式造成的。闭包只是函数记住其当前作用域、其父函数作用域和全局作用域中的变量引用的能力。当我们在全局作用域中使用关键字声明变量时,显然我们正在创建一个全局变量。因此,当我们单击一个元素时,它会记录5 ,因为这是我们稍后在回调函数中引用它时的值。liconsole.logili
5livarilii

  • 解决这个问题的一个方法是使用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);
}
Enter fullscreen mode Exit fullscreen mode

该解决方案之所以有效,是因为IIFE为每次迭代创建一个新范围,并且我们捕获的值i并将其传递给参数,因此当我们调用IIFE时,每次迭代currentIndex的值都是不同的currentIndex

27.使用Function.prototype.apply方法是什么?

调用apply一个函数,指定this调用时该函数的“所有者”对象。

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

function getMessage(){
  return this.message;
}

getMessage.apply(details); // returns 'Hello World!'
Enter fullscreen mode Exit fullscreen mode

此方法的工作原理与此类似,Function.prototype.call唯一的区别在于我们传递参数的方式。apply我们将参数作为数组传递。

const person = {
  name: "Marko Polo"
};

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

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

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

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

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

function getMessage(){
  return this.message;
}

getMessage.call(details); // returns 'Hello World!'
Enter fullscreen mode Exit fullscreen mode

此方法的工作原理与此类似,Function.prototype.apply唯一的区别在于传递参数的方式。call我们直接传递参数,并用逗号分隔,每个参数。

const person = {
  name: "Marko Polo"
};

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

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

Function.prototype.apply29.和有什么区别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
Enter fullscreen mode Exit fullscreen mode

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

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

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}                      
                  />
              </>
        )
     }
}
Enter fullscreen mode Exit fullscreen mode

31.什么是函数式编程? JavaScript的哪些特性使它成为函数式语言的候选?

函数式编程是一种声明式编程范式或模式,它说明了我们如何使用表达式通过函数构建应用程序,该表达式计算一个值,而不会改变或更改传递给它的参数。

JavaScript数组具有mapfilterreduce方法,它们是函数式编程世界中最著名的函数,因为它们很有用,并且它们不会变异或更改数组,这使得这些函数变得纯粹,并且 JavaScript 支持闭包高阶函数,这是函数式编程语言的一个特征

  • map方法创建一个新数组,并对数组中的每个元素调用提供的回调函数。
const words = ["Functional", "Procedural", "Object-Oriented"];

const wordsLength = words.map(word => word.length);
Enter fullscreen mode Exit fullscreen mode
  • filter方法创建一个新数组其中包含所有通过回调函数测试的元素。
const data = [
  { name: 'Mark', isRegistered: true },
  { name: 'Mary', isRegistered: false },
  { name: 'Mae', isRegistered: true }
];

const registeredUsers = data.filter(user => user.isRegistered);
Enter fullscreen mode Exit fullscreen mode
  • Reduce方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。
const strs = ["I", " ", "am", " ", "Iron", " ", "Man"];
const result = strs.reduce((acc, currentStr) => acc + currentStr, "");
Enter fullscreen mode Exit fullscreen mode

32.什么是高阶函数

高阶函数是可以返回一个函数或接收一个或多个具有函数值的参数的函数。

function higherOrderFunction(param,callback){
    return callback(param);
}
Enter fullscreen mode Exit fullscreen mode

33.为什么函数被称为“一等对象”

JavaScript 中的函数是“一等对象”,因为它们在 JavaScript 中被视为与任何其他值一样。它们可以赋值给变量,可以作为对象的属性(称为方法),可以作为数组中的元素,可以作为参数传递给函数,也可以作为函数的返回值。函数与JavaScript中任何其他值的唯一区别在于,函数可以被调用(invoked)。

34.Array.prototype.map手动实现该方法。


function map(arr, mapCallback) {
  // First, we check if the parameters passed are right.
  if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { 
    return [];
  } else {
    let result = [];
    // We're making a results array every time we call this function
    // because we don't want to mutate the original array.
    for (let i = 0, len = arr.length; i < len; i++) {
      result.push(mapCallback(arr[i], i, arr)); 
      // push the result of the mapCallback in the 'result' array
    }
    return result; // return the result array
  }
}
Enter fullscreen mode Exit fullscreen mode

正如该方法的MDN描述Array.prototype.map

map() 方法通过对调用数组中的每个元素调用提供的函数来创建一个新数组。

35.Array.prototype.filter手动实现该方法。


function filter(arr, filterCallback) {
  // First, we check if the parameters passed are right.
  if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') 
  {
    return [];
  } else {
    let result = [];
    // We're making a results array every time we call this function
    // because we don't want to mutate the original array.
    for (let i = 0, len = arr.length; i < len; i++) {
      // check if the return value of the filterCallback is true or "truthy"
      if (filterCallback(arr[i], i, arr)) { 
      // push the current item in the 'result' array if the condition is true
        result.push(arr[i]);
      }
    }
    return result; // return the result array
  }
}
Enter fullscreen mode Exit fullscreen mode

正如该方法的MDN描述Array.prototype.filter

filter() 方法创建一个新数组,其中包含通过所提供函数实现的测试的所有元素。

36.Array.prototype.reduce手动实现该方法。


function reduce(arr, reduceCallback, initialValue) {
  // First, we check if the parameters passed are right.
  if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function') 
  {
    return [];
  } else {
    // If no initialValue has been passed to the function we're gonna use the 
    let hasInitialValue = initialValue !== undefined;
    let value = hasInitialValue ? initialValue : arr[0];
    // first array item as the initialValue

    // Then we're gonna start looping at index 1 if there is no 
    // initialValue has been passed to the function else we start at 0 if 
    // there is an initialValue.
    for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
      // Then for every iteration we assign the result of the 
      // reduceCallback to the variable value.
      value = reduceCallback(value, arr[i], i, arr); 
    }
    return value;
  }
}
Enter fullscreen mode Exit fullscreen mode

正如该方法的MDN描述Array.prototype.reduce

reduce() 方法对数组的每个元素执行一个 reducer 函数(由您提供),从而生成单个输出值。

37.什么是arguments对象?

参数对象是函数中传递的参数值的集合。它是一个类似数组的对象,因为它具有length属性,并且我们可以使用数组索引符号访问单个值,它不具备数组中的内置方法 它可以帮助我们知道函数中传递的参数数量。arguments[1]forEachreducefiltermap

我们可以arguments使用将对象转换为数组Array.prototype.slice

function one() {
  return Array.prototype.slice.call(arguments);
}
Enter fullscreen mode Exit fullscreen mode

注意:arguments对象不适用于 ES6 箭头函数。

function one() {
  return arguments;
}
const two = function () {
  return arguments;
}
const three = function three() {
  return arguments;
}

const four = () => arguments;

four(); // Throws an error  - arguments is not defined
Enter fullscreen mode Exit fullscreen mode

当我们调用该函数时,four它会抛出一个错误。如果您的环境支持rest 语法,ReferenceError: arguments is not defined我们可以解决这个问题

const four = (...args) => args;
Enter fullscreen mode Exit fullscreen mode

这会自动将所有参数值放入数组中。

38.如何创建没有原型的对象

我们可以使用该方法创建一个没有原型的对象Object.create

   const o1 = {};
   console.log(o1.toString()); 
   // logs [object Object] get this method to the Object.prototype 

   const o2 = Object.create(null);
   // the first parameter is the prototype of the object "o2" which in this
   // case will be null specifying we don't want any prototype
   console.log(o2.toString());
   // throws an error o2.toString is not a function 
Enter fullscreen mode Exit fullscreen mode

39.为什么b在这段代码中调用这个函数时会变成全局变量?


function myFunc() {
  let a = b = 0;
}

myFunc();
Enter fullscreen mode Exit fullscreen mode

原因是赋值运算符=具有从右到左的结合性求值性。这意味着,当一个表达式中出现多个赋值运算符时,它们会从右到左求值。所以我们的代码变成了这样。

function myFunc() {
  let a = (b = 0);
}

myFunc();
Enter fullscreen mode Exit fullscreen mode

b = 0首先,本例中被求值的表达式b并没有被声明。因此,JS 引擎会b在这个函数外部创建一个全局变量,之后表达式的返回值b = 0将为 0,并a通过let关键字将其赋值给这个新的局部变量。

我们可以通过先声明变量然后再赋值来解决这个问题。

function myFunc() {
  let a,b;
  a = b = 0;
}
myFunc();
Enter fullscreen mode Exit fullscreen mode

40.什么是ECMAScript

ECMAScript是制定脚本语言的标准,这意味着JavaScript遵循ECMAScript标准中的规范变化,因为它是JavaScript蓝图

41. ES6ECMAScript 2015有哪些新特性

var42. 、let和关键字之间有什么区别const

var使用关键字声明的变量具有函数作用域
这意味着即使我们在代码块内声明变量,也可以跨函数访问该变量。

function giveMeX(showX) {
  if (showX) {
    var x = 5;
  }
  return x;
}

console.log(giveMeX(false));
console.log(giveMeX(true));
Enter fullscreen mode Exit fullscreen mode

第一条console.log语句打印了日志undefined
,第二条语句打印5了日志。 由于x变量被提升到了函数作用域的顶部,所以我们可以访问它。所以我们的函数代码是这样解释的。

function giveMeX(showX) {
  var x; // has a default value of undefined
  if (showX) {
    x = 5;
  }
  return x;
}
Enter fullscreen mode Exit fullscreen mode

如果您想知道为什么它会undefined在第一个console.log语句中记录,请记住声明的没有初始值的变量具有默认值undefined

let使用和关键字声明的变量const块作用域的。这意味着变量只能在{}声明它的块内访问。

function giveMeX(showX) {
  if (showX) {
    let x = 5;
  }
  return x;
}


function giveMeY(showY) {
  if (showY) {
    let y = 5;
  }
  return y;
}
Enter fullscreen mode Exit fullscreen mode

如果我们使用 的参数调用此函数,false它会抛出一个异常,Reference Error因为我们无法访问该块之外的xy变量,并且这些变量不会被提升

和 之间也有区别let,我们const可以使用 赋值,let但不能在 中赋值const,而是const可变的。这意味着,如果我们赋给const一个对象,我们可以更改其属性的值,但不能为该变量重新赋值。

43.什么是箭头函数

箭头函数是 JavaScript 中一种创建函数的新方法。箭头函数创建函数所需的时间较少,并且语法比函数表达式更简洁,因为我们省略了function关键字。

//ES5 Version
var getCurrentDate = function (){
  return new Date();
}

//ES6 Version
const getCurrentDate = () => new Date();
Enter fullscreen mode Exit fullscreen mode

在此示例中,ES5 版本分别需要function(){}声明和return关键字来创建函数和返回值。在箭头函数版本中,我们只需要()括号,而不需要return语句,因为如果只有一个表达式或值要返回,箭头函数会隐式返回

//ES5 Version
function greet(name) {
  return 'Hello ' + name + '!';
}

//ES6 Version
const greet = (name) => `Hello ${name}`;
const greet2 = name => `Hello ${name}`;

Enter fullscreen mode Exit fullscreen mode

我们还可以在箭头函数中使用与函数表达式函数声明相同的参数。如果箭头函数中只有一个参数,则可以省略括号,这也是有效的。

const getArgs = () => arguments

const getArgs2 = (...rest) => rest
Enter fullscreen mode Exit fullscreen mode

箭头函数无法访问arguments对象。因此调用第一个getArgs函数会抛出错误。我们可以使用剩余参数来获取箭头函数中传递的所有参数。

const data = {
  result: 0,
  nums: [1, 2, 3, 4, 5],
  computeResult() {
    // "this" here refers to the "data" object
    const addAll = () => {
      // arrow functions "copies" the "this" value of 
      // the lexical enclosing function
      return this.nums.reduce((total, cur) => total + cur, 0)
    };
    this.result = addAll();
  }
};
Enter fullscreen mode Exit fullscreen mode

箭头函数没有自己的this值。它捕获或获取this词法封闭函数的值,或者在本例中,addAll函数复制方法this的值computeResult。如果我们在全局范围内声明一个箭头函数,那么它的值this就是window对象。

44.什么是

类是JavaScript中编写构造函数的新方式。它是使用构造函数的语法糖,其底层仍然使用原型基于原型的继承。

   //ES5 Version
   function Person(firstName, lastName, age, address){
      this.firstName = firstName;
      this.lastName = lastName;
      this.age = age;
      this.address = address;
   }

   Person.self = function(){
     return this;
   }

   Person.prototype.toString = function(){
     return "[object Person]";
   }

   Person.prototype.getFullName = function (){
     return this.firstName + " " + this.lastName;
   }  

   //ES6 Version
   class Person {
        constructor(firstName, lastName, age, address){
            this.lastName = lastName;
            this.firstName = firstName;
            this.age = age;
            this.address = address;
        }

        static self() {
           return this;
        }

        toString(){
           return "[object Person]";
        }

        getFullName(){
           return `${this.firstName} ${this.lastName}`;
        }
   }
Enter fullscreen mode Exit fullscreen mode

覆盖方法从另一个类继承

//ES5 Version
Employee.prototype = Object.create(Person.prototype);

function Employee(firstName, lastName, age, address, jobTitle, yearStarted) {
  Person.call(this, firstName, lastName, age, address);
  this.jobTitle = jobTitle;
  this.yearStarted = yearStarted;
}

Employee.prototype.describe = function () {
  return `I am ${this.getFullName()} and I have a position of ${this.jobTitle} and I started at ${this.yearStarted}`;
}

Employee.prototype.toString = function () {
  return "[object Employee]";
}

//ES6 Version
class Employee extends Person { //Inherits from "Person" class
  constructor(firstName, lastName, age, address, jobTitle, yearStarted) {
    super(firstName, lastName, age, address);
    this.jobTitle = jobTitle;
    this.yearStarted = yearStarted;
  }

  describe() {
    return `I am ${this.getFullName()} and I have a position of ${this.jobTitle} and I started at ${this.yearStarted}`;
  }

  toString() { // Overriding the "toString" method of "Person"
    return "[object Employee]";
  }
}

Enter fullscreen mode Exit fullscreen mode

那么我们如何知道它在底层使用了原型呢?

   class Something {

   }

   function AnotherSomething(){

   }
   const as = new AnotherSomething();
   const s = new Something();

   console.log(typeof Something); // logs "function"
   console.log(typeof AnotherSomething); // logs "function"
   console.log(as.toString()); // logs "[object Object]"
   console.log(as.toString()); // logs "[object Object]"
   console.log(as.toString === Object.prototype.toString); 
   console.log(s.toString === Object.prototype.toString); 
   // both logs return true indicating that we are still using 
   // prototypes under the hoods because the Object.prototype is
   // the last part of the Prototype Chain and "Something"
   // and "AnotherSomething" both inherit from Object.prototype
Enter fullscreen mode Exit fullscreen mode

45.什么是模板字符串

模板字面量是JavaScript 中创建字符串的一种新方式。我们可以使用反引号或反引号来创建模板字面量。

//ES5 Version
var greet = 'Hi I\'m Mark';

//ES6 Version
let greet = `Hi I'm Mark`;
Enter fullscreen mode Exit fullscreen mode

在 ES5 版本中,我们需要使用 来'转义 ,\逃避该符号的正常功能,在本例中,该功能是结束该字符串值。在模板字面量中,我们不需要这样做。

//ES5 Version
var lastWords = '\n'
  + '   I  \n'
  + '   Am  \n'
  + 'Iron Man \n';


//ES6 Version
let lastWords = `
    I
    Am
  Iron Man   
`;
Enter fullscreen mode Exit fullscreen mode

在 ES5 版本中,我们需要添加此代码\n才能在字符串中添加新行。在模板字符串中,我们不需要这样做。

//ES5 Version
function greet(name) {
  return 'Hello ' + name + '!';
}


//ES6 Version
const greet = name => {
  return `Hello ${name} !`;
}
Enter fullscreen mode Exit fullscreen mode

在 ES5 版本中,如果我们需要在字符串中添加表达式或值,则需要使用+或 字符串连接运算符。在模板字面量中,我们可以使用 嵌入表达式,${expr}这使得它比 ES5 版本更简洁。

46.什么是对象解构

对象解构是一种从对象或数组中获取提取值的全新且更简洁的方法。

假设我们有一个看起来像这样的物体。

const employee = {
  firstName: "Marko",
  lastName: "Polo",
  position: "Software Developer",
  yearHired: 2017
};

Enter fullscreen mode Exit fullscreen mode

从对象中获取属性的传统方法是创建一个与对象属性同名的变量。这种方法很麻烦,因为我们要为每个属性创建一个新的变量。想象一下,我们有一个包含大量属性和方法的大对象,用这种方式提取属性会非常麻烦。

var firstName = employee.firstName;
var lastName = employee.lastName;
var position = employee.position;
var yearHired = employee.yearHired;
Enter fullscreen mode Exit fullscreen mode

如果我们使用对象解构,它看起来会比以前更简洁,也更省时。对象解构的语法是:如果要获取对象中的属性,我们使用 and {},并在其中指定要提取的属性;如果要从数组中获取数据,我们使用[]

let { firstName, lastName, position, yearHired } = employee;
Enter fullscreen mode Exit fullscreen mode

如果我们想更改要提取的变量名称,可以使用以下propertyName:newName语法。在此示例中,变量的值fName将保存属性的值firstNamelName变量将保存属性的值lastName

let { firstName: fName, lastName: lName, position, yearHired } = employee;
Enter fullscreen mode Exit fullscreen mode

解构时也可以设置默认值。在本例中,如果对象中的firstName属性持有一个值,那么解构时该变量将持有默认值undefinedfirstName"Mark"

let { firstName = "Mark", lastName: lName, position, yearHired } = employee;
Enter fullscreen mode Exit fullscreen mode

47. 什么是ES6 Modules

模块让我们能够将代码库拆分成多个文件,以提高可维护性,这样我们就可以避免将所有代码都放在一个大文件中(呸)。在 ES6 支持模块之前, JavaScript中有两种流行的模块系统用于提高代码可维护性

  • CommonJS - Node.js
  • AMD(异步模块定义)-浏览器

基本上,使用模块的语法很简单,
import用于从另一个文件或多个功能或值中获取
export功能,同时用于从一个文件或多个功能或值中公开功能。

在文件或命名导出中导出功能

使用 ES5(CommonJS)

// Using ES5 CommonJS - helpers.js
exports.isNull = function (val) {
  return val === null;
}

exports.isUndefined = function (val) {
  return val === undefined;
}

exports.isNullOrUndefined = function (val) {
  return exports.isNull(val) || exports.isUndefined(val);
}
Enter fullscreen mode Exit fullscreen mode

使用 ES6 模块

// Using ES6 Modules - helpers.js
export function isNull(val){
  return val === null;
}

export function isUndefined(val) {
  return val === undefined;
}

export function isNullOrUndefined(val) {
  return isNull(val) || isUndefined(val);
}


Enter fullscreen mode Exit fullscreen mode

在另一个文件中导入功能

// Using ES5 (CommonJS) - index.js
const helpers = require('./helpers.js'); // helpers is an object
const isNull = helpers.isNull;
const isUndefined = helpers.isUndefined;
const isNullOrUndefined = helpers.isNullOrUndefined;

// or if your environment supports Destructuring
const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js');
Enter fullscreen mode Exit fullscreen mode
// ES6 Modules - index.js
import * as helpers from './helpers.js'; // helpers is an object

// or 

import { isNull, isUndefined, isNullOrUndefined as isValid } from './helpers.js';

// using "as" for renaming named exports
Enter fullscreen mode Exit fullscreen mode

导出文件或默认导出中的单个功能

使用 ES5(CommonJS)

// Using ES5 (CommonJS) - index.js
class Helpers {
  static isNull(val) {
    return val === null;
  }

  static isUndefined(val) {
    return val === undefined;
  }

  static isNullOrUndefined(val) {
    return this.isNull(val) || this.isUndefined(val);
  }
}


module.exports = Helpers;
Enter fullscreen mode Exit fullscreen mode

使用 ES6 模块

// Using ES6 Modules - helpers.js
class Helpers {
  static isNull(val) {
    return val === null;
  }

  static isUndefined(val) {
    return val === undefined;
  }

  static isNullOrUndefined(val) {
    return this.isNull(val) || this.isUndefined(val);
  }
}

export default Helpers
Enter fullscreen mode Exit fullscreen mode

从另一个文件导入单个功能

使用 ES5(CommonJS)

// Using ES5 (CommonJS) - index.js
const Helpers = require('./helpers.js'); 
console.log(Helpers.isNull(null));
Enter fullscreen mode Exit fullscreen mode

使用 ES6 模块

import Helpers from '.helpers.js'
console.log(Helpers.isNull(null));
Enter fullscreen mode Exit fullscreen mode

这是使用ES6 模块的基础知识。我不会详细解释所有关于模块的内容,因为这是一个很宽泛的话题,而且我的帖子已经很长了。

48. 对象是什么Set以及它是如何工作的?

Set对象ES6 的一项功能,可用于存储唯一值、原始值对象引用。Set 中的值只能出现一次。它使用SameValueZero算法检查集合对象中是否存在某个值

我们可以Set使用构造函数创建实例Set,并且可以选择传递一个Iterable作为初始值。


const set1 = new Set();
const set2 = new Set(["a","b","c","d","d","e"]);

Enter fullscreen mode Exit fullscreen mode

Set我们可以使用该方法向实例添加新值add,由于该方法add返回Set对象,因此可以进行链式add调用。如果对象中已存在该值,Set则不会再次添加。


set2.add("f");
set2.add("g").add("h").add("i").add("j").add("k").add("k");
// the last "k" will not be added to the set object because it already exists

Enter fullscreen mode Exit fullscreen mode

Set我们可以使用该方法从实例中删除一个值delete,该方法返回一个boolean指示,指示对象true中是否存在该值Set,并false指示该值不存在。


set2.delete("k") // returns true because "k" exists in the set object
set2.delete("z") // returns false because "z" does not exists in the set object

Enter fullscreen mode Exit fullscreen mode

Set我们可以使用该方法检查实例中是否存在特定值has


set2.has("a") // returns true because "a" exists in the set object
set2.has("z") // returns false because "z" does not exists in the set object

Enter fullscreen mode Exit fullscreen mode

Set我们可以使用该属性获取实例的长度size


set2.size // returns 10

Enter fullscreen mode Exit fullscreen mode

Set我们可以使用 删除或移除实例中的所有元素clear


set2.clear(); // clears the set data

Enter fullscreen mode Exit fullscreen mode

我们可以使用该Set对象删除数组中的重复元素。


const numbers = [1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 5];
const uniqueNums = [...new Set(numbers)]; // has a value of [1,2,3,4,5,6,7,8]

Enter fullscreen mode Exit fullscreen mode

49.什么是回调函数?

回调函数是在稍后时间点调用的函数。

const btnAdd = document.getElementById('btnAdd');

btnAdd.addEventListener('click', function clickCallback(e) {
    // do something useless
});
Enter fullscreen mode Exit fullscreen mode

在此示例中,我们等待id 为btnAddclick event的元素中的,如果是,则执行该函数。回调函数将某些功能添加到某些数据或事件中。Array 中的方法需要回调作为参数。回调的一个很好的类比是,当您打电话给某人,如果他们没有接听,您会留言,并希望他们回调。打电话给某人或留言的行为事件或数据,而回调您期望稍后发生的操作clickedclickCallbackreducefiltermap

50.什么是Promise

Promises是JavaScript中处理异步操作的一种方式。表示异步操作的值。Promises是为了解决在我们使用回调函数之前执行和处理异步代码的问题而创建的。

fs.readFile('somefile.txt', function (e, data) {
  if (e) {
    console.log(e);
  }
  console.log(data);
});
Enter fullscreen mode Exit fullscreen mode

这种方法的问题在于,如果我们在回调函数内部再执行一个异步操作,代码就会变得混乱且难以阅读。这种代码被称为“回调地狱”

//Callback Hell yucksss
fs.readFile('somefile.txt', function (e, data) {
  //your code here
  fs.readdir('directory', function (e, files) {
    //your code here
    fs.mkdir('directory', function (e) {
      //your code here
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

如果我们在此代码中使用承诺,它将更具可读性、易于理解和易于维护。

promReadFile('file/path')
  .then(data => {
    return promReaddir('directory');
  })
  .then(data => {
    return promMkdir('directory');
  })
  .catch(e => {
    console.log(e);
  })
Enter fullscreen mode Exit fullscreen mode

承诺有 3 种不同的状态。

待处理- Promise 的初始状态。由于操作尚未完成,因此 Promise 的结果尚不清楚。

已完成- 异步操作已完成并成功返回结果值。

已拒绝- 异步操作失败,并且有失败的原因

已解决- 如果承诺已被履行拒绝

Promise构造函数有两个参数,分别是函数resolvereject
如果异步操作已完成且没有错误,则调用该resolve函数来解析 Promise;如果发生错误,
则调用该reject函数并将错误或原因传递给它。
我们可以使用该方法访问已完成 Promise 的结果.then
,并在该方法中捕获错误.catch。我们在该方法中链​​接多个异步 Promise 操作,.then因为该.then方法返回一个Promise,就像上图中的示例一样。

const myPromiseAsync = (...args) => {
  return new Promise((resolve, reject) => {
    doSomeAsync(...args, (error, data) => {
      if (error) {
        reject(error);
      } else {
        resolve(data);
      }
    })
  })
}

myPromiseAsync()
  .then(result => {
    console.log(result);
  })
  .catch(reason => {
    console.log(reason);
  })
Enter fullscreen mode Exit fullscreen mode

我们可以创建一个辅助函数,将带有回调的异步操作转换为 Promise。它的工作原理类似于Node 核心模块中的promisifyutil实用函数。

const toPromise = (asyncFuncWithCallback) => {
  return (...args) => {
    return new Promise((res, rej) => {
      asyncFuncWithCallback(...args, (e, result) => {
        return e ? rej(e) : res(result);
      });
    });
  }
}

const promReadFile = toPromise(fs.readFile);

promReadFile('file/path')
  .then((data) => {
    console.log(data);
  })
  .catch(e => console.log(e));
Enter fullscreen mode Exit fullscreen mode

51.什么是async/await以及它是如何工作的?

async/await是JavaScript中编写异步或非阻塞代码的新方法。它建立在Promises之上。它使编写的异步代码比
PromisesCallbacks更具可读性和简洁性。但是在使用此功能之前,您必须学习Promises的基础知识,因为正如我之前所说,它建立在Promises之上,这意味着它仍然在底层使用Promises 。

使用承诺。

function callApi() {
  return fetch("url/to/api/endpoint")
    .then(resp => resp.json())
    .then(data => {
      //do something with "data"
    }).catch(err => {
      //do something with "err"
    });
}

Enter fullscreen mode Exit fullscreen mode

使用 Async/Await。

注意:我们使用旧的try/catch语句来捕获try语句内任何异步操作中发生的任何错误

async function callApi() {
  try {
    const resp = await fetch("url/to/api/endpoint");
    const data = await resp.json();
    //do something with "data"
  } catch (e) {
    //do something with "err"
  }
}
Enter fullscreen mode Exit fullscreen mode

注意:函数声明前的async关键字使函数隐式返回一个Promise

const giveMeOne = async () => 1;

giveMeOne()
  .then((num) => {
    console.log(num); // logs 1
  });
Enter fullscreen mode Exit fullscreen mode

注意await关键字只能异步函数中使用在任何其他非异步函数中使用await关键字都会引发错误。await关键字等待右侧表达式(可能是Promise)返回后再执行下一行代码。

const giveMeOne = async () => 1;

function getOne() {
  try {
    const num = await giveMeOne();
    console.log(num);
  } catch (e) {
    console.log(e);
  }
}

//Throws a Compile-Time Error = Uncaught SyntaxError: await is only valid in an async function

async function getTwo() {
  try {
    const num1 = await giveMeOne(); //finishes this async operation first before going to
    const num2 = await giveMeOne(); //this line
    return num1 + num2;
  } catch (e) {
    console.log(e);
  }
}

await getTwo(); // returns 2
Enter fullscreen mode Exit fullscreen mode

52.Spread运算符Rest运算符有什么区别

Spread运算符Rest 参数具有相同的运算符,...区别在于,Spread 运算符我们将数组中的单个数据赋予传播到另一个数据,而Rest 参数用于函数或数组中以获取所有参数或值并将它们放入数组中或提取其中的一些部分。

function add(a, b) {
  return a + b;
};

const nums = [5, 6];
const sum = add(...nums);
console.log(sum);

Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们在调用函数时使用了展开运算符来展开add数组因此,参数 的值为5 的值为6。因此和为11numsab

function add(...rest) {
  return rest.reduce((total,current) => total + current);
};

console.log(add(1, 2)); // logs 3
console.log(add(1, 2, 3, 4, 5)); // logs 15
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们有一个函数add,它接受任意数量的参数并将它们全部加起来并返回总数。

const [first, ...others] = [1, 2, 3, 4, 5];
console.log(first); //logs 1
console.log(others); //logs [2,3,4,5]
Enter fullscreen mode Exit fullscreen mode

在另一个例子中,我们使用Rest 运算符提取所有剩余的数组值并将它们放入数组中others(第一项除外)。

53.什么是默认参数

默认参数是在JavaScript中定义默认变量的新方法,它在ES6ECMAScript 2015版本中可用

//ES5 Version
function add(a,b){
  a = a || 0;
  b = b || 0;
  return a + b;
}

//ES6 Version
function add(a = 0, b = 0){
  return a + b;
}
//If we don't pass any argument for 'a' or 'b' then 
// it's gonna use the "default parameter" value which is 0
add(1); // returns 1 
Enter fullscreen mode Exit fullscreen mode

我们还可以在默认参数中使用解构

function getFirst([first, ...rest] = [0, 1]) {
  return first;
}

getFirst();  // returns 0
getFirst([10,20,30]);  // returns 10

function getArr({ nums } = { nums: [1, 2, 3, 4] }){
    return nums;
}

getArr(); // returns [1, 2, 3, 4]
getArr({nums:[5,4,3,2,1]}); // returns [5,4,3,2,1]
Enter fullscreen mode Exit fullscreen mode

我们还可以将先定义的参数用于后定义的参数。

function doSomethingWithValue(value = "Hello World", callback = () => { console.log(value) }) {
  callback();
}
doSomethingWithValue(); //logs "Hello World"
Enter fullscreen mode Exit fullscreen mode

54.什么是包装对象

原始值,stringnumberboolean(除了和null),undefined尽管不是,但也具有属性和方法objects

let name = "marko";

console.log(typeof name); // logs  "string"
console.log(name.toUpperCase()); // logs  "MARKO"
Enter fullscreen mode Exit fullscreen mode

name是一个primitive string没有属性和方法的值,但在这个例子中,我们调用一个toUpperCase()不会引发错误但返回的方法MARKO

这样做的原因是,该primitive被临时转换或强制转换为,object因此name变量的行为类似于objectprimitive除了null和之外,每个undefined都有包装器对象。包装器对象包括StringNumber。在这种情况下,调用在后台看起来像这样。BooleanSymbolBigIntname.toUpperCase()


console.log(new String(name).toUpperCase()); // logs  "MARKO"

Enter fullscreen mode Exit fullscreen mode

当我们访问完属性或调用完方法后,新创建的对象会被立即丢弃。

55.隐式强制转换和显式强制转换有什么区别

隐式强制转换是一种将值转换为另一种类型的方法,无需程序员直接或手动执行。

假设我们有下面一个例子。

console.log(1 + '6');
console.log(false + true);
console.log(6 * '2');

Enter fullscreen mode Exit fullscreen mode

第一条 语句console.log输出16。在其他语言中,这会引发编译时错误,但在JavaScript中,被1转换为 ,string然后与+运算符连接。我们什么也没做,但JavaScript会自动为我们进行转换。
第二 console.log语句输出1,它将 转换为 ,false结果boolean0true1,因此结果为1第三条
语句输出,它将 转换为 ,然后再进行乘法运算,因此结果为。JavaScript强制转换规则 console.log12'2'number6 * 212

显式强制则是将值转换为另一种类型的方式,我们(程序员明确地这样做。

console.log(1 + parseInt('6'));
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们使用parseInt函数将转换为'6'number然后添加16使用+运算符。

56.什么是NaN?以及如何检查一个值是否是NaN

NaN表示“非数字” ,是JavaScript中的一个值,其结果是将数字转换为或执行运算后变为非数字值,从而得到结果NaN

let a;

console.log(parseInt('abc'));
console.log(parseInt(null));
console.log(parseInt(undefined));
console.log(parseInt(++a));
console.log(parseInt({} * 10));
console.log(parseInt('abc' - 2));
console.log(parseInt(0 / 0));
console.log(parseInt('10a' * 10));

Enter fullscreen mode Exit fullscreen mode

JavaScript有一个内置方法isNaN可以测试值是否为isNaN值。但这个函数的行为很奇怪。

console.log(isNaN()); //logs true
console.log(isNaN(undefined)); //logs true
console.log(isNaN({})); //logs true
console.log(isNaN(String('a'))); //logs true
console.log(isNaN(() => { })); //logs true
Enter fullscreen mode Exit fullscreen mode

即使我们传递的值不是,所有这些console.log语句都会返回trueNaN

ES6ECMAScript 2015中,建议我们使用Number.isNaN方法,因为它确实会检查值是否确实存在NaN,或者我们可以创建自己的辅助函数来检查这个问题,因为在JavaScript NaN中,这是唯一不等于其自身的值。

function checkIfNaN(value) {
  return value !== value;
}
Enter fullscreen mode Exit fullscreen mode

57.如何检查一个值是否是数组

我们可以使用Array全局对象中的方法检查一个值是否为数组。如果传递给它的参数是数组,则返回 true ,否则返回 false。Array.isArray

console.log(Array.isArray(5));  //logs false
console.log(Array.isArray("")); //logs false
console.log(Array.isArray()); //logs false
console.log(Array.isArray(null)); //logs false
console.log(Array.isArray({ length: 5 })); //logs false

console.log(Array.isArray([])); //logs true
Enter fullscreen mode Exit fullscreen mode

如果您的环境不支持此方法,您可以使用 polyfill 实现。

   function isArray(value){
     return Object.prototype.toString.call(value) === "[object Array]"
   }
Enter fullscreen mode Exit fullscreen mode

58. 如何在不使用%或模运算符的情况下检查一个数字是否为偶数?

我们可以使用按位与&运算符来解决这个问题。它对&操作数进行运算,并将它们视为二进制值,然后执行运算。

function isEven(num) {
  if (num & 1) {
    return false;
  } else {
    return true;
  }
};
Enter fullscreen mode Exit fullscreen mode

0二进制为000。二进制
1001。二进制
2010。
3二进制011。二进制100。二进制101。二进制110。二进制111。等等...
4
5
6
7

a b a & b
0 0 0
0 1 0
1 0 0
1 1 1

所以当我们执行console.log这个表达式时5 & 1它返回1。好的,首先,&运算符将两个数字都转换为二进制,因此5变为1011001 。然后,它使用按位运算符 比较每一位(0 和 1)。101 001。从表中我们可以看出,只有AND 相等时,结果才会是
& 1a b1

101 & 001
101
001
001
  • 因此我们首先比较最左边的1&0结果应该是多少0
  • 然后我们比较中间位的0&0结果应该是多少0
  • 然后我们比较最后一位的1&1结果应该是多少1
  • 然后将二进制结果001转换为十进制数1

如果我们用console.log这个表达式,4 & 1它将返回0。知道的最后一位4是,0并且0 & 1将是0。如果你很难理解这一点,我们可以使用递归函数来解决这个问题。

function isEven(num) {
  if (num < 0 || num === 1) return false;
  if (num == 0) return true;
  return isEven(num - 2);
}
Enter fullscreen mode Exit fullscreen mode

59.如何检查对象中是否存在某个属性?

有三种方法可以检查对象中是否存在某个属性。

首先,使用in运算符。使用运算符的语法in如下。如果属性存在,则propertyname in object返回,否则返回truefalse

const o = { 
  "prop" : "bwahahah",
  "prop2" : "hweasa"
};

console.log("prop" in o); //This logs true indicating the property "prop" is in "o" object
console.log("prop1" in o); //This logs false indicating the property "prop" is not in  "o" object
Enter fullscreen mode Exit fullscreen mode

其次,使用hasOwnProperty对象中的方法。JavaScript 中的所有对象都支持此方法。true如果属性存在,则返回 ,否则返回false

//Still using the o object in the first example.
console.log(o.hasOwnProperty("prop2")); // This logs true
console.log(o.hasOwnProperty("prop1")); // This logs false
Enter fullscreen mode Exit fullscreen mode

第三,使用括号表示法obj["prop"]。如果属性存在,则返回该属性的值,否则返回undefined

//Still using the o object in the first example.
console.log(o["prop"]); // This logs "bwahahah"
console.log(o["prop1"]); // This logs undefined
Enter fullscreen mode Exit fullscreen mode

60.什么是AJAX

AJAX代表异步 JavaScript 和 XML。它是一组用于异步显示数据的相关技术。这意味着我们可以在不重新加载网页的情况下将数据发送到服务器并从服务器获取数据。

用于AJAX的技术。

  • HTML——网页结构
  • CSS——网页样式
  • JavaScript——网页的行为和DOM的更新
  • XMLHttpRequest API - 用于从服务器发送和检索数据
  • PHP、Python、Nodejs - 一些服务器端语言

61. JavaScript 中创建对象的方式有哪些?

使用对象字面量

  const o = {
   name: "Mark",
   greeting() {
      return `Hi, I'm ${this.name}`;
   }
  };

  o.greeting(); //returns "Hi, I'm Mark"
Enter fullscreen mode Exit fullscreen mode

使用构造函数

function Person(name) {
   this.name = name;
}

Person.prototype.greeting = function () {
   return `Hi, I'm ${this.name}`;
}

const mark = new Person("Mark");

mark.greeting(); //returns "Hi, I'm Mark"
Enter fullscreen mode Exit fullscreen mode

使用Object.create方法。

const n = {
   greeting() {
      return `Hi, I'm ${this.name}`;
   }
};

const o = Object.create(n); // sets the prototype of "o" to be "n"

o.name = "Mark";

console.log(o.greeting()); // logs "Hi, I'm Mark"


Enter fullscreen mode Exit fullscreen mode

Object.seal62.和方法有什么区别Object.freeze

这两种方法的区别在于,当我们Object.freeze对一个对象使用该方法时,该对象的属性是不可变的,这意味着我们无法更改或编辑这些属性的值。而在该Object.seal方法中,我们可以更改这些现有属性,但不能向对象添加新属性。

in63.对象中的运算符和方法有什么区别hasOwnProperty

众所周知,这两个函数都会检查对象中是否存在某个属性。返回truefalse。它们的区别在于,如果在当前对象中未找到该属性,in运算符还会检查对象的原型链,hasOwnProperty而该方法仅检查当前对象中是否存在该属性,而忽略原型链

// We'll still use the object in the previous question.
console.log("prop" in o); // This logs true;
console.log("toString" in o); // This logs true, the toString method is available in this object's prototype which is the Object.prototype


console.log(o.hasOwnProperty("prop")); // This logs true
console.log(o.hasOwnProperty("toString")); // This logs false, does not check the object's prototype
Enter fullscreen mode Exit fullscreen mode

64. JavasScript 中处理异步代码的方法有哪些?

65.函数表达式函数声明有什么区别

假设我们有下面一个例子。

hoistedFunc();
notHoistedFunc();

function hoistedFunc(){
  console.log("I am hoisted");
}

var notHoistedFunc = function(){
  console.log("I will not be hoisted!");
}
Enter fullscreen mode Exit fullscreen mode

调用notHoistedFunc会抛出错误,而hoistedFunc调用不会,因为hoistedFunc提升了,而 却notHoistedFunc没有。
点击此处了解提升

66. 函数有多少种调用方式?

JavaScript有四种调用函数的方式调用方式决定了该函数的值或“所有者”对象。this

  • 作为函数调用- 如果函数不是作为方法、构造函数或方法调用,applycall它将作为函数调用。该函数的“所有者”对象将是window对象。
  //Global Scope

  function add(a,b){
    console.log(this);
    return a + b;
  }  

  add(1,5); // logs the "window" object and returns 6

  const o = {
    method(callback){
      callback();
    }
  }

  o.method(function (){
      console.log(this); // logs the "window" object
  });
Enter fullscreen mode Exit fullscreen mode
  • 作为方法调用- 如果对象的某个属性具有某个函数的值,我们称之为方法。当该方法调用时,该方法的值this就是该对象。
   const details = {
     name : "Marko",
     getName(){
       return this.name;
     }
   }

   details.getName(); // returns Marko
   // the "this" value inside "getName" method will be the "details" object 
Enter fullscreen mode Exit fullscreen mode
  • 作为构造函数调用- 如果函数调用new时前面带有关键字,则该函数被称为function constructor。将创建一个空对象并this指向该对象。
function Employee(name, position, yearHired) {
  // creates an empty object {}
  // then assigns the empty object to the "this" keyword
  // this = {};
  this.name = name;
  this.position = position;
  this.yearHired = yearHired;
  // inherits from Employee.prototype
  // returns the "this" value implicitly if no 
  // explicit return statement is specified
};

const emp = new Employee("Marko Polo", "Software Developer", 2017);

Enter fullscreen mode Exit fullscreen mode
  • 使用applycall方法调用- 如果我们想要明确指定this函数的值或“所有者”对象,可以使用这些方法。这些方法适用于所有函数。
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]);  //the "this" object inside the "reduceAdd" function will be "obj1"
reduceAdd.call(obj2, 1, 2, 3, 4, 5); //the "this" object inside the "reduceAdd" function will be "obj2"
Enter fullscreen mode Exit fullscreen mode

67.什么是记忆化(memoization)以及它有什么用处?

记忆化是指构建一个能够记住先前计算结果或值的函数的过程。
创建记忆化函数的目的是,如果该函数在上次计算中已经使用相同的参数执行过,则可以避免再次计算该函数。这可以节省时间,但缺点是,保存先前的结果会消耗更多内存。

68. 实现记忆辅助函数。


function memoize(fn) {
  const cache = {};
  return function (param) {
    if (cache[param]) {
      console.log('cached');
      return cache[param];
    } else {
      let result = fn(param);
      cache[param] = result;
      console.log(`not cached`);
      return result;
    }
  }
}

const toUpper = (str ="")=> str.toUpperCase();

const toUpperMemoized = memoize(toUpper);

toUpperMemoized("abcdef");
toUpperMemoized("abcdef");
Enter fullscreen mode Exit fullscreen mode

memoize辅助函数仅适用于接受一个参数的函数。我们需要创建一个接受多个参数的memoize辅助函数

const slice = Array.prototype.slice;
function memoize(fn) {
  const cache = {};
  return (...args) => {
    const params = slice.call(args);
    console.log(params);
    if (cache[params]) {
      console.log('cached');
      return cache[params];
    } else {
      let result = fn(...args);
      cache[params] = result;
      console.log(`not cached`);
      return result;
    }
  }
}
const makeFullName = (fName, lName) => `${fName} ${lName}`;
const reduceAdd = (numbers, startingValue = 0) => numbers.reduce((total, cur) => total + cur, startingValue);

const memoizedMakeFullName = memoize(makeFullName);
const memoizedReduceAdd = memoize(reduceAdd);

memoizedMakeFullName("Marko", "Polo");
memoizedMakeFullName("Marko", "Polo");

memoizedReduceAdd([1, 2, 3, 4, 5], 5);
memoizedReduceAdd([1, 2, 3, 4, 5], 5);

Enter fullscreen mode Exit fullscreen mode

69. 为什么要typeof null返回object?如何检查一个值是否为null

typeof null == 'object'始终会返回,因为这是JavaScript诞生以来true的实现。有人提出将其更改为 ,但被拒绝了,因为这会导致现有项目和软件出现更多错误。nulltypeof null == 'object'typeof null == 'null'

我们可以使用===严格相等运算符来检查某个值是否为null

  function isNull(value){
    return value === null;
  }
Enter fullscreen mode Exit fullscreen mode

70.new关键字起什么作用?

该关键字与构造new函数一起使用来在JavaScript创建对象

假设我们有下面的示例代码。

function Employee(name, position, yearHired) {
  this.name = name;
  this.position = position;
  this.yearHired = yearHired;
};

const emp = new Employee("Marko Polo", "Software Developer", 2017);

Enter fullscreen mode Exit fullscreen mode

new关键字有 4 个作用。

  • 创建一个空对象。
  • 将该空对象分配给该this值。
  • 函数将从functionName.prototype继承
  • this如果未return使用显式语句,则返回。

在上图中,它首先会创建一个空对象{},然后
this值赋给该空对象this = {},并为其添加属性this。由于我们没有显式return声明,它会自动返回this

感谢大家阅读这篇文章。

祝您有美好的一天😃,新年快乐🎆🎆🎆。

文章来源:https://dev.to/macmacky/70-javascript-interview-questions-5gfi
PREV
一篇你能理解的 Redux 简介,祝你拥有美好的一天😃!太棒了👏🏻
NEXT
我如何构建 React 项目