看完這幾道 JavaScript 面試題,讓你與考官對答如流(中)

做者:Mark A
譯者:前端小智
來源:dev
點贊再看,養成習慣

本文 GitHub https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。javascript

因爲篇幅過長,我將此係列分紅上中下三篇,上篇:前端

看完這幾道 JavaScript 面試題,讓你與考官對答如流(上)java

26. 什麼是 IIFE,它的用途是什麼?

IIFE或當即調用的函數表達式是在建立或聲明後將被調用或執行的函數。 建立IIFE的語法是,將function (){}包裹在在括號()內,而後再用另外一個括號()調用它,如:(function(){})()react

(function(){
  ...
} ());

(function () {
  ...
})();

(function named(params) {
  ...
})();

(() => {

});

(function (global) {
  ...
})(window);

const utility = (function () {
  return {
    ...
  }
})

這些示例都是有效的IIFE。 倒數第二個救命代表咱們能夠將參數傳遞給IIFE函數。 最後一個示例代表,咱們能夠將IIFE的結果保存到變量中,以便稍後使用。git

IIFE的一個主要做用是避免與全局做用域內的其餘變量命名衝突或污染全局命名空間,來個例子。github

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

假設咱們引入了一個omelibr.js的連接,它提供了一些咱們在代碼中使用的全局函數,可是這個庫有兩個方法咱們沒有使用:createGraphdrawGraph,由於這些方法都有bug。咱們想實現本身的createGraphdrawGraph方法。面試

解決此問題的一種方法是直接覆蓋:編程

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

當咱們使用這個解決方案時,咱們覆蓋了庫提供給咱們的那兩個方法。segmentfault

另外一種方式是咱們本身更名稱:數組

<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>

在此解決方案中,咱們要聲明瞭graphUtility 變量,用來保存IIFE執行的結果,該函數返回一個包含兩個方法createGraphdrawGraph的對象。

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元素,它有5li子元素。 當咱們單擊單個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]); // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 15

30. Function.prototype.bind 的用途是什麼?

bind() 方法建立一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其他參數將做爲新函數的參數,供調用時使用。

import React from 'react';

class MyComponent extends React.Component {
     constructor(props){
          super(props); 
          this.state = {
             value : ""
          }  
          this.handleChange = this.handleChange.bind(this); 
          // 將 「handleChange」 方法綁定到 「MyComponent」 組件
     }

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

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

31. 什麼是函數式編程? JavaScript 的哪些特性使其成爲函數式語言的候選語言?

函數式編程(一般縮寫爲FP)是經過編寫純函數,避免共享狀態、可變數據、反作用 來構建軟件的過程。數式編程是聲明式 的而不是命令式 的,應用程序的狀態是經過純函數流動的。與面向對象編程造成對比,面向對象中應用程序的狀態一般與對象中的方法共享和共處。

函數式編程是一種編程範式 ,這意味着它是一種基於一些基本的定義原則(如上所列)思考軟件構建的方式。固然,編程範示的其餘示例也包括面向對象編程和過程編程。

函數式的代碼每每比命令式或面向對象的代碼更簡潔,更可預測,更容易測試 - 但若是不熟悉它以及與之相關的常見模式,函數式的代碼也可能看起來更密集雜亂,而且 相關文獻對新人來講是很差理解的。

JavaScript支持閉包和高階函數是函數式編程語言的特色。

32. 什麼是高階函數?

高階函數只是將函數做爲參數或返回值的函數。

function higherOrderFunction(param,callback){
    return callback(param);
}

33. 爲何函數被稱爲一等公民?

在JavaScript中,函數不只擁有一切傳統函數的使用方式(聲明和調用),並且能夠作到像簡單值同樣賦值(var func = function(){})、傳參(function func(x,callback){callback();})、返回(function(){return function(){}}),這樣的函數也稱之爲第一級函數(First-class Function)。不只如此,JavaScript中的函數還充當了類的構造函數的做用,同時又是一個Function類的實例(instance)。這樣的多重身份讓JavaScript的函數變得很是重要。

34. 手動實現 Array.prototype.map 方法

map() 方法建立一個新數組,其結果是該數組中的每一個元素都調用一個提供的函數後返回的結果。

function map(arr, mapCallback) {
  // 首先,檢查傳遞的參數是否正確。
  if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { 
    return [];
  } else {
    let result = [];
    // 每次調用此函數時,咱們都會建立一個 result 數組
    // 由於咱們不想改變原始數組。
    for (let i = 0, len = arr.length; i < len; i++) {
      result.push(mapCallback(arr[i], i, arr)); 
      // 將 mapCallback 返回的結果 push 到 result 數組中
    }
    return result;
  }
}

35. 手動實現Array.prototype.filter方法

filter() 方法建立一個新數組, 其包含經過所提供函數實現的測試的全部元素。

function filter(arr, filterCallback) {
  // 首先,檢查傳遞的參數是否正確。
  if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') 
  {
    return [];
  } else {
    let result = [];
     // 每次調用此函數時,咱們都會建立一個 result 數組
     // 由於咱們不想改變原始數組。
    for (let i = 0, len = arr.length; i < len; i++) {
      // 檢查 filterCallback 的返回值是不是真值
      if (filterCallback(arr[i], i, arr)) { 
      // 若是條件爲真,則將數組元素 push 到 result 中
        result.push(arr[i]);
      }
    }
    return result; // return the result array
  }
}

36. 手動實現Array.prototype.reduce方法

reduce() 方法對數組中的每一個元素執行一個由您提供的reducer函數(升序執行),將其結果彙總爲單個返回值。

function reduce(arr, reduceCallback, initialValue) {
  // 首先,檢查傳遞的參數是否正確。
  if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function') 
  {
    return [];
  } else {
    // 若是沒有將initialValue傳遞給該函數,咱們將使用第一個數組項做爲initialValue
    let hasInitialValue = initialValue !== undefined;
    let value = hasInitialValue ? initialValue : arr[0];
   、

    // 若是有傳遞 initialValue,則索引從 1 開始,不然從 0 開始
    for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
      value = reduceCallback(value, arr[i], i, arr); 
    }
    return value;
  }
}

37. arguments 的對象是什麼?

arguments對象是函數中傳遞的參數值的集合。它是一個相似數組的對象,由於它有一個length屬性,咱們可使用數組索引表示法arguments[1]來訪問單個值,但它沒有數組中的內置方法,如:forEachreducefiltermap

咱們可使用Array.prototype.slicearguments對象轉換成一個數組。

function one() {
  return Array.prototype.slice.call(arguments);
}

注意:箭頭函數中沒有arguments對象。

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

當咱們調用函數four時,它會拋出一個ReferenceError: arguments is not defined error。使用rest語法,能夠解決這個問題。

const four = (...args) => args;

這會自動將全部參數值放入數組中。

38. 如何建立一個沒有 prototype(原型)的對象?

咱們可使用Object.create方法建立沒有原型的對象。

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

const o2 = Object.create(null);
console.log(o2.toString());
// throws an error o2.toString is not a function

39. 爲何在調用這個函數時,代碼中的b會變成一個全局變量?

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

myFunc();

緣由是賦值運算符是從右到左的求值的。這意味着當多個賦值運算符出如今一個表達式中時,它們是從右向左求值的。因此上面代碼變成了這樣:

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

myFunc();

首先,表達式b = 0求值,在本例中b沒有聲明。所以,JS引擎在這個函數外建立了一個全局變量b,以後表達式b = 0的返回值爲0,並賦給新的局部變量a

咱們能夠經過在賦值以前先聲明變量來解決這個問題。

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

40. ECMAScript 是什麼?

ECMAScript 是編寫腳本語言的標準,這意味着JavaScript遵循ECMAScript標準中的規範變化,由於它是JavaScript的藍圖。

ECMAScript 和 Javascript,本質上都跟一門語言有關,一個是語言自己的名字,一個是語言的約束條件
只不過發明JavaScript的那我的(Netscape公司),把東西交給了ECMA(European Computer Manufacturers Association),這我的規定一下他的標準,由於當時有java語言了,又想強調這個東西是讓ECMA這我的定的規則,因此就這樣一個神奇的東西誕生了,這個東西的名稱就叫作ECMAScript。

javaScript = ECMAScript + DOM + BOM(自認爲是一種廣義的JavaScript)

ECMAScript說什麼JavaScript就得作什麼!

JavaScript(狹義的JavaScript)作什麼都要問問ECMAScript我能不能這樣幹!若是不能我就錯了!能我就是對的!

——忽然感受JavaScript好沒有尊嚴,爲啥要搞我的出來約束本身,

那我的被創造出來也好委屈,本身被創造出來徹底是由於要約束JavaScript。

41. ES6或ECMAScript 2015有哪些新特性?

  • 箭頭函數
  • 模板字符串
  • 增強的對象字面量
  • 對象解構
  • Promise
  • 生成器
  • 模塊
  • Symbol
  • 代理
  • Set
  • 函數默認參數
  • rest 和展開
  • 塊做用域

42. var,letconst的區別是什麼?

var聲明的變量會掛載在window上,而letconst聲明的變量不會:

var a = 100;
console.log(a,window.a);    // 100 100

let b = 10;
console.log(b,window.b);    // 10 undefined

const c = 1;
console.log(c,window.c);    // 1 undefined

var聲明變量存在變量提高,letconst不存在變量提高:

console.log(a); // undefined  ===>  a已聲明還沒賦值,默認獲得undefined值
var a = 100;

console.log(b); // 報錯:b is not defined  ===> 找不到b這個變量
let b = 10;

console.log(c); // 報錯:c is not defined  ===> 找不到c這個變量
const c = 10;

letconst聲明造成塊做用域

if(1){
  var a = 100;
  let b = 10;
}

console.log(a); // 100
console.log(b)  // 報錯:b is not defined  ===> 找不到b這個變量

-------------------------------------------------------------

if(1){
  var a = 100;
  const c = 1;
}
console.log(a); // 100
console.log(c)  // 報錯:c is not defined  ===> 找不到c這個變量

同一做用域下letconst不能聲明同名變量,而var能夠

var a = 100;
console.log(a); // 100

var a = 10;
console.log(a); // 10
-------------------------------------
let a = 100;
let a = 10;

//  控制檯報錯:Identifier 'a' has already been declared  ===> 標識符a已經被聲明瞭。

暫存死區

var a = 100;

if(1){
    a = 10;
    //在當前塊做用域中存在a使用let/const聲明的狀況下,給a賦值10時,只會在當前做用域找變量a,
    // 而這時,還未到聲明時候,因此控制檯Error:a is not defined
    let a = 1;
}

const

/*
*   一、一旦聲明必須賦值,不能使用null佔位。
*
*   二、聲明後不能再修改
*
*   三、若是聲明的是複合類型數據,能夠修改其屬性
*
* */

const a = 100; 

const list = [];
list[0] = 10;
console.log(list);  // [10]

const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj);  // {a:10000,name:'apple'}

43. 什麼是箭頭函數?

箭頭函數表達式的語法比函數表達式更簡潔,而且沒有本身的thisargumentssupernew.target。箭頭函數表達式更適用於那些原本須要匿名函數的地方,而且它不能用做構造函數。

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

//ES6 Version
const getCurrentDate = () => new Date();

在本例中,ES5 版本中有function(){}聲明和return關鍵字,這兩個關鍵字分別是建立函數和返回值所須要的。在箭頭函數版本中,咱們只須要()括號,不須要 return 語句,由於若是咱們只有一個表達式或值須要返回,箭頭函數就會有一個隱式的返回。

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

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

咱們還能夠在箭頭函數中使用與函數表達式和函數聲明相同的參數。若是咱們在一個箭頭函數中有一個參數,則能夠省略括號。

const getArgs = () => arguments

const getArgs2 = (...rest) => rest

箭頭函數不能訪問arguments對象。因此調用第一個getArgs函數會拋出一個錯誤。相反,咱們可使用rest參數來得到在箭頭函數中傳遞的全部參數。

const data = {
  result: 0,
  nums: [1, 2, 3, 4, 5],
  computeResult() {
    // 這裏的「this」指的是「data」對象
    const addAll = () => {
      return this.nums.reduce((total, cur) => total + cur, 0)
    };
    this.result = addAll();
  }
};

箭頭函數沒有本身的this值。 它捕獲詞法做用域函數的this值,在此示例中,addAll函數將複製computeResult 方法中的this值,若是咱們在全局做用域聲明箭頭函數,則this值爲 window 對象。

44. 什麼是類?

類(class)是在 JS 中編寫構造函數的新方法。它是使用構造函數的語法糖,在底層中使用仍然是原型和基於原型的繼承。

//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}`;
        }
   }

重寫方法並從另外一個類繼承。

//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]";
  }
}

因此咱們要怎麼知道它在內部使用原型?

class Something {

}

function AnotherSomething(){

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

console.log(typeof Something); // "function"
console.log(typeof AnotherSomething); // "function"
console.log(as.toString()); // "[object Object]"
console.log(as.toString()); // "[object Object]"
console.log(as.toString === Object.prototype.toString); // true
console.log(s.toString === Object.prototype.toString); // true

45. 什麼是模板字符串?

模板字符串是在 JS 中建立字符串的一種新方法。咱們能夠經過使用反引號使模板字符串化。

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

//ES6 Version
let greet = `Hi I'm Mark`;

在 ES5 中咱們須要使用一些轉義字符來達到多行的效果,在模板字符串不須要這麼麻煩:

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


//ES6 Version
let lastWords = `
    I
    Am
  Iron Man   
`;

在ES5版本中,咱們須要添加\n以在字符串中添加新行。 在模板字符串中,咱們不須要這樣作。

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


//ES6 Version
function greet(name) {
  return `Hello ${name} !`;
}

在 ES5 版本中,若是須要在字符串中添加表達式或值,則須要使用+運算符。 在模板字符串s中,咱們可使用${expr}嵌入一個表達式,這使其比 ES5 版本更整潔。

46. 什麼是對象解構?

對象析構是從對象或數組中獲取或提取值的一種新的、更簡潔的方法。假設有以下的對象:

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

從對象獲取屬性,早期方法是建立一個與對象屬性同名的變量。這種方法很麻煩,由於咱們要爲每一個屬性建立一個新變量。假設咱們有一個大對象,它有不少屬性和方法,用這種方法提取屬性會很麻煩。

var firstName = employee.firstName;
var lastName = employee.lastName;
var position = employee.position;
var yearHired = employee.yearHired;

使用解構方式語法就變得簡潔多了:

{ firstName, lastName, position, yearHired } = employee;

咱們還能夠爲屬性取別名:

let { firstName: fName, lastName: lName, position, yearHired } = employee;

固然若是屬性值爲 undefined 時,咱們還能夠指定默認值:

let { firstName = "Mark", lastName: lName, position, yearHired } = employee;

47. 什麼是 ES6 模塊?

模塊使咱們可以將代碼基礎分割成多個文件,以得到更高的可維護性,而且避免將全部代碼放在一個大文件中。在 ES6 支持模塊以前,有兩個流行的模塊。

  • CommonJS-Node.js
  • AMD(異步模塊定義)-瀏覽器

基本上,使用模塊的方式很簡單,import用於從另外一個文件中獲取功能或幾個功能或值,同時export用於從文件中公開功能或幾個功能或值。

導出

使用 ES5 (CommonJS)

// 使用 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);
}

使用 ES6 模塊

// 使用 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);
}

在另外一個文件中導入函數

// 使用 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');
-------------------------------------------------------

// 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

在文件中導出單個功能或默認導出

使用 ES5 (CommonJS)

// 使用 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;

使用ES6 Modules

// 使用 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

從另外一個文件導入單個功能

使用ES5 (CommonJS)

// 使用 ES5 (CommonJS) - index.js
const Helpers = require('./helpers.js'); 
console.log(Helpers.isNull(null));

使用 ES6 Modules

import Helpers from '.helpers.js'
console.log(Helpers.isNull(null));

48. 什麼是Set對象,它是如何工做的?

Set 對象容許你存儲任何類型的惟一值,不管是原始值或者是對象引用。

咱們可使用Set構造函數建立Set實例。

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

咱們可使用add方法向Set實例中添加一個新值,由於add方法返回Set對象,因此咱們能夠以鏈式的方式再次使用add。若是一個值已經存在於Set對象中,那麼它將再也不被添加。

set2.add("f");
set2.add("g").add("h").add("i").add("j").add("k").add("k");
// 後一個「k」不會被添加到set對象中,由於它已經存在了

咱們可使用has方法檢查Set實例中是否存在特定的值。

set2.has("a") // true
set2.has("z") // true

咱們可使用size屬性得到Set實例的長度。

set2.size // returns 10

可使用clear方法刪除 Set 中的數據。

set2.clear();

咱們可使用Set對象來刪除數組中重複的元素。

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

49. 什麼是回調函數?

回調函數是一段可執行的代碼段,它做爲一個參數傳遞給其餘的代碼,其做用是在須要的時候方便調用這段(回調函數)代碼。

在JavaScript中函數也是對象的一種,一樣對象能夠做爲參數傳遞給函數,所以函數也能夠做爲參數傳遞給另一個函數,這個做爲參數的函數就是回調函數。

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

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

在本例中,咱們等待idbtnAdd的元素中的click事件,若是它被單擊,則執行clickCallback函數。回調函數向某些數據或事件添加一些功能。

數組中的reducefiltermap方法須要一個回調做爲參數。回調的一個很好的類比是,當你打電話給某人,若是他們不接,你留下一條消息,你期待他們回調。調用某人或留下消息的行爲是事件或數據,回調是你但願稍後發生的操做。

50. Promise 是什麼?

Promise 是異步編程的一種解決方案:從語法上講,promise是一個對象,從它能夠獲取異步操做的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。promise有三種狀態:pending(等待態)fulfiled(成功態)rejected(失敗態);狀態一旦改變,就不會再變。創造promise實例後,它會當即執行。

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

若是咱們在回調內部有另外一個異步操做,則此方法存在問題。 咱們將有一個混亂且不可讀的代碼。 此代碼稱爲「回調地獄」

// 回調地獄
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
    })
  })
})

若是咱們在這段代碼中使用promise,它將更易於閱讀、理解和維護。

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

promise有三種不一樣的狀態:

  • pending:初始狀態,完成或失敗狀態的前一個狀態
  • fulfilled:操做成功完成
  • rejected:操做失敗

pending 狀態的 Promise 對象會觸發 fulfilled/rejected 狀態,在其狀態處理方法中能夠傳入參數/失敗信息。當操做成功完成時,Promise 對象的 then 方法就會被調用;不然就會觸發 catch。如:

const myFirstPromise = new Promise((resolve, reject) => {
    setTimeout(function(){
        resolve("成功!"); 
    }, 250);
});

myFirstPromise.then((data) => {
    console.log("Yay! " + data);
}).catch((e) => {...});

因爲篇幅過長,我將此係列分紅上中下三篇,下篇咱們在見。


代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

原文:

https://dev.to/macmacky/70-ja...


交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq44924588...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索