Javascript 库开发范围和命名空间

发布于 2024-10-31 10:27:44 字数 3229 浏览 1 评论 0原文

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

目前我正在运行这样的实现:

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 是如何实际实现这些东西的,但没有得到真正的线索是如何完成的......太多的代码......:))

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

we currently learn some Javascript stuff in a course at the university.
For that we implement a library for common tasks like show(), hide(), write and such things.

Currently im running with an implementation like:

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();

Than I have an external function (for testing) to switch 2 textareas contents.

 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)
 }

I first tried with the following code, which obviously does not work, cause I overwrite my private currentElement and so I write always to 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)
 } 

But what I really wanted initially was not using a private currentElement variable.
The first implementation of the write method extended the Element Object

Element.prototype.write= function (content){
    var tg = this.tagName.toLowerCase();
    if (tg === 'input' || tg === 'textarea') {
        this.value = content; 
    } else { 
         this.innerHTML = content;
    }
    return this;
}

and such the getElement function returned

document.getElementById(id)

I want cascading (I hope this is the right word -> I mean the myLib.getElement("myid").show().hide() concatenation thing) and getting direct access to
all Element attributes but we must not use global scope for our library, so I have to encapsulate my library in any way.

So is there an elegant way to use the cascading thing and be able to get a direct access to all attributes on an element object without implementing each method within the global element scope?

Or is my lib desing completely wrong and has to be done totally different.
If so, just tell me, I appreciate any help.
(I tried to figure out how jQuery actually implement these things, but didn't get a real clue how it is done ... too much code ... :) )

I hope I described my wishes and requirements. If not please ask for more specific details.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绳情 2024-11-07 10:27:44

正如您所了解的,currentElement 在对 getElement 的调用之间共享。相反,您可以使用 Object.create 创建 myLib-object 的新实例并绑定 currentElement 对此。

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

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

As you've figured out, the currentElement is shared between calls to getElement. Instead you could create a new instance of myLib-object with Object.create and bind currentElement to that.

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

And use this.currentElement throughout so that each call uses its own current element.

夏尔 2024-11-07 10:27:44

虽然 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 中先前规则的规则来做到这一点。

最好不要重写 Element 对象,因为无论浏览器不兼容,这都是最糟糕的全局命名空间污染,因为它会影响所有元素,增加另一个依赖该方法(或粗心的库)的机会覆盖内置原型本身)可能会给您带来意想不到的结果。

While Magnar's solution will work with this (singleton) pattern, it is a better idea to avoid creating a whole new object each time you call getElement. There is a reason for creating "classes" instead of singletons.

You can do it like this:

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();

By the way, what you describe is called chaining; cascading is used in the likes of CSS to indicate that like different waves out of a waterfall, one may write over the other, as you can do by writing rules which override prior ones in CSS.

And it is good you moved away from overriding the Element object because, whatever the browser incompatibilities, this is the worst kind of global namespace pollution because it affects all elements, increasing the chance that another library which depends on that method (or which is careless in overriding the built-in prototypes itself) may get you unexpected results.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文