`
robinqu
  • 浏览: 90260 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

JavaScript Class模拟深入 - Duck Typing 和 Class相关的工具类

阅读更多
Duck Typing
引用
If it implements all the methods defined by a class, it is an instance of that class.

Duck typing is particularly useful in conjunction with classes that "borrow" methods from other classes. Earlier in the chapter, a Rectangle class borrowed the implementation of an equals( ) method from another class named GenericEquals. Thus, you can consider any Rectangle instance to also be an instance of GenericEquals. The instanceof operator will not report this, but you can define a method that will.


Duck Typing的假设如下,如果一个对象实现了一个类中的所有方法,那么这个对象就是这个类的实例。

Duck Typing和之前讲的“方法借用”一起就非常有用,在Duck Typing的假设下,你的确可以说之前例子中的Rectangle是GenricEquals的实例。但是instanceof并不这么认为,我们只有自己写一个方法来判断:

// 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 types 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;
}


引用
The borrows( ) method of Example 9-7 is relatively strict: it requires the object o to have exact copies of the methods defined by the class c.

TRue duck typing is more flexible: o should be considered an instance of c as long as it provides methods that look like methods of c.



这个borrow()函数严格的判断这个对象是否实现了类中的所有方法,但有时候我们的Duck Typing需要更加灵活。我们只需要这个对象提供了同名方法,以及方法的参数一致就可以了。
下面是实现这种判定的函数:

// 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 nonenumerable 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 constructor 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 types.
    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 the same number
        // of arguments, return false.
        if (o[p].length != proto[p].length) return false;
    }
    // If all the methods check out, we can finally return true.
    return true;
}


下面给出一个实际的使用环境。假设Comparable()是一个通用的API接口,我们用provide()函数就可以判定某个对象是否拥有了Comparable中的同名方法,也就是有没有类似的compareTo()方法:

function Comparable( ) {}
Comparable.prototype.compareTo = function(that) {
    throw "Comparable.compareTo( ) is abstract.  Don't invoke it!";
}

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


最后给一个定义类的函数,老外实在太NB了,自己看注释吧,虽然很长不过很清晰:

/**
 * defineClass( ) -- a utility function for defining JavaScript classes.
 *
 * This function expects a single object as its only argument.  It defines
 * a new JavaScript class based on the data in that object and returns the
 * constructor function of the new class.  This function handles the repetitive
 * tasks of defining classes: setting up the prototype object for correct
 * inheritance, copying methods from other types, and so on.
 *
 * The object passed as an argument should have some or all of the
 * following properties:
 *
 *      name: The name of the class being defined.
 *            If specified, this value will be stored in the classname
 *            property of the prototype object.
 *
 *    extend: The constructor of the class to be extended. If omitted,
 *            the Object( ) constructor will be used. This value will
 *            be stored in the superclass property of the prototype object.
 *
 * construct: The constructor function for the class. If omitted, a new
 *            empty function will be used. This value becomes the return
 *            value of the function, and is also stored in the constructor
 *            property of the prototype object.
 *
 *   methods: An object that specifies the instance methods (and other shared
 *            properties) for the class. The properties of this object are
 *            copied into the prototype object of the class. If omitted,
 *            an empty object is used instead. Properties named
 *            "classname", "superclass", and "constructor" are reserved
 *            and should not be used in this object.
 *
 *   statics: An object that specifies the static methods (and other static
 *            properties) for the class. The properties of this object become
 *            properties of the constructor function. If omitted, an empty
 *            object is used instead.
 *
 *   borrows: A constructor function or array of constructor functions.
 *            The instance methods of each of the specified classes are copied
 *            into the prototype object of this new class so that the
 *            new class borrows the methods of each specified class.
 *            Constructors are processed in the order they are specified,
 *            so the methods of a class listed at the end of the array may
 *            overwrite the methods of those specified earlier. Note that
 *            borrowed methods are stored in the prototype object before
 *            the properties of the methods object above. Therefore,
 *            methods specified in the methods object can overwrite borrowed
 *            methods. If this property is not specified, no methods are
 *            borrowed.
 *
 *  provides: A constructor function or array of constructor functions.
 *            After the prototype object is fully initialized, this function
 *            verifies that the prototype includes methods whose names and
 *            number of arguments match the instance methods defined by each
 *            of these classes. No methods are copied; this is simply an
 *            assertion that this class "provides" the functionality of the
 *            specified classes. If the assertion fails, this method will
 *            throw an exception. If no exception is thrown, any
 *            instance of the new class can also be considered (using "duck
 *            typing") to be an instance of these other types.  If this
 *            property is not specified, no such verification is performed.
 **/
function defineClass(data) {
    // Extract the fields we'll use from the argument object.
    // Set up default values.
    var classname = data.name;
    var superclass = data.extend || Object;
    var constructor = data.construct || function( ) {};
    var methods = data.methods || {};
    var statics = data.statics || {};
    var borrows;
    var provides;

    // Borrows may be a single constructor or an array of them.
    if (!data.borrows) borrows = [];
    else if (data.borrows instanceof Array) borrows = data.borrows;
    else borrows = [ data.borrows ];

    // Ditto for the provides property.
    if (!data.provides) provides = [];
    else if (data.provides instanceof Array) provides = data.provides;
    else provides = [ data.provides ];

    // Create the object that will become the prototype for our class.
    var proto = new superclass( );

    // Delete any noninherited properties of this new prototype object.
    for(var p in proto)
        if (proto.hasOwnProperty(p)) delete proto[p];

    // Borrow methods from "mixin" classes by copying to our prototype.
    for(var i = 0; i < borrows.length; i++) {
        var c = data.borrows[i];
        borrows[i] = c;
        // Copy method properties from prototype of c to our prototype
        for(var p in c.prototype) {
            if (typeof c.prototype[p] != "function") continue;
            proto[p] = c.prototype[p];
        }
    }
    // Copy instance methods to the prototype object
    // This may overwrite methods of the mixin classes
    for(var p in methods) proto[p] = methods[p];

    // Set up the reserved "constructor", "superclass", and "classname"
    // properties of the prototype.
    proto.constructor = constructor;
    proto.superclass = superclass;
    // classname is set only if a name was actually specified.
    if (classname) proto.classname = classname;

    // Verify that our prototype provides all of the methods it is supposed to.
    for(var i = 0; i < provides.length; i++) {  // for each class
        var c = provides[i];
        for(var p in c.prototype) {   // for each property
            if (typeof c.prototype[p] != "function") continue;  // methods only
            if (p == "constructor" || p == "superclass") continue;
            // Check that we have a method with the same name and that
            // it has the same number of declared arguments.  If so, move on
            if (p in proto &&
                typeof proto[p] == "function" &&
                proto[p].length == c.prototype[p].length) continue;
            // Otherwise, throw an exception
            throw new Error("Class " + classname + " does not provide method "+
                            c.classname + "." + p);
        }
    }

    // Associate the prototype object with the constructor function
    constructor.prototype = proto;

    // Copy static properties to the constructor
    for(var p in statics) constructor[p] = data.statics[p];

    // Finally, return the constructor function
    return constructor;
}


演示一下这东西怎么用:
// A Comparable class with an abstract method
// so that we can define classes that "provide" Comparable.
var Comparable = defineClass({
    name: "Comparable",
    methods: { compareTo: function(that) { throw "abstract"; } }
});

// A mixin class with a usefully generic equals( ) method for borrowing
var GenericEquals = defineClass({
    name: "GenericEquals",
    methods: {
        equals: function(that) {
            if (this == that) return true;
            var propsInThat = 0;
            for(var name in that) {
                propsInThat++;
                if (this[name] !== that[name]) return false;
            }

            // Now make sure that this object doesn't have additional props
            var propsInThis = 0;
            for(name in this) propsInThis++;

            // If this has additional properties, then they are not equal
            if (propsInThis != propsInThat) return false;

            // The two objects appear to be equal.
            return true;
        }
    }
});
// A very simple Rectangle class that provides Comparable
var Rectangle = defineClass({
    name: "Rectangle",
    construct: function(w,h) { this.width = w; this.height = h; },
    methods: {
        area: function( ) { return this.width * this.height; },
        compareTo: function(that) { return this.area( ) - that.area( ); }
    },
    provides: Comparable
});
// A subclass of Rectangle that chains to its superclass constructor,
// inherits methods from its superclass, defines an instance method and
// a static method of its own, and borrows an equals( ) method.
var PositionedRectangle = defineClass({
    name: "PositionedRectangle",
    extend: Rectangle,
    construct: function(x,y,w,h) {
        this.superclass(w,h);  // chain to superclass
        this.x = x;
        this.y = y;
    },
    methods: {
        isInside: function(x,y) {
            return x > this.x && x < this.x+this.width &&
                y > this.y && y < this.y+this.height;
        }
    },
    statics: {
        comparator: function(a,b) { return a.compareTo(b); }
    },
    borrows: [GenericEquals]
});
分享到:
评论

相关推荐

    AbstractFactory-Duck.rar_Duck!

    抽象工厂模式是软件设计模式中的一种重要模式,它属于创建型模式,主要用来解决在不同的平台或者环境下创建一组相关的对象,而无需指定它们的具体类。在这个"AbstractFactory-Duck.rar_Duck!"的例子中,我们看到它...

    saga-duck:用于redux-saga的可扩展和可组合鸭

    传奇鸭 ... 您只应通过duck.selector或duck.selectors访问存储。 单鸭 import { Duck } from "saga-duck" ; import { takeEvery , call , put , select } from "redux-saga/effects" ; import { delay }

    Android代码-DuckDuckGo Privacy Browser

    DuckDuckGo Android Welcome to our android application. We are excited to engage the community in development, see CONTRIBUTING.md. We are hiring! Are you a talented cross-platform mobile developer? We...

    基于Spring MVC/Spring的快速开发框架K-Duck-Core设计源码

    K-Duck-Core是一个开源、免费的Java快速开发框架,基于Spring MVC、Spring和Spring JdbcTemplate构建。该框架通过封装数据表为对象,自动处理数据访问层逻辑,减轻开发者编写DAO代码的负担,并要求使用SQL装配方式...

    assert-duck-type:Node.js 中函数参数的运行时类型断言

    "assert-duck-type" 是一个专门用于函数参数运行时类型检查的库,它基于 JavaScript 的鸭子类型(Duck Typing)概念,帮助开发者确保传入的参数符合预期的结构。本文将深入探讨鸭子类型、assert-duck-type 库的工作...

    项目:《会游泳的小黄鸭》点击链接预览___↓↓↓_yellow-duck-1.zip

    项目:《会游泳的小黄鸭》点击链接预览___↓↓↓_yellow-duck-1

    zsh-duckduckgo:在您的 z-shell 中从 DuckDuckGo 获得非常简单的响应

    `zsh-duckduckgo` 是一个专为 Z-Shell(ZSH)用户设计的插件,它使得用户可以直接在命令行环境中通过 DuckDuckGo 搜索引擎获取信息,无需离开终端。这个工具提供了高效且简洁的交互方式,帮助用户快速找到所需的信息...

    Mountain-Duck-4.10.3

    MacOS 将云存储挂载为本地磁盘Mountain_Duck_4.10.3

    rubber-duck-debugger:提供惊人的、功能齐全的调试工具的自定义元素

    带有全功能调试工具的自定义元素,具有惊人的性能。 演示 安装 使用安装组件: $ bower install rubber-duck-debugger --save 或 。 用法 导入 Web Components 的 polyfill: &lt; script src =" bower_...

    React-Duck2Go:由Duck​​DuckGo API提供支持的React应用

    在JavaScript编程语言的环境下,React-Duck2Go通过创建可复用的组件和优化的同构(即服务器端和客户端都能运行的代码)架构,实现了高效且用户友好的界面。 首先,我们来看React。React是Facebook开发的一个用于...

    querygate-duckduckgo

    总的来说,"querygate-duckduckgo"项目提供了一个与DuckDuckGo搜索引擎交互的工具,利用TypeScript的强类型系统和面向对象特性,为开发者带来了更好的类型安全和开发体验。通过学习和使用这个项目,开发者不仅可以...

    https-duckduckgo.com-:鸭鸭去

    【标题】:“https-duckduckgo.com-:鸭鸭去” 暗示了我们...同时,通过分析提供的文件名,我们可以推测该压缩包可能包含有关DuckDuckGo网站源代码的信息,这对于开发者研究其技术实现或者了解其隐私保护机制具有价值。

    redux-duck-immer:帮助者适应鸭子-模块化-redux提案

    Redux-Duck-immer 受启发。 提供redux帮助器以实现建议。 减速器的状态由产生,以实现不变性。安装yarn add redux-duck-immer原料药定义动作类型import { defineType } from 'redux-duck-immer' ;/** * type Action...

    odin-duckduckgo-homepage

    【标题】"Odin DuckDuckGo Homepage" 是一个项目,专注于DuckDuckGo搜索引擎主页的CSS和HTML实现,力求简洁且高效,不依赖JavaScript元素。这个项目旨在提供一个用户体验良好、隐私保护的网页设计示例。 【描述】...

    community-platform, DuckDuckGo社区平台.zip

    community-platform, DuckDuckGo社区平台 DuckDuckGo --社区平台这是 duck.co DuckDuckGo社区平台的源代码。要求/安装DuckDuckGo社区平台构建在 Perl Catalyst Catalyst DBIx::Class,Text::Xslat

    redux-duck:Helper函数使用ducks-modular-redux建议创建Redux模块

    红鸭子 Helper函数使用建议创建Redux模块。 安装 yarn add redux-duck 原料药 创建鸭子 ... 如果未定义应用程序或模块名称,则结果应为字符串,例如application-name/duck-name/ACTION_TYPE或duck-

    run-duck-run:发电机功能运行器

    :duck: 鸭跑 :duck: 发电机功能运行器 用法 function * foo ( ) { yield ( cb ) =&gt; setTimeout ( cb , 1000 ) console . log ( 'done cb' ) } const run = require ( 'run-duck-run' ) run ( foo , ( err ) =...

    Game-FPS-Duck:使用Java Swing的游戏射击游戏

    Java作为一款跨平台的编程语言,提供了丰富的库和工具来帮助开发者创建各种类型的游戏。本文将详细探讨如何使用Java Swing构建一个名为"Game-FPS-Duck"的第一人称射击(FPS)游戏,同时也会涉及相关的编程概念和技术...

    docker-duck-dns

    docker-duck-dns

    rubber-duck-scripts:USB Rubber Duckey的有用脚本

    标题 "rubber-duck-scripts:USB Rubber Duckey的有用脚本" 涉及到一个名为USB Rubber Duckey的独特工具,这是一个硬件设备,通常用于安全测试和渗透测试中。它模拟了键盘输入,允许测试人员快速执行一系列预定义的...

Global site tag (gtag.js) - Google Analytics