prototypejs库Function#wrap()的使用和源码解析
来源:互联网 发布:js难学吗 编辑:程序博客网 时间:2024/06/07 06:32
之前的2篇文章在讨论prototypejs库的继承实现方式、$super和_super过程中,我看到了Function#wrap()这个很有意思的API。这篇文章学习下如何使用Function#wrap(),以及它的源码。
我们先看下官方对这个API的说明:也就是说wrap()可以用来实现类似AOP编程的效果。
[[Function#wrap]] distills(提炼) the essence of aspect-oriented programming(AOP) into a single method, letting you easily build on existing functions byspecifying before and after behavior, transforming the return value, oreven preventing the original function from being called.
function sayName(name){console.log("original method="+name);return "return="+name;}// prototypejs将wrap加入到了Function.prototype中var wrapper = sayName.wrap(function(callOriginal, before_or_after, msg, name){if(before_or_after == "before"){console.log(msg);return callOriginal(name);}else if(before_or_after == "after"){var result = callOriginal(name);console.log(msg);return result;}else{console.log(msg);var result = callOriginal(name);console.log(msg);return result;}});console.log(wrapper("before","morning", "aty"));console.log(wrapper("after","afternoon", "qun"));console.log(wrapper("both","welcome", "xy"));
prototypejs库将wrap方法添加到了Function.prototype对象中,所以每一个函数对象都会获得wrap方法。Function#wrap()的入参是一个函数,返回值也是一个函数。入参的函数签名具有如下性质:
// callOriginal这个函数是原始函数(未经过包装处理)// arg1 arg2...是调用函数需要传递的参数function wrapper(callOriginal[, arg1[, arg2]...]){}
function Person(id){this.id = id;}Person.prototype.sayName = function(name){console.log("["+this.id+"]original method="+name);return this.id+"."+name;}Person.prototype.sayName = Person.prototype.sayName.wrap(function(callOriginal, before_or_after, msg, name){if(before_or_after == "before"){console.log(msg);return callOriginal(name);}else if(before_or_after == "after"){var result = callOriginal(name);console.log(msg);return result;}else{console.log(msg);var result = callOriginal(name);console.log(msg);return result;}});var po1 = new Person("1");console.log(po1.sayName("before","morning", "aty"));var po2 = new Person("2");console.log(po2.sayName("after","afternoon", "qun"));var po3 = new Person("3");console.log(po3.sayName("both","welcome", "xy"));
学会了如何使用这个API之后,我们看看prototypejs源码是怎么实现的。很简单没有什么的特别的,就是用到了bind和apply这2个方法。
function wrap(wrapper) { var __method = this;//原始函数 return function() { var a = update([__method.bind(this)], arguments); return wrapper.apply(this, a); }} // console.log(update([1,2],[4,5,6]));//[1, 2, 4, 5, 6]// 这个工具函数,其实就是将数组和函数参数对象arguments合并成一个数组function update(array, args){var arrayLength = array.length, length = args.length;while (length--) array[arrayLength + length] = args[length];return array;}
现在我们着手自己实现一个wrap,上面的update()方法很简单了,我们直接使用它不做任何修改。prototypejs是将wrap挂在Function.prototype对象上,现在我们将自己的wrap作为全局函数。
function Person(id){this.id = id;}Person.prototype.sayName = function(name){console.log("["+this.id+"]original method="+name);return this.id+"."+name;}function hiWrapper(callOriginal, before_or_after, msg, name){if(before_or_after == "before"){console.log(msg);return callOriginal(name);}else if(before_or_after == "after"){var result = callOriginal(name);console.log(msg);return result;}else{console.log(msg);var result = callOriginal(name);console.log(msg);return result;}}//模拟prototypejs,但是存放到全局的window对象上function atyWrap(wrapper, originalMethod, context) {return function() { var a = update([originalMethod.bind(context)], arguments); return wrapper.apply(context, a);}} function update(array, args){var arrayLength = array.length, length = args.length;while (length--) array[arrayLength + length] = args[length];return array;}//模拟prototypejs var po1 = new Person("1");po1.sayName = atyWrap(hiWrapper, Person.prototype.sayName, po1);console.log(po1.sayName("before","morning", "aty"));console.log("---------------------");var po2 = new Person("2");po2.sayName = atyWrap(hiWrapper, Person.prototype.sayName, po2);console.log(po2.sayName("after","afternoon", "qun"));console.log("---------------------");var po3 = new Person("3");po3.sayName = atyWrap(hiWrapper, Person.prototype.sayName, po3);console.log(po3.sayName("both","welcome", "xy"));执行结果如下:
可以看到实现了跟prototypejs同样的效果,但是种方式有2个问题:调用atyWrap很麻烦必需自己传递原始方法和上下文,而且这种做法有性能问题(po1、po2和po3这3个对象都自己拷贝了一份Person.prototype.sayName)占用了3份空间。导致这2个问题的根源是:自定义的atyWrap挂在了全局对象window上,atyWrap中无法自己获取上下文。
为了解决不知道上下文this的问题,这次我们将atyWrap挂在Object.prototype上。
function Person(id){this.id = id;}Person.prototype.sayName = function(name){console.log("["+this.id+"]original method="+name);return this.id+"."+name;}function hiWrapper(callOriginal, before_or_after, msg, name){if(before_or_after == "before"){console.log(msg);return callOriginal(name);}else if(before_or_after == "after"){var result = callOriginal(name);console.log(msg);return result;}else{console.log(msg);var result = callOriginal(name);console.log(msg);return result;}}//模拟prototypejs,放在Object.prototype上Object.prototype.atyWrap = function(wrapper, originalMethod){return function() { var a = update([originalMethod.bind(this)], arguments); return wrapper.apply(this, a);}} function update(array, args){var arrayLength = array.length, length = args.length;while (length--) array[arrayLength + length] = args[length];return array;}//模拟prototypejs// 不需要传递this了Person.prototype.sayName = Object.prototype.atyWrap(hiWrapper, Person.prototype.sayName); var po1 = new Person("1");console.log(po1.sayName("before","morning", "aty"));console.log("---------------------");var po2 = new Person("2");console.log(po2.sayName("after","afternoon", "qun"));console.log("---------------------");var po3 = new Person("3");console.log(po3.sayName("both","welcome", "xy"));执行结果如下:
结果是完全正常的,函数签名和调用方法简单很多(不需要this了)。debug可以发现:性能问题也没有了,po1、po2和po3对象中都没有sayName方法的拷贝,这3个对象共享原型中的方法。
虽然不用显示传递上下文对象了,但是还必需显示地传递原始方法。现在我们将atyWrap挂在Function.prototype对象上。
function Person(id){this.id = id;}Person.prototype.sayName = function(name){console.log("["+this.id+"]original method="+name);return this.id+"."+name;}function hiWrapper(callOriginal, before_or_after, msg, name){if(before_or_after == "before"){console.log(msg);return callOriginal(name);}else if(before_or_after == "after"){var result = callOriginal(name);console.log(msg);return result;}else{console.log(msg);var result = callOriginal(name);console.log(msg);return result;}}//模拟prototypejs,放在Function.prototype上Function.prototype.atyWrap = function(wrapper, originalMethod){var method = this;//this代表方法对象return function() { var a = update([method.bind(this)], arguments);//this代表的是对象Person return wrapper.apply(this, a);}} function update(array, args){var arrayLength = array.length, length = args.length;while (length--) array[arrayLength + length] = args[length];return array;}//模拟prototypejs// 不需要传递this了,也不需要传递方法了Person.prototype.sayName = Person.prototype.sayName.atyWrap(hiWrapper); var po1 = new Person("1");console.log(po1.sayName("before","morning", "aty"));console.log("---------------------");var po2 = new Person("2");console.log(po2.sayName("after","afternoon", "qun"));console.log("---------------------");var po3 = new Person("3");console.log(po3.sayName("both","welcome", "xy"));执行结果如下:
可以看到执行结果也是完全正常的,无论是调用方式,还是wrap函数签名, 都跟使用prototypejs一样的了。实际上这就说prototypejs的做法,可以看到将wrap挂在Function.prototype上是最简单、最合理的,虽然修改原型有一定的风险。
0 0
- prototypejs库Function#wrap()的使用和源码解析
- 使用jquery的wrapAll()和wrap()方法包装元素
- presto源码解析(function)
- WRAP在换行的使用
- EventBus的使用和源码解析
- Picasso的使用和源码解析
- Okio的使用和源码解析
- Picasso的使用和源码解析
- EventBus的使用和源码解析
- Retrofit的使用和源码解析
- Robotium框架的使用和源码解析
- 使用oracle wrap工具加密package,function,procedure等
- 学习prototypejs中的继承实现机制(一): Object.extend()、Class.create()、Class#addMethods()的使用
- AsyncTask使用和源码解析
- HandlerThread使用和源码解析
- ThreadLocal 源码解析和使用
- AtomicReference源码解析和使用
- IoBuffer的wrap,rewind的使用
- 有关AndroidStudio R文件错误的小结
- centos 入门1 安装和网络配置
- 组合索引,索引内过滤
- 查看系统中Android 应用的cup占用率
- PHP获取网页标题的代码
- prototypejs库Function#wrap()的使用和源码解析
- [LeetCode]Search in Rotated Sorted Array
- 基于Boost库的C++文件遍历
- 转载自百度百科——java记——关于java中decimalformat类
- pc与Android进行USB通信
- Java反射之java.beans包
- Light Bulb(三分)
- atoi-字符串转Int的实现
- hadoop编译报错