当前位置: 首页 > news >正文

JS方法代理

作者:Jiang, Jilin

JS作为一门脚本语言,十分容易上手。外加其灵活性,可以轻而易举地扩展功能。今天,我们就聊聊JS的方法代理。方法代理是脚本语言中常见的方法扩展形式。这种灵活的形式优点在于遇到复杂的JS代码需要扩展时,可以相对简单的抽取并修改。但是,其缺点也十分明显,会造成代码的碎片化,因而是一把双刃剑。

基本形式

var _func = obj.func;

obj.func = function() {

         return _func();

};

 

此处,obj的func方法是原始方法。我们使用一个_func变量保存该方法,之后重写func方法,返回调用原始的方法。相当的好理解。

不定长参数

有时候,我们会遇到不定长参数的形式。那么我们可以做以下更改:

var _func = obj.func;

obj.func = function() {

         return _func.apply(this, arguments);

};

 

写到这里,JS方法代理的两种形式已经写完了。这时,你可能会疑惑这种方法代理有什么用?那么,就跟随我看看几个例子吧:

 

重写静态方法

出于某些原因,我们需要在console打印的同时,将打印数据传输到指定服务器上用户帮助统计数据。而在开发过程中,直接使用了原生态的console.log方法输出。那么,我们就可做如下修改。

         首先,构建代理方法:

var _log = console.log;

console.log = function() {

         return _log.apply(this, arguments);

};

 

接着,插入ajax调用逻辑:

console.log = function() {

         var _args = Array.prototype.slice.call(arguments);

         $.post("srvAddr", {userID: userID, args: _args});

         return _log.apply(this, arguments);

};

 

 

可以很容易预见效果:



原型代理

有时候,我们需要直接接管一个类的方法。那么相应的,我们就要将其prototype中的方法做代理。现在我们假设要使得Array默认支持数字排序:

var _sort = Array.prototype.sort;

Array.prototype.sort = function() {

         _sort.apply(this, arguments);

};

我们首先检测数组中的元素是否是数字,如果是则按照数字排序,否则就按照默认方式排序:

var _sort = Array.prototype.sort;

Array.prototype.sort = function() {

         var _isNum = true;

         for(var i = 0 ; i < this.length ; i += 1) {

                   if(typeof this[i] !== "number") {

                            _isNum = false;

                            break;

                   }

         }

 

         if(_isNum) {

                   return _sort.call(this, function(a, b) {

                            return a - b;

                   });

         } else {

                   return _sort.apply(this, arguments);

         }

};

原始排序方式(将数字转换成字符排序)

代理后排序方式

 

注意:此处仅作为原型方法代理的例子。在实际开发中,应该尽量避免对原生对象原型方法做代理。

 

重载

在jQuery中,toggle方法拥有多种重载,其中一个为toggle(display),通过接受boolean参数来决定元素显示还是隐藏。而fadeToggle方法则不接受该参数。让我们进行简单的代理,使其支持boolean参数:

var _fadeToggle = $.fn.fadeToggle;

 

$.fn.fadeToggle = function(display) {

         if(arguments.length === 1 && typeof display === "boolean") {

                   if(display) {

                            return this.fadeIn();

                   } else {

                            return this.fadeOut();

                   }

         }

         return _fadeToggle.apply(this, arguments);

};

通过参数长度和接收参数类型类型判断是否调用fadeIn或fadeOut方法。如果不匹配则调用原来的fadeToggle方法。以此类推,slideToggle也可以同样适用。

 

*继承

继承中,我们有时候会需要调用父类方法来实现。这和java中的super有些类似。一个简单的例子。我们定义了一个Person,可以laugh。同时定义一个Robot继承,是其沿用Person的laugh但是稍作修改:

var Person = function() {

         this.name = "";

         this.laugh = function() {

                   return "Ha ha!";

         };

         return this;

};

 

var Robot = function() {

         Person.apply(this);

         var _laugh = this.laugh;

         this.laugh = function() {

                   return encodeURIComponent(_laugh());

         };

         return this;

};

 

劫持

说到劫持,则和安全性开始挂钩了。如今大多数网站都习惯于使用开源的js库来开发,但是如果碰巧被插入了恶意代码,那么安全也就变得不安全了(例如chrome中大量未经验证的游览器插件)。以下,我们将举几个劫持的例子。

 

Ajax劫持

不少人以为,通过闭包的方式将重要用户参数存储在作用域内(例如动态生成的secureID),那么即便是被插入了恶意代码也无法获取到,从而无法伪造ajax请求来获取重要信息似乎就足够了。但是如果整个ajax请求被拦截了呢?

var _ajax = $.ajax;

 

$.ajax = function() {

         var $p = _ajax.apply($, arguments);

         $p.done(function(data) {

                   // Do something...

         });

         return $p;

};

jQuery中所有ajax请求(get,post,getJSON)最终都会调用$.ajax来实现。而通过方法代理直接劫持$.ajax方法并返回一个正常promise对象,会使得页面脚本运行毫无影响。但是实际上数据已经被劫持走了。

 

Array劫持

         Array拥有不少原型方法,例如push,pop,shift,unshift等等。我们只需要简单的代理数组操作方法,便可以截获数据。

var _push = Array.prototype.push;

Array.prototype.push = function() {

         // Do something...

         return _push.apply(this, arguments);

};

 

好了,以上就是这次的方法代理介绍。由于方法代理会混乱代码逻辑使得代码结构变得不易理解。在日常开发过程中,我们应该尽量避免使用它。只有在代理重载,或者更改难以理解的遗留代码的部分逻辑时使用它。从而避免将双刃剑误伤了自己。


相关文章:

  • Hadoop作业性能指标及参数调优实例 (一)Hadoop作业性能异常指标
  • Hadoop作业性能指标及参数调优实例 (二)Hadoop作业性能调优7个建议
  • Hadoop作业性能指标及参数调优实例 (三)Hadoop作业性能参数调优方法
  • 漫谈程序控制流
  • Hadoop集群硬盘故障分析与自动化修复
  • jQuery数据赋值解析
  • Apache Kylin的快速数据立方体算法——概述
  • eBay RUM实践
  • 基于数理统计分析的服务器端持续性能压力测试方案
  • 支付结果通知机制研究
  • Apache Eagle:eBay开源分布式实时Hadoop数据安全引擎
  • Ebay开源:Eclipse Plugin Repository Portal
  • eBay WebRex: 动态web资源优化工具
  • MapOutputBuffer理解的三重境界
  • Druid at Pulsar
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • django开发-定时任务的使用
  • EOS是什么
  • Flannel解读
  • HTTP--网络协议分层,http历史(二)
  • scrapy学习之路4(itemloder的使用)
  • 入门级的git使用指北
  • 三分钟教你同步 Visual Studio Code 设置
  • 实现简单的正则表达式引擎
  • 项目实战-Api的解决方案
  • 小程序 setData 学问多
  • hi-nginx-1.3.4编译安装
  • zabbix3.2监控linux磁盘IO
  • ​插件化DPI在商用WIFI中的价值
  • # 数论-逆元
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (52)只出现一次的数字III
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (LeetCode 49)Anagrams
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (办公)springboot配置aop处理请求.
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计ssm电影分享网站
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • (转) RFS+AutoItLibrary测试web对话框
  • (转)Sublime Text3配置Lua运行环境
  • (转)详解PHP处理密码的几种方式
  • *** 2003
  • *Django中的Ajax 纯js的书写样式1
  • .aanva
  • .net 8 发布了,试下微软最近强推的MAUI
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件