express源碼中的對象繼承

nodejs web框架express源碼中, 多處封裝涉及到了對象繼承,單獨拿出來總結下它們的特點,參考學習

對象繼承方式

簡單閱讀源碼所看到的對象繼承方式有三種形式

  • merge-descriptors

var EventEmitter = require(events).EventEmitter;var mixin = require(merge-descriptors);//app對象從EventEmitter.prototype對象繼承mixin(app, EventEmitter.prototype, false);

  • Object.create()

//app.request對象從req對象繼承,並定義app屬性app.request = Object.create(req, { app: { configurable: true, enumerable: true, writable: true, value: app } })

  • Object.setPrototypeOf()

var setPrototypeOf = require(setprototypeof)//將router對象的原型對象設置為protosetPrototypeOf(router, proto)

源碼分析

這應該是最常用的的幾種繼承形式了

屬性拷貝

第一種為屬性值拷貝,沒什麼技巧就是把要繼承的屬性全部拷貝到自己身上, 可以選擇是否覆蓋同名屬性

源碼的實現如下

//merge-descriptorsmodule.exports = mergevar hasOwnProperty = Object.prototype.hasOwnProperty/** * Merge the property descriptors of `src` into `dest` * * @param {object} dest Object to add descriptors to * @param {object} src Object to clone descriptors from * @param {boolean} [redefine=true] Redefine `dest` properties with `src` properties * @returns {object} Reference to dest * @public */function merge(dest, src, redefine) { if (!dest) { throw new TypeError(argument dest is required) } if (!src) { throw new TypeError(argument src is required) } if (redefine === undefined) { // Default to true redefine = true } Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) { if (!redefine && hasOwnProperty.call(dest, name)) { // Skip desriptor return } // Copy descriptor var descriptor = Object.getOwnPropertyDescriptor(src, name) Object.defineProperty(dest, name, descriptor) }) return dest}

拷貝繼承,繼承時會耗費更多時間和空間,但後續訪問時理論上效率更高

常駐於內存中的對象更加適合這種繼承方式, 一次繼承多次使用

後兩種可歸為一類,均是從原型對象繼承

原型繼承

原型繼承,是js中常見的的繼承方式,這裡有用到了兩種操作方法

  • Object.create(proto[, propertiesObject])

返回在指定原型對象上添加新屬性後的對象

即除了繼承之外,還可以自定義屬性

  • setprototypeof

//setprototypeofmodule.exports = Object.setPrototypeOf || ({__proto__:[]} instanceof Array ? setProtoOf : mixinProperties);function setProtoOf(obj, proto) { obj.__proto__ = proto; return obj;}function mixinProperties(obj, proto) { for (var prop in proto) { if (!obj.hasOwnProperty(prop)) { obj[prop] = proto[prop]; } } return obj;}

一般來說調用的是原生介面Object.setPrototypeOf

同時也提供了兩種兼容方案

  • obj.__proto__ = proto;
  • 屬性值拷貝

總結

自己寫代碼或者寫庫程序不可避免會涉及到繼承封裝,express提供了久經考驗的最佳實踐

  • 對於常駐對象推薦使用屬性拷貝,用空間換時間
  • 原型繼承有Object.create()Object.setPrototypeOf()等方法,分別有各自適合的場景,更多了解可以參照MDN的說明
  • 不想自己造輪子,express的這倆輪子也可以直接拿來使用,merge-descriptorssetPrototypeOf, 源碼也十分簡潔

推薦閱讀:

如何系統地學習 Express?
nodejs怎麼部署能hold住訪問量較大的項目?

TAG:Express框架 | 源碼 | 面向對象編程 |