独家解析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)起来了。