1. 程式人生 > >JavaScript 程式碼簡潔之道

JavaScript 程式碼簡潔之道

測試程式碼質量的唯一方式:別人看你程式碼時說 f * k 的次數。

程式碼質量與其整潔度成正比。乾淨的程式碼,既在質量上較為可靠,也為後期維護、升級奠定了良好基礎。

本文並不是程式碼風格指南,而是關於程式碼的可讀性複用性擴充套件性探討。

我們將從幾個方面展開討論:

  1. 變數
  2. 函式
  3. 物件和資料結構
  4. SOLID
  5. 測試
  6. 非同步
  7. 錯誤處理
  8. 程式碼風格
  9. 註釋

變數

用有意義且常用的單詞命名變數

Bad:

const yyyymmdstr = moment().format('YYYY/MM/DD');
複製程式碼

Good:

const currentDate = moment().format('YYYY/MM/DD'
); 複製程式碼

↑回到頂部

保持統一

可能同一個專案對於獲取使用者資訊,會有三個不一樣的命名。應該保持統一,如果你不知道該如何取名,可以去 codelf 搜尋,看別人是怎麼取名的。

Bad:

getUserInfo();
getClientData();
getCustomerRecord();
複製程式碼

Good:

getUser()
複製程式碼

↑回到頂部

每個常量都該命名

可以用 buddy.js 或者 ESLint 檢測程式碼中未命名的常量。

Bad:

// 三個月之後你還能知道 86400000 是什麼嗎?
setTimeout(blastOff, 86400000);
複製程式碼

Good:

const MILLISECOND_IN_A_DAY = 86400000;

setTimeout(blastOff, MILLISECOND_IN_A_DAY);
複製程式碼

↑回到頂部

可描述

通過一個變數生成了一個新變數,也需要為這個新變數命名,也就是說每個變數當你看到他第一眼你就知道他是幹什麼的。

Bad:

const ADDRESS = 'One Infinite Loop, Cupertino 95014';
const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(ADDRESS.match(CITY_ZIP_CODE_REGEX)[1
], address.match(CITY_ZIP_CODE_REGEX)[2]); 複製程式碼

Good:

const ADDRESS = 'One Infinite Loop, Cupertino 95014';
const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = ADDRESS.match(CITY_ZIP_CODE_REGEX) || [];
saveCityZipCode(city, zipCode);
複製程式碼

↑回到頂部

直接了當

Bad:

const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((l) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  // 需要看其他程式碼才能確定 'l' 是幹什麼的。
  dispatch(l);
});
複製程式碼

Good:

const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((location) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  dispatch(location);
});
複製程式碼

↑回到頂部

避免無意義的字首

如果建立了一個物件 car,就沒有必要把它的顏色命名為 carColor。

Bad:

const Car = {
  carMake: 'Honda',
  carModel: 'Accord',
  carColor: 'Blue'
};

function paintCar(car) {
  car.carColor = 'Red';
}
複製程式碼

Good:

const Car = {
  carMake: 'Honda',
  carModel: 'Accord',
  carColor: 'Blue'
};

function paintCar(car) {
  car.carColor = 'Red';
}
複製程式碼

↑回到頂部

使用預設值

Bad:

function createMicrobrewery(name) {
  const breweryName = name || 'Hipster Brew Co.';
  // ...
}
複製程式碼

Good:

function createMicrobrewery(name = 'Hipster Brew Co.') {
  // ...
}
複製程式碼

↑回到頂部

函式

引數越少越好

如果引數超過兩個,使用 ES2015/ES6 的解構語法。

Bad:

function createMenu(title, body, buttonText, cancellable) {
  // ...
}
複製程式碼

Good:

function createMenu({ title, body, buttonText, cancellable }) {
  // ...
}

createMenu({
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true
});
複製程式碼

↑回到頂部

只做一件事情

這是一條在軟體工程領域流傳久遠的規則。嚴格遵守這條規則會讓你的程式碼可讀性更好,也更容易重構。如果違反這個規則,那麼程式碼會很難被測試或者重用。

Bad:

function emailClients(clients) {
  clients.forEach((client) => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}
複製程式碼

Good:

function emailActiveClients(clients) {
  clients
    .filter(isActiveClient)
    .forEach(email);
}
function isActiveClient() {
  const clientRecord = database.lookup(client);    
  return clientRecord.isActive();
}
複製程式碼

↑回到頂部

顧名思義

看函式名就應該知道它是幹啥的。

Bad:

function addToDate(date, month) {
  // ...
}

const date = new Date();

// 很難知道是把什麼加到日期中
addToDate(date, 1);
複製程式碼

Good:

function addMonthToDate(month, date) {
  // ...
}

const date = new Date();
addMonthToDate(1, date);
複製程式碼

↑回到頂部

只需要一層抽象層

如果函式巢狀過多會導致很難複用以及測試。

Bad:

function parseBetterJSAlternative(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
      // ...
    });
  });

  const ast = [];
  tokens.forEach((token) => {
    // lex...
  });

  ast.forEach((node) => {
    // parse...
  });
}
複製程式碼

Good:

function parseBetterJSAlternative(code) {
  const tokens = tokenize(code);
  const ast = lexer(tokens);
  ast.forEach((node) => {
    // parse...
  });
}

function tokenize(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
      tokens.push( /* ... */ );
    });
  });

  return tokens;
}

function lexer(tokens) {
  const ast = [];
  tokens.forEach((token) => {
    ast.push( /* ... */ );
  });

  return ast;
}
複製程式碼

↑回到頂部

刪除重複程式碼

很多時候雖然是同一個功能,但由於一兩個不同點,讓你不得不寫兩個幾乎相同的函式。

要想優化重複程式碼需要有較強的抽象能力,錯誤的抽象還不如重複程式碼。所以在抽象過程中必須要遵循 SOLID 原則(SOLID 是什麼?稍後會詳細介紹)。

Bad:

function showDeveloperList(developers) {
  developers.forEach((developer) => {
    const expectedSalary = developer.calculateExpectedSalary();
    const experience = developer.getExperience();
    const githubLink = developer.getGithubLink();
    const data = {
      expectedSalary,
      experience,
      githubLink
    };

    render(data);
  });
}

function showManagerList(managers) {
  managers.forEach((manager) => {
    const expectedSalary = manager.calculateExpectedSalary();
    const experience = manager.getExperience();
    const portfolio = manager.getMBAProjects();
    const data = {
      expectedSalary,
      experience,
      portfolio
    };

    render(data);
  });
}
複製程式碼

Good:

function showEmployeeList(employees) {
  employees.forEach(employee => {
    const expectedSalary = employee.calculateExpectedSalary();
    const experience = employee.getExperience();
    const data = {
      expectedSalary,
      experience,
    };
    
    switch(employee.type) {
      case 'develop':
        data.githubLink = employee.getGithubLink();
        break
      case 'manager':
        data.portfolio = employee.getMBAProjects();
        break
    }
    render(data);
  })
}
複製程式碼

↑回到頂部

物件設定預設屬性

Bad:

const menuConfig = {
  title: null,
  body: 'Bar',
  buttonText: null,
  cancellable: true
};

function createMenu(config) {
  config.title = config.title || 'Foo';
  config.body = config.body || 'Bar';
  config.buttonText = config.buttonText || 'Baz';
  config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
}

createMenu(menuConfig);
複製程式碼

Good:

const menuConfig = {
  title: 'Order',
  // 'body' key 缺失
  buttonText: 'Send',
  cancellable: true
};

function createMenu(config) {
  config = Object.assign({
    title: 'Foo',
    body: 'Bar',
    buttonText: 'Baz',
    cancellable: true
  }, config);

  // config 就變成了: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}

createMenu(menuConfig);
複製程式碼

↑回到頂部

不要傳 flag 引數

通過 flag 的 true 或 false,來判斷執行邏輯,違反了一個函式幹一件事的原則。

Bad:

function createFile(name, temp) {
  if (temp) {
    fs.create(`./temp/${name}`);
  } else {
    fs.create(name);
  }
}
複製程式碼

Good:

function createFile(name) {
  fs.create(name);
}
function createFileTemplate(name) {
  createFile(`./temp/${name}`)
}
複製程式碼

↑回到頂部

避免副作用(第一部分)

函式接收一個值返回一個新值,除此之外的行為我們都稱之為副作用,比如修改全域性變數、對檔案進行 IO 操作等。

當函式確實需要副作用時,比如對檔案進行 IO 操作時,請不要用多個函式/類進行檔案操作,有且僅用一個函式/類來處理。也就是說副作用需要在唯一的地方處理。

副作用的三大天坑:隨意修改可變資料型別、隨意分享沒有資料結構的狀態、沒有在統一地方處理副作用。

Bad:

// 全域性變數被一個函式引用
// 現在這個變數從字串變成了陣列,如果有其他的函式引用,會發生無法預見的錯誤。
var name = 'Ryan McDermott';

function splitIntoFirstAndLastName() {
  name = name.split(' ');
}

splitIntoFirstAndLastName();

console.log(name); // ['Ryan', 'McDermott'];
複製程式碼

Good:

var name = 'Ryan McDermott';
var newName = splitIntoFirstAndLastName(name)

function splitIntoFirstAndLastName(name) {
  return name.split(' ');
}

console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
複製程式碼

↑回到頂部

避免副作用(第二部分)

在 JavaScript 中,基本型別通過賦值傳遞,物件和陣列通過引用傳遞。以引用傳遞為例:

假如我們寫一個購物車,通過 addItemToCart() 方法新增商品到購物車,修改 購物車陣列。此時呼叫 purchase() 方法購買,由於引用傳遞,獲取的 購物車陣列 正好是最新的資料。

看起來沒問題對不對?

如果當用戶點選購買時,網路出現故障, purchase() 方法一直在重複呼叫,與此同時使用者又添加了新的商品,這時網路又恢復了。那麼 purchase() 方法獲取到 購物車陣列 就是錯誤的。

為了避免這種問題,我們需要在每次新增商品時,克隆 購物車陣列 並返回新的陣列。

Bad:

const addItemToCart = (cart, item) => {
  cart.push({ item, date: Date.now() });
};
複製程式碼

Good:

const addItemToCart = (cart, item) => {
  return [...cart, {item, date: Date.now()}]
};
複製程式碼

↑回到頂部

不要寫全域性方法

在 JavaScript 中,永遠不要汙染全域性,會在生產環境中產生難以預料的 bug。舉個例子,比如你在 Array.prototype 上新增一個 diff 方法來判斷兩個陣列的不同。而你同事也打算做類似的事情,不過他的 diff 方法是用來判斷兩個陣列首位元素的不同。很明顯你們方法會產生衝突,遇到這類問題我們可以用 ES2015/ES6 的語法來對 Array 進行擴充套件。

Bad:

Array.prototype.diff = function diff(comparisonArray) {
  const hash = new Set(comparisonArray);
  return this.filter(elem => !hash.has(elem));
};
複製程式碼

Good:

class SuperArray extends Array {
  diff(comparisonArray) {
    const hash = new Set(comparisonArray);
    return this.filter(elem => !hash.has(elem));        
  }
}
複製程式碼

↑回到頂部

比起命令式我更喜歡函數語言程式設計

函式式變程式設計可以讓程式碼的邏輯更清晰更優雅,方便測試。

Bad:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];

let totalOutput = 0;

for (let i = 0; i < programmerOutput.length; i++) {
  totalOutput += programmerOutput[i].linesOfCode;
}
複製程式碼

Good:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];
let totalOutput = programmerOutput
  .map(output => output.linesOfCode)
  .reduce((totalLines, lines) => totalLines + lines, 0)
複製程式碼

↑回到頂部

封裝條件語句

Bad:

if (fsm.state === 'fetching' && isEmpty(listNode)) {
  // ...
}
複製程式碼

Good:

function shouldShowSpinner(fsm, listNode) {
  return fsm.state === 'fetching' && isEmpty(listNode);
}

if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}
複製程式碼

↑回到頂部

儘量別用“非”條件句

Bad:

function isDOMNodeNotPresent(node) {
  // ...
}

if (!isDOMNodeNotPresent(node)) {
  // ...
}
複製程式碼

Good:

function isDOMNodePresent(node) {
  // ...
}

if (isDOMNodePresent(node)) {
  // ...
}
複製程式碼

↑回到頂部

避免使用條件語句

Q:不用條件語句寫程式碼是不可能的。

A:絕大多數場景可以用多型替代。

Q:用多型可行,但為什麼就不能用條件語句了呢?

A:為了讓程式碼更簡潔易讀,如果你的函式中出現了條件判斷,那麼說明你的函式不止幹了一件事情,違反了函式單一原則。

Bad:

class Airplane {
  // ...
  
  // 獲取巡航高度
  getCruisingAltitude() {
    switch (this.type) {
      case '777':
        return this.getMaxAltitude() - this.getPassengerCount();
      case 'Air Force One':
        return this.getMaxAltitude();
      case 'Cessna':
        return this.getMaxAltitude() - this.getFuelExpenditure();
    }
  }
}
複製程式碼

Good:

class Airplane {
  // ...
}
// 波音777
class Boeing777 extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getPassengerCount();
  }
}
// 空軍一號
class AirForceOne extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude();
  }
}
// 賽納斯飛機
class Cessna extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getFuelExpenditure();
  }
}
複製程式碼

↑回到頂部

避免型別檢查(第一部分)

JavaScript 是無型別的,意味著你可以傳任意型別引數,這種自由度很容易讓人困擾,不自覺的就會去檢查型別。仔細想想是你真的需要檢查型別還是你的 API 設計有問題?

Bad:

function travelToTexas(vehicle) {
  if (vehicle instanceof Bicycle) {
    vehicle.pedal(this.currentLocation, new Location('texas'));
  } else if (vehicle instanceof Car) {
    vehicle.drive(this.currentLocation, new Location('texas'));
  }
}
複製程式碼

Good:

function travelToTexas(vehicle) {
  vehicle.move(this.currentLocation, new Location('texas'));
}
複製程式碼

↑回到頂部

避免型別檢查(第二部分)

如果你需要做靜態型別檢查,比如字串、整數等,推薦使用 TypeScript,不然你的程式碼會變得又臭又長。

Bad:

function combine(val1, val2) {
  if (typeof val1 === 'number' && typeof val2 === 'number' ||
      typeof val1 === 'string' && typeof val2 === 'string') {
    return val1 + val2;
  }

  throw new Error('Must be of type String or Number');
}
複製程式碼

Good:

function combine(val1, val2) {
  return val1 + val2;
}
複製程式碼

↑回到頂部

不要過度優化

現代瀏覽器已經在底層做了很多優化,過去的很多優化方案都是無效的,會浪費你的時間,想知道現代瀏覽器優化了哪些內容,請點這裡

Bad:

// 在老的瀏覽器中,由於 `list.length` 沒有做快取,每次迭代都會去計算,造成不必要開銷。
// 現代瀏覽器已對此做了優化。
for (let i = 0, len = list.length; i < len; i++) {
  // ...
}
複製程式碼

Good:

for (let i = 0; i < list.length; i++) {
  // ...
}
複製程式碼

↑回到頂部

刪除棄用程式碼

很多時候有些程式碼已經沒有用了,但擔心以後會用,捨不得刪。

如果你忘了這件事,這些程式碼就永遠存在那裡了。

放心刪吧,你可以在程式碼庫歷史版本中找他它。

Bad:

function oldRequestModule(url) {
  // ...
}

function newRequestModule(url) {
  // ...
}

const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
複製程式碼

Good:

function newRequestModule(url) {
  // ...
}

const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
複製程式碼

↑回到頂部

物件和資料結構

getset 方法操作資料

這樣做可以帶來很多好處,比如在操作資料時打日誌,方便跟蹤錯誤;在 set 的時候很容易對資料進行校驗...

Bad:

function makeBankAccount() {
  // ...

  return {
    balance: 0,
    // ...
  };
}

const account = makeBankAccount();
account.balance = 100;
複製程式碼

Good:

function makeBankAccount() {
  // 私有變數
  let balance = 0;

  function getBalance() {
    return balance;
  }
  
  function setBalance(amount) {
    // ... 在更新 balance 前,對 amount 進行校驗
    balance = amount;
  }

  return {
    // ...
    getBalance,
    setBalance,
  };
}

const account = makeBankAccount();
account.setBalance(100);
複製程式碼

↑回到頂部

使用私有變數

可以用閉包來建立私有變數

Bad:

const Employee = function(name) {
  this.name = name;
};

Employee.prototype.getName = function getName() {
  return this.name;
};

const employee = new Employee('John Doe');
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
複製程式碼

Good:

function makeEmployee(name) {
  return {
    getName() {
      return name;
    },
  };
}

const employee = makeEmployee('John Doe');
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
複製程式碼

↑回到頂部

使用 class

在 ES2015/ES6 之前,沒有類的語法,只能用建構函式的方式模擬類,可讀性非常差。

Bad:

// 動物
const Animal = function(age) {
  if (!(this instanceof Animal)) {
    throw new Error('Instantiate Animal with `new`');
  }

  this.age = age;
};

Animal.prototype.move = function move() {};

// 哺乳動物
const Mammal = function(age, furColor) {
  if (!(this instanceof Mammal)) {
    throw new Error('Instantiate Mammal with `new`');
  }

  Animal.call(this, age);
  this.furColor = furColor;
};

Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};

// 人類
const Human = function(age, furColor, languageSpoken) {
  if (!(this instanceof Human)) {
    throw new Error('Instantiate Human with `new`');
  }

  Mammal.call(this, age, furColor);
  this.languageSpoken = languageSpoken;
};

Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};
複製程式碼

Good:

// 動物
class Animal {
  constructor(age) {
    this.age = age
  };
  move() {};
}

// 哺乳動物
class Mammal extends Animal{
  constructor(age, furColor) {
    super(age);
    this.furColor = furColor;
  };
  liveBirth() {};
}

// 人類
class Human extends Mammal{
  constructor(age, furColor, languageSpoken) {
    super(age, furColor);
    this.languageSpoken = languageSpoken;
  };
  speak() {};
}
複製程式碼

↑回到頂部

鏈式呼叫

這種模式相當有用,可以在很多庫中發現它的身影,比如 jQuery、Lodash 等。它讓你的程式碼簡潔優雅。實現起來也非常簡單,在類的方法最後返回 this 可以了。

Bad:

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

const car = new Car('Ford','F-150','red');
car.setColor('pink');
car.save();
複製程式碼

Good:

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
    return this;
  }

  setModel(model) {
    this.model = model;
    return this;
  }

  setColor(color) {
    this.color = color;
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
    return this;
  }
}

const car = new Car('Ford','F-150','red')
  .setColor('pink');
  .save();
複製程式碼

↑回到頂部

不要濫用繼承

很多時候繼承被濫用,導致可讀性很差,要搞清楚兩個類之間的關係,繼承表達的一個屬於關係,而不是包含關係,比如 Human->Animal vs. User->UserDetails

Bad:

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  // ...
}

// TaxData(稅收資訊)並不是屬於 Employee(僱員),而是包含關係。
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}
複製程式碼

Good:

class EmployeeTaxData {
  constructor(ssn, salary) {
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
  // ...
}
複製程式碼

↑回到頂部

SOLID

SOLID 是幾個單詞首字母組合而來,分別表示 單一功能原則開閉原則里氏替換原則介面隔離原則以及依賴反轉原則

單一功能原則

如果一個類乾的事情太多太雜,會導致後期很難維護。我們應該釐清職責,各司其職減少相互之間依賴。

Bad:

class UserSettings {
  constructor(user) {
    this.user = user;
  }

  changeSettings(settings) {
    if (this.verifyCredentials()) {
      // ...
    }
  }

  verifyCredentials() {
    // ...
  }
}
複製程式碼

Good:

class UserAuth {
  constructor(user) {
    this.user = user;
  }
  verifyCredentials() {
    // ...
  }
}

class UserSetting {
  constructor(user) {
    this.user = user;
    this.auth = new UserAuth(this.user);
  }
  changeSettings(settings) {
    if (this.auth.verifyCredentials()) {
      // ...
    }
  }
}
}

複製程式碼

↑回到頂部

開閉原則

“開”指的就是類、模組、函式都應該具有可擴充套件性,“閉”指的是它們不應該被修改。也就是說你可以新增功能但不能去修改原始碼。

Bad:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'ajaxAdapter';
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'nodeAdapter';
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    if (this.adapter.name === 'ajaxAdapter') {
      return makeAjaxCall(url).then((response) => {
        // 傳遞 response 並 return
      });
    } else if (this.adapter.name === 'httpNodeAdapter') {
      return makeHttpCall(url).then((response) => {
        // 傳遞 response 並 return
      });
    }
  }
}

function makeAjaxCall(url) {
  // 處理 request 並 return