JavaScript写作技巧,函数A中调用函数B,怎样在函数B中写代码中断函数A的运行?

function funcA(){
    funcB();
    //other code
}

怎么定义函数B,让B在运行的时候不仅能终止B本身,而且能终止函数A的运行?

这是个非常规的问题,我们分两大部分讨论(1.为什么一定这样做 2.怎么实现)

1. 显然,这种编码方式已经打乱了正规的程序编写原则,我们编写函数的目的就是为了封装,为了实现代码的模块化,如果B能让A退出返回, 那这种编码方式肯定比滥用 goto 语句还滥了。

这样做有必要吗?为什么一定要这样做....??

答案如下:
假如我们要扩展Array的prototype,比方说:定义一个find方法,用来返回第一个让执行函数为真的数组元素。

<script>
// by go_rush(阿舜) @ http://ashun.cnblogs.com

Array.prototype.each=function(f){
    for(var i=0;i<this.length;i++) f(this[i],i,this)
}

Array.prototype.find=function(f){  
    var result;
    this.each(function(value,index,arr){
        if (f(value,index,arr)) result=value
    })
    return result
}

var arr=[1,2,3,4,5,7,9]

function foo(v){    //检测是不是偶数
    return v%2==0
}
alert(arr.find(foo))

</script>

结果另我们大失所望。
首先:在逻辑上,程序是错误的,因为我们期望返回第一个偶数,但是程序却返回的是最后一个偶数。
其次:程序的效率是低下的,那怕是找最后一个偶数,他在找到偶数4后,仍然检测了4后面的所有元素。这个动作是多余的。

怎么办呢?请看代码中的第11行,如果检测到 f(value,index,arr) 为真的时候,能够直接中断函数 this.each() 该多好啊。效率、结果,双赢的局面.

所以对于问题一 "为什么一定这样做" , 在这里,具体到这个应用上,有足够的理由让函数B()来中断函数A()

看到这里,你可能会问: 你的 find 方法为什么不这样写?

Array.prototype.find=function(f){  
    for(var i=0;i<this.length;i++){
        if (f(this[i],i,this)) return this[i]
    }
}

这样不整个世界都清净了吗?

是的,如果我只是简单的写一个find 这样写肯定没问题,但是如果现在我正在写一个复杂的应用,或一个写一个js框架呢?

我要实现一系列的
Array.prototype.all
Array.prototype.any
Array.prototype.each
Array.prototype.map
Array.prototype.find
Array.prototype.findAll
Array.prototype.grep
Array.prototype.inject
......
详细请参见 prototype.js v1.4 有上十种方法等着实现呢,我怎不可能每个方法都用 for 循环一个一个的遍历数组把,我肯定要实现一个 each 方法作为统一入口吧。

闲话少说,我们来看怎么解决问题:

要在 B函数中终止A函数,并返回结果,目前我能想到的办法就是用异常 try{}catch(x){}

<script>
// by go_rush(阿舜) @ http://ashun.cnblogs.com

var $break=new Object()

Array.prototype.each=function(f){
    try{
        for(var i=0;i<this.length;i++){
            try{
                f(this[i],i,this)
            }catch(e){
                if (e==$break) throw e
            }
        }
    }catch(e){}
}

Array.prototype.find=function(f){  
    var result;
    this.each(function(value,index,arr){
        if (f(value,index,arr)){
            result=value
            throw $break
        }    
    })
    return result
}

var arr=[1,2,3,4,5,7,9]

function foo(v){    //检测是不是偶数
    return v%2==0
}
alert(arr.find(foo))

</script>

在第24行,如果程序已经找到第一个满足函数返回值为真的元素,那么就抛出一个自定义异常,终止 this.each()的运行..   注意第12行,只有确保函数抛出的是自定义异常才继续向上抛出异常,从而终止函数的运行。

在上面的代码中,我用的 try---catch 方法完全是用来解决本贴所提出的问题的,并未进行任何其他错误处理。

在这方面,prototype.js 通过定义两个自定义异常对象 $break 和 $continue ,既照顾到了异常处理,又解决了本贴提出的问题。
Enumerable 对象实现得很优雅,大家不妨再去体会体会 prototype.js 中Enumerable的妙处。

我们看看prototype.js 是怎么做的,我还是贴出来吧

prototype.js的代码片段摘取:
var $break    = new Object();
var $continue = new Object();

var Enumerable = {
    each: function(iterator) {
        var index = 0;
        try {
            this._each(function(value) {
                try {
                    iterator(value, index++);
                } catch (e) {
                    if (e != $continue) throw e;
                }
            });
        } catch (e) {
            if (e != $break) throw e;
        }
    },

    all: function(iterator) {
        var result = true;
        this.each(function(value, index) {
            result = result && !!(iterator || Prototype.K)(value, index);
            if (!result) throw $break;
        });
        return result;
    },

    any: function(iterator) {
        var result = true;
        this.each(function(value, index) {
            if (result = !!(iterator || Prototype.K)(value, index))
            throw $break;
        });
    return result;
},


评论: 0 | 引用: 0 | 查看次数: 4737
发表评论
登录后再发表评论!