獨家解析Javascript原型繼承 - 之函數原型和AOP

引子

獨家解析Javascript原型繼承已經比較全面的分析了自定義函數類型,JS內置基本類(undefined, null, bool, number, string, symbol)和JS內置對象類型(Error, Date, Function)的design-timeprototype, 以及run-time的__proto__原型鏈。鑑於函數是JS的一等公民,另闢新篇介紹函數的原型及其應用。程序員

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

AOP編程實現

至此咱們瞭解到,透過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)起來了。

相關文章
相關標籤/搜索