Javascript库开发范围和名称空间

Javascript库开发范围和名称空间,javascript,namespaces,Javascript,Namespaces,我们目前在大学的一门课程中学习了一些Javascript内容。 为此,我们为诸如show()、hide()、write等常见任务实现了一个库 目前,im正在使用以下实现运行: var myLib_maker = function () { /* private scope */ var debuggingMode=true; var currentElement=null; /* end private scope */ return { getElement: function (id

我们目前在大学的一门课程中学习了一些Javascript内容。 为此,我们为诸如show()、hide()、write等常见任务实现了一个库

目前,im正在使用以下实现运行:

var myLib_maker = function () {
/*
private scope
*/
var debuggingMode=true;
var currentElement=null;
/*
end private scope
*/
return {
    getElement: function (id) {
        var o;
        if (typeof id === 'string') { o = document.getElementById(id); }
        if (!!(o && o.nodeType !== 1)) { 
            throw {
                name: 'Type Error',
                message: 'Wrong node type at id: '+id
            } 
        }
      currentElement=o;
      return this;
    },
    getCurrentElement: function() {
        console.log(currentElement)
        return currentElement;
    },
    isVisible: function () {
        return this.getCurrentElement().style.display.toLowerCase() === "block"; 
    },
    show: function () {
        this.debug("show "+this.getCurrentElement())
        this.getCurrentElement().style.display = "block";
        return this;
    },
    hide: function () {
        this.debug("hide "+this.getCurrentElement())
        this.getCurrentElement().style.display = "none";
        return this;
    },
    toggle: function() {
        this.debug("toggle "+this.getCurrentElement())
        this.isVisible() ? this.hide(): this.show();
        return this;
    },
    write: function (content){
        this.debug("write to"+this.getCurrentElement().id);
        var tg = this.getCurrentElement().tagName.toLowerCase();
        if (tg === 'input' || tg === 'textarea') {
            currentElement.value = content; 
        } else { 
            currentElement.innerHTML = content;
        }
        return this
    },
    debug: function (what) {
        if (debuggingMode===true){
            console.log("[DEBUG] "+what);
        }
        return this;
    }

};
  }
  var myLib=myLib_maker();
然后我有一个外部功能(用于测试)来切换2个文本区域的内容

 function switchEditors(id1, id2){
      c1=myLib.getElement(id1).getCurrentElement().value;
      c2=myLib.getElement(id2).getCurrentElement().value;
      myLib.getElement(id1).write(c2)
      myLib.getElement(id2).write(c1)
 }
我首先尝试了下面的代码,这显然不起作用,因为我覆盖了我的私有currentElement,所以我总是写入id2

function switchEditors(id1, id2){
      tmp=myLib.getElement(id1).getCurrentElement().value
      myLib.getElement(id1).write(myLib.getElement(id2).getCurrentElement().value)
      myLib.getElement(id2).write(tmp)
 } 
但我最初真正想要的不是使用私有currentElement变量。 write方法的第一个实现扩展了Element对象

Element.prototype.write= function (content){
    var tg = this.tagName.toLowerCase();
    if (tg === 'input' || tg === 'textarea') {
        this.value = content; 
    } else { 
         this.innerHTML = content;
    }
    return this;
}
这样,getElement函数返回

document.getElementById(id)
我想要级联(我希望这是一个正确的词->我指的是myLib.getElement(“myid”).show().hide()级联)并直接访问 所有元素属性,但我们不能为我们的库使用全局范围,所以我必须以任何方式封装我的库

那么,有没有一种优雅的方法可以使用级联,并且能够直接访问元素对象上的所有属性,而无需在全局元素范围内实现每个方法

还是我的自由设计完全错误,必须完全不同。 如果是这样,请告诉我,我感谢你的帮助。 (我试图弄清楚jQuery实际上是如何实现这些功能的,但没有弄清楚它是如何实现的……代码太多了……:)


我希望我描述了我的愿望和要求。如果没有,请询问更具体的细节。

如您所知,
currentElement
在调用
getElement
之间共享。相反,您可以使用创建myLib对象的新实例,并将
currentElement
绑定到该实例

getElement: function (id) {
    var o, self = Object.create(this);
    /* ... */
    self.currentElement = o;
    return self;
}

并始终使用
this.currentElement
,以便每次调用都使用自己的当前元素。

虽然Magnar的解决方案将使用这种(单例)模式,但最好避免每次调用
getElement
时创建一个全新的对象。创建“类”而不是单例是有原因的

您可以这样做:

var MyLib_Maker = (function () { // I capitalized the class as a helpful 
                                // convention recommended by Douglas Crockford

    // Private static vars
    var debuggingMode = true;
    var currentElement = null;

    // Private static methods
    function _myMethod (x, y) { // call below by _myMethod(3,4);
        return x * y;
    }

    // Private instance methods (but only if called properly: 
    // invoke below by _instMethod.call(this, 3, 4); )
    function _instMethod (x, y) {
        return this.anInstanceNumber * x * y;
    }

    // Private instance properties (quite cumbersome but doable if you 
    // absolutely must--e.g., for classes whose objects need to be clean when iterated)
    // See http://brettz9.blogspot.com/2009/02/true-private-instance-variables-in.html
    // and http://brettz9.blogspot.com/2009/02/further-relator-enhancements.html
    // (put the Relator inside the closure if you don't want it reusable (and public),
    // but then your extending classes must be inside the closure too)

    function MyLib_Maker (arg1, arg2) {
        // I recommend the following check to allow your class to be
        // instantiated without the 'new' keyword (as in jQuery/$):
        if (!(this instanceof MyLib_Maker)) {
            return new MyLib_Maker(arg1, arg2);
        }
        // Other constructor code here
        // ...
    }
    // Methods added on the prototype benefit from merely 
    // providing a low-memory reference across all instances; 
    // this will avoid adding a whole new object unnecessarily 
    // into memory
    MyLib_Maker.prototype.getElement = function () {
        // ....
        return this; // Keep the chain going (if not public
        // properties, you could add a method which also avoids 
        // extending the chain, like $(el).get() in jQuery
    };


    return MyLib_Maker;
}()); // We can invoke immediately to wrap up the closure

// Usage example:
var mlm = MyLib_Maker(2, 3).getElement().doSomething();
顺便说一句,你所描述的叫做链接;级联在类似CSS中使用,表示像瀑布中的不同波一样,一个可以覆盖另一个,正如您可以通过编写覆盖CSS中先前规则的规则所做的那样


最好不要重写元素对象,因为不管浏览器有什么不兼容之处,这都是最严重的全局命名空间污染,因为它会影响所有元素,增加了依赖该方法的另一个库(或者忽略重写内置原型本身)的机会可能会给您带来意想不到的结果。

您好,谢谢。我不明白的是,为什么要用两个附加参数调用构造函数。如果我理解正确,我只需要在私有名称空间中使用参数。这是正确的吗?如果我不使用它们,或者它们用于标识对象本身,以确定var mlm=MyLib_Maker(2,3)是var mlm2=MyLib_Maker(2,4)之外的另一个对象,那么这是否也有效。因为我不想每次都手工创建一个单独的实例。以后只能使用mlm.dowhatIlike().oreventhis()@evildead:您是如何实现的?想把它放在jsfiddle.net上吗?至于构造函数参数,只有在希望构造函数具有参数时才需要它们。内部构造函数类似于在闭包外部生成的任何构造函数,只是它可以访问所有私有变量或方法。如果愿意,可以使用它初始化对象(例如,设置私有或公共属性),但这不是必需的。我使用参数只是为了演示如何传递多个参数(即,在强制调用时,如果没有
new
,则将其拼写出来,并将其视为具有)。@evildead:要继续,否,参数不标识对象。这只是一个常规构造函数。是的,它可以根据您的要求进行链接。如果您仍然有问题,请尝试制作一个版本,并在JSFIDLE上显示给我们。Douglas Crockford建议仅对
new
JavaScript没有类的构造函数使用大写字母。原型继承是JavaScript的工作方式,所以我认为这个答案一点帮助都没有。这和预期的一样。非常感谢。也许有些解决方案比单例更漂亮,但我要说的是,这正是我所需要的。它不是单例。您已经创建了一个对象,正在使用原型继承创建其副本。这无疑是一种全新的方式。最初创建的myLib_maker确实是一个单例,即使您在内部重写它,即使JS提供了利用任何对象(包括单例)进行继承的能力。是的,您正在创建一个从当前对象继承的对象,但在这种情况下,继承是不必要的。通过使用
Object.create
,您正在内存中构建一个额外的函数,在内存中只需使用一个简单的
即可(无需新函数)。我的示例有助于避免使用
new
,同时提供了不在每次调用getElement时创建和调用函数的好处。