一、接口

在JS这种弱类型的语言中,类型不匹配错误很难跟踪。可以使用接口来进行类型检查。如果强制使用,又会弱化语言的灵活性。因为有额外的代码调用,所以又会降低性能。解决方法就是在开发时进行类型检查,在开始完成后删除此部分代码。

但JS中的接口实现方式是模拟的,它以文档和方法检查相结合。缺点在于它只检查方法的名称,并不检查参数的名称,类型,数量以及方法的返回值。

接口定义工具类

var Interface = function(name, methods) {
    if(arguments.length != 2) {
        throw new Error("Interface constructor expected exactly 2.");
    }
    this.name = name;//接口名称
    this.methods = [];//接口的方法名称数组
    for(var i = 0, len = methods.length; i < len; i++) {
        if(typeof methods[i] !== 'string') {
            throw new Error("Interface constructor expects method names is String.");
        }
        this.methods.push(methods[i]);
    }
};

Interface.ensureImplements = function(object) {
    if(arguments.length < 2) {
        throw new Error("Interface.ensureImplements exactly arguments at least 2.");
    }
    for(var i = 1, len = arguments.length; i < len; i++) {
        var interface = arguments[i];
        if(interface.constructor !== Interface) {
            throw new Error(interface + "exactly type is Interface.");
        }
        for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
            var method = interface.methods[j];
            if(!object[method] || typeof object[method] !== 'function') {
                throw new Error("Function Interface.ensureImplements: object "
                + "does not implement the " + interface.name
                + " interface. Method " + method + " was not found.");
            }
        }
    }
};
//模拟使用
//定义了接口Person0和Person1,并在每个接口中定义三个方法
var Interface0 = new Interface('Person0', ['add', 'del', 'getItem']);
var Interface1 = new Interface('Person1', ['add', 'del', 'getItem']);
function addForm(myClassInstance){
    //用来检查myClassInstance是否实现了接口Interface0和Interface1
    Interface.ensureImplements(myClassInstance, Interface0, Interface1)
}

JS的灵活性是其最大的特点,所以在强制类型检查时需要有选择的使用。

二、封装和信息隐藏

创建私有成员是OOP中最基本和有用的特性之一。信息隐藏是目的,封装则是达到这个目的的技术。JS实现封装的方式多种多样,比如公用、私用、特权。下面有两个简单的例子。

私有属性、方法、静态方法。

Book.getInt8 = function(){
    //static Method
}
Book.prototype = {
    _checkIsbn: function (isbn) {
    }//用「-」人为约定私有方法和属性,并不在程序中提供保护
}

//用闭包的方式来实现私有方法和变量
var Book = function(newIsbn, newTitle, newAuthor) {
    // Private attributes.
    var isbn, title, author;
    // Private method.
    function checkIsbn(isbn) {
    }
}

常量

var Class = (function() {
    // Private static attributes.
    var constants = {
        UPPER_BOUND: 100,
        LOWER_BOUND: -100
    }
    //Constructor
    var ctor = function(constructorArgument){};
    // Privileged static method.
    ctor.getConstant = function(name){
        return constants[name];
    }
    // Return the constructor.
    return ctor;
})();
/* 调用,如果在上面的括号中加入参数也可以实现常量的动态实例化 */
var t = Class.getConstant('UPPER_BOUND');