JavaScript API 設計原則

2022-01-06 全棧開發者中心

前段時間組織優化我們的原生模塊 API(iOS、Android 模塊封裝成 JavaScript 接口),於是學習了幾篇 JavaScript API 設計的文章,儘管是舊文,但受益匪淺,這裡記錄一下。

好的 API 設計:在自描述的同時,達到抽象的目標。

設計良好的 API ,開發者可以快速上手,沒必要經常抱著手冊和文檔,也沒必要頻繁光顧技術支持社區。

流暢的接口

方法鏈:流暢易讀,更易理解

//常見的 API 調用方式:改變一些顏色,添加事件監聽var elem = document.getElementById("foobar");elem.style.background = "red";elem.style.color = "green";elem.addEventListener('click', function(event) {  alert("hello world!");}, true);//(設想的)方法鏈 APIDOMHelper.getElementById('foobar')  .setStyle("background", "red")  .setStyle("color", "green")  .addEvent("click", function(event) {    alert("hello world");  });

設置和獲取操作,可以合二為一;方法越多,文檔可能越難寫

var $elem = jQuery("#foobar");//setter$elem.setCss("background", "green");//getter$elem.getCss("color") === "red";//getter, setter 合二為一$elem.css("background", "green");$elem.css("color") === "red";

一致性

相關的接口保持一致的風格,一整套 API 如果傳遞一種熟悉和舒適的感覺,會大大減輕開發者對新工具的適應性。

命名這點事:既要短,又要自描述,最重要的是保持一致性

「There are only two hard problems in computer science: cache-invalidation and naming things.」
「在計算機科學界只有兩件頭疼的事:緩存失效和命名問題」
— Phil Karlton

選擇一個你喜歡的措辭,然後持續使用。選擇一種風格,然後保持這種風格。

處理參數

需要考慮大家如何使用你提供的方法,是否會重複調用?為何會重複調用?你的 API 如何幫助開發者減少重複的調用?
接收map映射參數,回調或者序列化的屬性名,不僅讓你的 API 更乾淨,而且使用起來更舒服、高效。

jQuery 的 css() 方法可以給 DOM 元素設置樣式:

jQuery("#some-selector")  .css("background", "red")  .css("color", "white")  .css("font-weight", "bold")  .css("padding", 10);

這個方法可以接受一個 JSON 對象:

jQuery("#some-selector").css({  "background" : "red",  "color" : "white",  "font-weight" : "bold",  "padding" : 10});//通過傳一個 map 映射綁定事件jQuery("#some-selector").on({  "click" : myClickHandler,  "keyup" : myKeyupHandler,  "change" : myChangeHandler});//為多個事件綁定同一個處理函數jQuery("#some-selector").on("click keyup change", myEventHandler);

處理類型

定義方法的時候,需要決定它可以接收什麼樣的參數。我們不清楚人們如何使用我們的代碼,但可以更有遠見,考慮支持哪些參數類型。

//原來的代碼DateInterval.prototype.days = function(start, end) {  return Math.floor((end - start) / 86400000);};//修改後的代碼DateInterval.prototype.days = function(start, end) {  if (!(start instanceof Date)) {    start = new Date(start);  }  if (!(end instanceof Date)) {    end = new Date(end);  }  return Math.floor((end.getTime() - start.getTime()) / 86400000);};

加了短短的6行代碼,我們的方法強大到可以接收 Date 對象,數字的時間戳,甚至像 Sat Sep 08 2012 15:34:35 GMT+0200 (CEST) 這樣的字符串

如果你需要確保傳入的參數類型(字符串,數字,布爾),可以這樣轉換:

function castaway(some_string, some_integer, some_boolean) {  some_string += "";  some_integer += 0; // parseInt(some_integer, 10) 更安全些  some_boolean = !!some_boolean;}

處理 undefined

為了使你的 API 更健壯,需要鑑別是否真正的 undefined 值被傳遞進來,可以檢查 arguments 對象:

function testUndefined(expecting, someArgument) {  if (someArgument === undefined) {    console.log("someArgument 是 undefined");  }  if (arguments.length > 1) {    console.log("然而它實際是傳進來的");  }}testUndefined("foo");// 結果: someArgument 是 undefinedtestUndefined("foo", undefined);// 結果:  someArgument 是 undefined , 然而它實際是傳進來的

給參數命名

event.initMouseEvent(  "click", true, true, window,  123, 101, 202, 101, 202,  true, false, false, false,  1, null);

Event.initMouseEvent 這個方法簡直喪心病狂,不看文檔的話,誰能說出每個參數是什麼意思?

給每個參數起個名字,賦個默認值,可好

event.initMouseEvent(  type="click",  canBubble=true,  cancelable=true,  view=window,  detail=123,  screenX=101,  screenY=202,  clientX=101,  clientY=202,  ctrlKey=true,  altKey=false,  shiftKey=false,  metaKey=false,  button=1,  relatedTarget=null);

ES6, 或者 Harmony 就有 默認參數值 和 rest 參數 了。

參數接收 JSON 對象

與其接收一堆參數,不如接收一個 JSON 對象:

function nightmare(accepts, async, beforeSend, cache, complete, /* 等28個參數 */) {  if (accepts === "text") {    // 準備接收純文本  }}function dream(options) {  options = options || {};  if (options.accepts === "text") {    // 準備接收純文本  }}

調用起來也更簡單了:

nightmare("text", true, undefined, false, undefined, /* 等28個參數 */);dream({  accepts: "text",  async: true,  cache: false});

參數默認值

參數最好有默認值,通過 jQuery.extend() http://underscorejs.org/#extend) 和 Protoype 的 Object.extend ,可以覆蓋預設的默認值。

var default_options = {  accepts: "text",  async: true,  beforeSend: null,  cache: false,  complete: null,  // …};function dream(options) {  var o = jQuery.extend({}, default_options, options || {});  console.log(o.accepts);}dream({ async: false });// prints: "text"

擴展性回調(callbacks)

通過回調, API 用戶可以覆蓋你的某一部分代碼。把一些需要自定義的功能開放成可配置的回調函數,允許 API 用戶輕鬆覆蓋你的默認代碼。

API 接口一旦接收回調,確保在文檔中加以說明,並提供代碼示例。

事件(events)

事件接口最好見名知意,可以自由選擇事件名字,避免與原生事件 重名。

處理錯誤

不是所有的錯誤都對開發者調試代碼有用:

// jQuery 允許這麼寫$(document.body).on('click', {});// 點擊時報錯//   TypeError: ((p.event.special[l.origType] || {}).handle || l.handler).apply is not a function//   in jQuery.min.js on Line 3

這樣的錯誤調試起來很痛苦,不要浪費開發者的時間,直接告訴他們犯了什麼錯:

if (Object.prototype.toString.call(callback) !== '[object Function]') { // 看備註  throw new TypeError("callback is not a function!");}

備註:typeof callback === "function" 在老的瀏覽器上會有問題,object 會當成個 function 。

可預測性

好的 API 具有可預測性,開發者可以根據例子推斷它的用法。

Modernizr’s 特性檢測 是個例子:

a) 它使用的屬性名完全與 HTML5、CSS 概念和 API 相匹配

b) 每一個單獨的檢測一致地返回 true 或 false 值

// 所有這些屬性都返回 'true' 或 'false'Modernizr.geolocationModernizr.localstorageModernizr.webworkersModernizr.canvasModernizr.borderradiusModernizr.boxshadowModernizr.flexbox

依賴於開發者已熟悉的概念也可以達到可預測的目的。

jQuery’s 選擇器語法 就是一個顯著的例子,CSS1-CSS3 的選擇器可直接用於它的 DOM 選擇器引擎。

$("#grid") // Selects by ID$("ul.nav > li‍") // All LIs for the UL with class "nav"$("ul li:nth-child(2)") // Second item in each list

比例協調

好的 API 並不一定是小的 API,API 的體積大小要跟它的功能相稱。

比如 Moment.js ,著名的日期解析和格式化的庫,可以稱之為均衡,它的 API 既簡潔又功能明確。

像 Moment.js 這樣特定功能的庫,確保 API 的專注和小巧非常重要。

編寫 API 文檔

軟體開發最艱難的任務之一是寫文檔,實際上每個人都恨寫文檔,怨聲載道的是沒有一個好用的文檔工具。

以下是一些文檔自動生成工具:

YUIDoc (requires Node.js, npm)

JsDoc Toolkit (requires Node.js, npm)

Markdox (requires Node.js, npm)

Dox (requires Node.js, npm)

Docco (requires Node.js, Python, CoffeeScript)

JSDuck (reqires Ruby, gem)

JSDoc 3 (requires Java)

最重要的是:確保文檔跟代碼同步更新。

參考資料:

via:http://jinlong.github.io/2015/08/31/secrets-of-awesome-javascript-api-design/

相關焦點

  • 設計一個JavaScript插件系統,編程思維比死磕API更重要
    作者:Bryan Braun翻譯:張張原文:https://css-tricks.com/designing-a-javascript-plugin-system/WordPress這違反了開放-封閉原則,即一個軟體實體應該是開放的擴展,但封閉的修改。另外,「squared」函數通過產生副作用發揮作用。這在JavaScript中並不少見,但感覺並不好——特別是當其他插件可能處在同一內部狀態的情況下。一種更實用的方法將大大有助於使我們的系統更安全、更可預測。
  • 使用Google的api來實現自定義搜索百度網盤資源
    如何使用Google的api來實現自定義搜索網盤資源?前言我之所以折騰這個是因為我想實現博客對接我自己的網盤資源,但是可惜都只能找到百度公開的數據接口,像我這種對接私人網盤的需求沒找到,不過找到了這個自定網盤搜索,覺得還有趣。
  • photoshop_python_api: 最好用的photoshop python api
    如果買了Ftrack或者Shotgun的公司他們自帶的工具鏈就支持通過python去操作Photoshop除此之外就通過Python的第三方庫comtypes去操作Photoshop今天在這裡我要像大家分享的是我自己寫的一個python庫叫photoshop_python_api然後也感謝我的小夥伴Limbo為我這個API設計的LOGO安裝
  • 程式設計師必須掌握的 12 個 JavaScript 技能!
    本文將與大家分享 12 個實用的 JavaScript 技能,未來我也將在 Github 的 JS Tips&Tidbits 倉庫(https://github.com/nas5w/javascript-tips-and-tidbits)中持續更新此概念列表。
  • 我讀完了React的API,並為新手送上了一些建議
    更重要的是,React 是一個由 Jordan Walke 創建的 Facebook 開源項目,這個項目由一些頗具野心的年輕開發人員主導——其中包括 Dan Abramov,他經常會隱晦地解釋這個庫背後的理念,(社交)媒體上關於 React 的話題幾乎都能找到他的身影;還有 Brain Vaughn,他從圖形設計領域轉向了編程工作,構建了 react-virtualized
  • Apimock系統演進
    本文主要介紹轉轉的api 和 mock功能的演進。希望對大家有所幫助,少踩一些坑。轉轉業務剛起步,接口比較少(百個以內),大家都在wiki上維護,搜索和維護比較費力,於是我們實現了第一版。主要功能很簡單就是列表頁和詳情頁。舊數據從wiki上導出,格式化之後批量導入。
  • asp.net web api中的版本管理
    api也是演進的,這篇博文就說說asp.net web api演進時的版本管理。asp.net web api的版本管理是通過微軟的一個包來實現的。api-version=2.0方式訪問不同的版本。顯而易見,這種通過在請求url末尾加參數的方式有點囉嗦。
  • JavaScript:學會toString()字符串
    javascript中的toString()方法,主要用於Array、Boolean、Date、Error、Function
  • Java、C/C++、JavaScript、PHP、Python,到底用來開發什麼?
    javascript聽起來跟java有關係,其實並沒有任何關係,只是名字像而已。就好比雷峰塔和雷鋒的關係一樣,雖然只差一個字。 js最廣泛的應用毫無疑問是在web前端。簡單的說,網站給你傳過來的是一堆用各種標籤表示格式的文檔,而js負責操縱這些文檔實現一些客戶端動態效果。js的領地還不僅如此,現在的Node.js還可以用於伺服器端的開發。
  • 老姚淺談:怎麼學JavaScript?
    《JavaScript設計模式與開發實踐》 js設計模式也是要學的,此書把js的設計模式講得非常清晰,一點不晦澀,看起來沒多少難度。《正則指引》,分析源碼時,如果正則表達式不懂,沒法進行下去的。此書相對來說講得比較清晰。
  • Resty 1.2.0-SNAPSHOT 更新,可通過header來控制api的版本,數據源...
    個多月滿一年了,在沒有推廣的情況下如果能積累到500 star,真是棒棒的,如果覺得不錯可以給顆心(https://github.com/Dreampie/Resty),希望大家多多鼓勵,也希望有心的同學參與維護,開源說明它屬於大家,無論你使用或者作為一個框架的基礎學習還是從中得到靈感做出好用的東西 都希望你能和大家一起分享 開源希望大家互相幫助本次更新說大不大說小不小,主要解決一下對api
  • 第一篇:JavaScript基本語法
    html><head><title>這是登錄頁面</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><script type="text/javascript
  • 園林景觀設計原則
    三、設計的基本原則1、首先要尊重場地、因地制宜、實時造景、就地取材,植物造景,四時爛漫。並且要符合當地的人文景觀和節令氣候。比如西安有一些大的主題公園,如大雁塔、南湖、大唐芙蓉園等,這都是一些大師的作品,給遠道而來的遊客帶來美的享受。園林設計師要有一雙發現美的眼睛,而不是標新立異,不尊重客觀地形,刻意打造一個出奇的景觀。
  • 包裝設計的基本原則!
    商品包裝設計仍然是一項具體的和具有一定範圍的工作,它既包含著思維的也包含著技能的,更存在著一定的設計原則。安全原則是要求設計要遵循保護商品、防止洩露和外溢的客觀規律,來設計容器的各個部位的位置和比例等。
  • JavaScript模擬輪播圖效果
    div{       border: 1px solid white;       width:600px ;       height:400px;       margin: auto;       text-align: center;     }   </style>   <script type="text/javascript
  • JavaScript函數 - 遞歸
    >2.找出一個臨界值,n==0時,就不需要列印了,直接return;即可3.不等於0時,直接document.write列印hello world4.然後緊跟著return print(n-1); 依次列印<script type = "text/javascript
  • 界面設計方法(9):界面設計的原則與標準
    編輯導語:前幾篇文章中,我們已經了解到了許多關於界面設計的相關知識。今天這篇文章讓我們回歸一下界面設計本身,談一談其設計原則與標準,希望對你有幫助。前面介紹了5種基本的界面形式,如果從構成界面的要素(構件)層面看,實際上所有的界面都是一樣的,因為它們都是由同樣的控制項構成的,只是控制項的位置不同而已。
  • 100個常用免費API接口大全
    QNAP - 藉助 QNAP 開發工具包(API 和 SDK), 開發人員可以設計能在客戶端設備(如智慧型手機或 PC)上運行的應用, 並遠程管理和訪問存儲在NAS上的文件和文檔。Verizon Cloud - 上傳, 檢索和管理大量數據, 通過 API 調用訪問數據, 查看預先打包的報告, 依靠 Verizon 的安全措施保證數據安全和隨時可用.
  • (14/30)Blazor系列:JavaScript interop(互操作)
    https://sweetalert.js.org[2]Blazor版本: https://github.com/Basaingeal/Razor.SweetAlert2[3]Blazor JavaScript interoperability (JS interop): https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability
  • 20個常用的JavaScript簡寫技巧
    箭頭函數 參考:JavaScript Arrow function https://jscurious.com/javascript-arrow-function/ 8. 模板字符串 我們一般使用 + 運算符來連接字符串變量。