獨家解析Javascript原型繼承已經比較全面的分析了自定義函數類型,JS內置基本類(undefined, null, bool, number, string, symbol)和JS內置對象類型(Error, Date, Function)的design-time的prototype, 以及run-time的__proto__原型鏈。鑑於函數是JS的一等公民,另闢新篇介紹函數的原型及其應用。程序員
一般狀況下定義一個函數:編程
function add1(a, b) { return a + b; } console.log(add1(1,2)) //3
經過new Function也能構造:segmentfault
var add2 = new Function('a', 'b', 'return a + b'); console.log(add2(1,2)) //3
兩種方式達到的目的是一致的。而add1和add2的run-time __proto__都是Function.prototype, 能夠看做add1和add2都是透過new Function構造出來的,而function關鍵子就是調用new Function構造的便利途徑。app
add1.__proto__ = Function.prototype //true add2.__proto__ = Function.prototype //true
函數自己也是對象,它遵循獨家解析Javascript原型繼承所描述的自定義函數類型對象的原型法則。函數
//add1 創自於Function類型,是Function的實例 add1.__proto__ //[Function] add instanceof Function //true // add1 也繼承了object類型的原型 add1.__proto__.__proto__ //{} add1 instanceof Object //true // 因此add1 run-time __proto__原型鏈的頂端一樣是null add1.__proto__.__proto__.__proto__ // null
至此咱們瞭解到,透過funtion關鍵字定義的函數,實質是Fuction類型的實例,和經過new Function方式構造自己是同樣的。因此咱們經過修改Function的design-time的prototype,來實現面向切面編程(AOP)this
值得一提的是:咱們(程序員)通常狀況下只推薦修改design-time的prototype,而不去更改run-time的__proto__, 不然修改run-time的__proto__會形成程序難以維護和閱讀,增長不肯定的運行錯誤。Object.setPrototypeOf能夠更改對象實例run-time的__proto__prototype
面向切面編程(AOP,Aspect-Oritented Programming), 簡而言之,可理解爲函數調用時,在該函數執行前或/和執行後作一些通用的工做,好比作log,記錄執行時間等。這須要一個代理函數,把原始函數打扮成具備作log等功能的新函數。實際中調用該代理函數代理
function foo() { console.log('foo runs'); } foo(); // foo runs
如今咱們想在foo函數執行先後分別打印log,而不修改foo函數自己。code
Function.prototype.before = function() { var _self = this; return function() { console.log('before foo calls'); return _self.apply(this, arguments); } } Function.prototype.after = function() { var _self = this; return function() { var ret = _self.apply(this, arguments); console.log('after foo calls'); return ret; } } //這裏foo就具備了執行先後打印log的功能 foo = foo.before().after(); foo(); // before foo calls // foo runs // after foo calls
把打印log的功能提出來,作成更通用的邏輯。對象
Function.prototype.before = function(beforeFn) { var _self = this; return function() { beforeFn.apply(this, arguments); return _self.apply(this, arguments); } } Function.prototype.after = function(afterFn) { var _self = this; return function() { var ret = _self.apply(this, arguments); afterFn.apply(this, arguments); return ret; } } //包裝成具備log功能的新的foo函數 foo = foo.before(function() { console.log('foo enters') }).after(function(){ console.log('foo exits') }); foo(); // foo enters // foo runs // foo exits
由此,可把函數調用和通用的log能功能掛接(hook)起來了。