原生JS技巧(2)浅谈回调函数(拦截器、注入和控制反转原理)

  现在很多框架都在说拦截器、依赖注入、控制反转,尤其是java,很多的js框架也引入这种设计思想,包括angular、vue等等,在网上一查也有好多关于这方面的文章,但技术性有很深,但读过源码的人应该就明白它核心的原理,由大到小来解释就是“回调函数”。

1、回调函数

  什么是“回调函数”,看下面例子

1 var list = [{a:1,b:2,c:3},{a:3,b:4,c:5},{a:4,b:3,c:2},{a:7,b:1,c:1}];
2 list.filter(function(item){
3 return item.a > 3;
4 });

filter是Array的一个函数,正常我们会list.filter()这么调用,来筛选数组,

如果所有的逻辑都写在filter()内部,我们需要大量的判断条件,在不清楚list数组内容的情况,就无法去写filter的条件,

这样我们就需要把控制权交给外面,里面只提供基础功能,用于返回新数组。

那我们自己写一个回调,如下:

1 var filter = function(array,callback){
2 var _array = [];
3 for(var i in array){
4 if(callback.call(this,array[i])) _array.push(array[i]);
5 }
6 }

由此能看出,回调函数就是参数只传入一个函数体,而函数内部执行作为参数的这个函数。

 

2、拦截器

很多语言都有拦截器,而拦截器的原理和回调函数很像,也是把部分的控制权交给了外部,而内部只是调用包含外部控制权的函数

var c = function(callback){
    var b = 0;
    setInterval(function(){
        b += 1;
        if(b % 3 === 0){
            callback.apply(this,[b*1000]);
        }
    },1000)
}
var a = [];
c(function(val){
    a.push(val);
});

a的值: [3000, 6000, 9000, ...] ,会按照每3秒的时间拦截一次,并向a数组中添加

我们在外部可以任意定义callback中的逻辑,如果c函数内部是一个ajax请求或其他异步,我们需要捕捉请求回来时变量被赋值,或者捕捉模块加载完成时,都可以在需要的节点下调用一个回调函数,并把内部获取的数据通过参数返回给外部,像:callback.apply(this指向的对象,[参数]),外部可以任意控制处理,这就是拦截器的基本原理。

 

3、控制反转和依赖注入

我们也可以用回调来实现,先简单理解下控制反转和依赖注入,首先依赖注入是控制反转的实现,

我们每次实例化对象的时候,都会现new一个新的对象,像var a = new b()

然后用a.xxx的方式来使用b中的属性和方法,但有一个地方调b的对象,要用到b里y1 : function(),另一个地方也调b对象,却不能有y1 : function(),

这样我们就不方便把控制权交给b对象,而是放在外面,而里面只需要让它自动new一个对象,这就是注入

简单来说就是外部只需要传入类的引用,外部就可以直接调用它的对象。

 1 export class SummaryComponent implements OnInit, AfterViewInit {
 2   constructor(public http: Http){
 3 
 4   ngOnInit() {
 5     //XHR异步请求:发送get请求
 6      this.http.get('./app/contact360/summary.sub.component.html').subscribe(data => {
 7         alert(data.text()); //如果请求的是文本:  data.text() ,  如果请求的是json文件:  data.json()
 8      });
 9   }
10 }

这是angular2注入的例子,在构造方法中传入Http这个类,正常我需要 this.http = new Http()

但现在我把new的控制权交给了angular,使它在实例化SummaryComponent的时候,顺便把构造方法中传入的类都实例化了,并反给自身,

这样,我们就可以不用new而直接可以调用this.http的对象,这么做也有个好处就是,new Http()的时候,我不知道传什么参数给它,而现在我也不用考虑这个了。

问:这和回调函数有什么关系?

答:都是把控制权交给外面,里面只做基本操作

 1 define(["demo"],function(_demo){ return new _demo(); }); 

知道模块化开发的同学都知道上面这个用于引入新外来模块用的,但如果define我这么写

1 _define = function(mods,func){
2     var _objs = [];
3     define(mods,function(){
4          for(var i=0;i<arguments.length;i++){
5              _objs.push(new arguments[i]);
6          }
7     });
8     func.apply(this,_objs);
9 }

用的时候我就不用new _demo()了,并可以直接用_demo.test()对象方法了

 1 _define(["demo"],function(_demo){ return _demo.test(); }); 

这就是通过回调函数来注入,

当然注入对象的主要还是通过调用接口和继承的方式,但原理依然脱离不了这种方式,

虽然原理很简单,但结合各种设计模式后,就会奥妙无穷。

posted @ 2018-09-03 19:35  冉夜  阅读(4138)  评论(0编辑  收藏  举报