Simulating class in JavaScript -- 9

来源:互联网 发布:pc蛋蛋预测源码 编辑:程序博客网 时间:2024/06/05 18:53

9.7.2 Duck Typing

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为的对象,并调用它的方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

    尝试这种方式,如果当执行类的所有方法,那么它就是类的实例。如果一个对象有着X类定义的属性,你可以把它当成是X的实例,甚至它不是由X()构造函数所创建的。

Duck Typing 在类之间的衔接是特别有用的,可以借用别的类的方法。上一章,一个Rectangle类借用了GenericEquals的equals()方法的安装。因此,你可以认为任何Rectangle的实例也是GenericEquals的实例,instanceof操作符是不会报告这点的,但我们可以也将要定义一个这样的方法。

//Testing whether an object borrows the methods of class//Return true if each of the method properties in c.prototype have been//borrowed by o. If o is a function rather than an object, we//test the prototype of o rather than o itself.//Note that this function requires methods to be copied, not//reimplemented. If a class borrows a method and then overrides it,//this method will return false.function borrows(o, c) {    //If we are an instance of something,then of course we have its methods    if (o instanceof c) return true;    //It is impossible to test whether the methods of a built-in type have     //been borrowed, since the methods of built-in type are not enumerable.    //We return undefined in this case as a kind of "I don't know" answer    //instead of throwing an exception. Undefined behaves much like false,    //but can be distinguished from false if the caller needs to.    if (c == Array || c == Boolean || c == Date || c == Error || c == Function || c == Number || c == RegExp || c == String)        return undefined;    if (typeof o == "function") o = o.prototype;    var proto = c.prototype;    for (var p in proto) {        //Ignore properties that are not functions        if (typeof proto[p] != "function") continue;        if (o[p] != proto[p]) return false;    }    return true;}

上面的borrows()方法是相对地严格,它要求了对象必须要真的有c类的定义的方法的copy.真正的duck typing是更加灵活的。o应该是被认为是c的实例,只要它提供一些跟c的方法很像的方法。在JavaScript里,“看起来很像”意味着“与之有着同样的名字”或者“声明了与之一样个数的参数”。下面将给一个这样的例子

//Return true if o has methods with the same name and arity as all//methods in c.prototype. Otherwise, return false. Throws an exception//if c is a built-in type with nonumerable methods.function provides(o, c) {    //If o actually is an instance of c, it obviously looks like c    if (o instanceof c) return true;    //If a consturctor was passed instead of an object, use its prototype    if (typeof o == "function") o = o.prototype;    //The methods of built-in types are not enumerable, and we return    //undefined. Otherwise, any object would appear to provide any of     //the built-in type.    if (c == Array || c == Boolean || c == Date || c == Error || c == Function || c == Number || c == RegExp || c == String)        return undefined;    var proto = c.prototype;    for (var p in proto) { // Loop through all properties in c.prototype        // Ignore properties that are not functions        if (typeof proto[p] != "function") continue;        // If o does not have a property by the same name, return false        if (!(p in o)) return false;        // If that property is not a function, return false        if (typeof o[p] != "function") return false;        // If the two functions are not declared with th same number        // of arguments, return false        if (o[p].length != proto[p].length) return false;    }    return true;}

正如provides()方法是有用的,就考虑下之前讲过的compareTo()。compareTo()并不是一个把自己借给别的类的,但是如果我们用compareTo()方法简单的测一下对象也是不错的。下面定义Comparable类:

 function Comparable() { }    Comparable.prototype.compareTo = function (that) {        throw "Compareble.compareTo() is abastract. Don't inovoke it";     }    

Comparable类是抽象的:它的方法事实上不是设计成被调用,但是简单的定义成一个API。这个类定义了后,那么,你就可以测测这个对象是否可以这样比较:

//Check whether objects o and p can be compared//They must be of the same type, and that type must be comparableif (o.constructor == param.constructor && provides(o, Comparable)) {    var order = o.compareTo(p);}

注意了borrows()和 provides()方法两者都表现了如果传给任何的 JavaScript核心的内部类型则返回undefined,例如Array,这是因为内部类型的prototype对象的属性在for/in循环中并不是枚举的,所以这里要特别的区分开来。

Array Like在duck typing中要特别的考虑下:

function isArrayLike(x) {    if (x instanceof Array) return true; // Real arrays are array-like    if (!("length" in x)) return false; // Arrays must have a length property    if (typeof x.length != "number") return false; // Length must be a number    if (x.length < 0) return false;    if (x.length > 0) {        //If the array is nonempty, it must at a minimum        //have a property defined whose name is the number length-1        if(!((x.length-1) in x)) return false;    }    return true;}



原创粉丝点击