0

我想在项目中实现 JavaScript 命名空间。我和我的同事都不是 JavaScript 专家,因此我们正在寻找一种简单有效的命名空间方法。可读性是一项巨大的资产,我们是 Java 开发人员,很容易成为 JavaScript 陷阱的猎物。

我们编写了以下概念证明:

<html><body><script>
var mynamespace = {};
mynamespace.variable = "inside mynamespace";
mynamespace.level1 = {};
mynamespace.level1.level2 = {};

(function(){
    this.variable = "inside mynamespace.level1";
    this.print = function(){ return "print() : " + this.variable };
}).apply(mynamespace.level1);

(function(){
    this.variable = "inside mynamespace.level1.level2";
    this.print = function(){ return "print() : " + this.variable };
}).apply(mynamespace.level1.level2);

// We can use "that" instead of "this"; however I do not see any benefit in doing it...
(function(){
    if(!mynamespace.level1.level2b){ this.level2b = {}; var that = this.level2b };
    that.variable = "inside mynamespace.level1.level2b";
    that.print = function(){ return "print() : " + this.variable };
}).apply(mynamespace.level1);

document.write(mynamespace.level1.print() + "<br>"); // print() : inside mynamespace.level1
document.write(mynamespace.level1.variable + "<br>");// inside mynamespace.level1

document.write(mynamespace.level1.level2.print() + "<br>");// print() : inside mynamespace.level1.level2
document.write(mynamespace.level1.level2.variable + "<br>");// inside mynamespace.level1.level2

document.write(mynamespace.level1.level2b.print() + "<br>");// print() : inside mynamespace.level1.level2b
document.write(mynamespace.level1.level2b.variable + "<br>");// inside mynamespace.level1.level2b

document.write(mynamespace.variable + "<br>");// inside mynamespace
document.write(mynamespace.something + "<br>");// undefined
</script></body></html>

=== 问题 ===

Q1。你认为这是一个好方法吗?

Q2。我们会遇到什么陷阱吗?

Q3。您有更好的建议吗(请考虑简单性、可读性)?

非常感谢任何帮助。

干杯,

编辑: 我们将使用 jQuery。因此,我们会考虑使用 jQuery 的更好方法。

4

3 回答 3

2

即使对于简单的项目,我也总是使用 JavaScript 命名空间。我正在使用以下命名空间模式(在 JavaScript 模式中找到):

var SN = SN || {};

SN.namespace = function(ns_string) {
    var parts = ns_string.split('.'),
        parent = SN,
        i;

    // strip redundant leading global
    if(parts[0] === "SN") {
        parts = parts.slice(1);
    }

    for(i = 0; i < parts.length; i++) {

        //create property if it doesn't exist
        if(typeof parent[parts[i]] === "undefined") {
            parent[parts[i]] = {};
        }

        parent = parent[parts[i]];
    }

    return parent;
}

你可以像这样使用它:

SN.namespace('SN.subnamespace1.subnamespace3');
SN.subnamespace1.subnamespace3.object = function() {};

在团队工作时以这种方式创建命名空间是一种很好的方法——没有人会覆盖你的命名空间。当团队将创建命名空间时,您还可以轻松地指定一个位置(文件),这样就可以很容易地看到使用或不使用的名称。

然后,如果需要,我将使用闭包来分隔对象的私有上下文(正如您在示例中所做的那样)。闭包可能是 JS(函数式编程)中最强大的工具。您可以创建持续存在直到删除每个内部对象的上下文。

我也喜欢getInstance JavaScript 模式和启示模式(不管有new没有都可以):

var cat = function(name, color, size) {
    var _color = color,
        _size = size,
        _name = name,
        getName = function() { return _name; },
        getColor = function() { return _color; },
        getSize = function() { return _size; };
    return {
        color: getColor,
        size: getSize,
        name: getName
    }
};

var new_cat1 = new cat('a', 'b', 1);
var new_cat2 = new cat('c', 'd', 2);
var new_cat3 = cat('e', 'f', 3);

console.log(new_cat1.name());
console.log(new_cat2.name());
console.log(new_cat3.name());

还请阅读我在开始时提到的那本书——我个人认为这是用 JS 编程时最佳实践的最佳来源。我从这本书中学到了很多!

于 2013-04-02T04:09:11.287 回答
0

就个人而言,我认为你已经做得很好了,只是一些小事情需要改进。首先,您应该将您的命名空间移动到一个单独的文件中,而不是像这样将它与您的工作放在同一个文件中。我建议你把它放在namespace.js. 接下来,也很重要,你应该检查当前命名空间是否存在。您上面的代码总是声明新变量。这是一个相当严重的问题,特别是当你的同事已经声明了命名空间并为该命名空间添加了一些属性时,你的代码将完全覆盖你的同事,并且当你拉取同事的代码时总是会出错。我会建议:

namespace.js

var window.MyNameSpace = window.MyNameSpace || {};
MyNameSpace.variable = "inside mynamespace";
MyNameSpace.level1 = MyNameSpace.level1 || {};
MyNameSpace.level1.level2 = MyNameSpace.level1.level2 || {};

因此,当您的同事已经声明了命名空间或向现有命名空间添加了一些属性时,它不会覆盖现有的命名空间。假设您的同事已经创建MyNameSpace并添加了MyNameSpace.level1 = something. 上面的代码不会覆盖MyNameSpaceand MyNameSpace.level1,它只是指向当前现有的命名空间及其属性。

注意必须在其他 js 文件之前包含 namespace.js,否则会undefined出错。

希望这有帮助!

于 2013-04-02T04:09:40.623 回答
0

我可以看到一些快速的问题。

考虑一下:

var car = {};
car.manufacturer = "Toyota";
car.doors = 4;
car.format = "sedan";
car.wheels = 4;

这里有很多重复。
如果您要尝试使其成为进一步嵌套的对象,但继续以这种方式定义属性,则需要大量重新键入或复制/粘贴。

var garage = {};
garage.cars = [];
garage.cars[0] = {};
garage.cars[0].doors = 4;
garage.cars[0].drivers = [];
garage.cars[0].drivers[0].name = "Bob";

...等等。

这里的第一个问题是很容易以一种或另一种形式错过细节。有太多的行,太多的重复,也许在第 37 行,你忘记了初始化cars[3].drivers[1].license_number

第二个问题是,如果你打破了这个初始化,但尝试以定义不明确的顺序进行初始化(即:对多个 JS 文件执行此操作,或对多个 AJAX 调用执行此操作),那么您将面临命中的风险一个例外,当您尝试将属性添加到尚不存在的成员时。

// 核心 js var MyApp = {}; MyApp.modules = {};

// js #2 在 js #1 之前加载 MyApp.modules.myModule.subModule = {}; // 会抛出异常

// js #1 MyApp.modules.myModule = {};

有很多方法可以解决这个问题,但它们都取决于您希望实现的目标:

如果您正在考虑构建具有静态默认值的数据结构,那么我建议您构建一个像这样的简单数据结构:

var car = {
    doors : 4,
    manufacturer : "Toyota"
};

或者像这样更精细的对象:

var garage = {
    cars : [
        {
            doors : 2,
            drivers : [
                { name : "Bob", age : 32 },
                { name : "Susanne", age : 18 }
            ]
        }
    ]
};

它们仍然可以很容易地被引用,如下所示:

garage.cars[0].drivers[1].name; // Susanne

如果您正在构建功能模块,例如库,那么命名空间仍然是一个好主意。
以其他答案中提供的任何一种方式执行此操作。

系统检查:

if (MyApp && MyApp.modules.myModule) {
    MyApp.modules.myModule.subModule = {
        send : function () { }
    };
}

或者构建一个可以使用的函数,例如:

namespace("MyApp.modules.myModule.subModule.addOn", { method : function () {},
                                                      property_1 : 1,
                                                      property_2 : false       });

然后可以拆分字符串并检查每个名称是否存在,如果不存在则创建它们。

另外,不要使用document.write. 浏览器现在可以很好地防止它搞砸网站,但您应该将console.log其用于日志记录目的,或 DOM 操作(或.innerHTML,如果需要)用于写入页面。

that没有真正做到你认为的那样。
问题this在于,如果您在另一个函数内部有一个函数,this它将变为window,而不是与以前保持相同。

一个例子:

function decrease_fuel () { this.fuel -= 1; }

var car = {
    fuel : 100,
    speed : 10,
    pos  : { long : ..., lat : ... },
    dir  : { long : ..., lat : ... },
    drive : function () {
        if (this.fuel > 0) {
            this.pos.long += this.dir.long * this.speed;
            this.pos.lat  += this.dir.lat  * this.speed;
            decrease_fuel(); // whoops! `this === window`
            // decrease_fuel.call(this); would work
        }
    }
};

因此,如果您要创建一个希望内部范围指向与this外部相同的函数,您将使用一个变量来保存外部的值this,以便内部通过闭包引用。

var outer = function () {
    var that = this,
        inner = function () { console.log(that); };
    inner();
};

outer.call({ name : "Bob" }); // logs bob object

如果中的行innerconsole.log(this);,那么:

outer.call({ name : "Bob" }); // would log `window` object

至于namespacingvs requireJS,在某种程度上,它们都是同一个东西。
它们的不同之处在于它们的用途。
requireJS旨在用于加载多个文件,并确保它们以正确的顺序加载。如果您使用 requireJS 定义命名空间,那么它们将在加载完成后被命名空间。

使用简单的命名空间功能,您可以按照项目约定的方式加载单个组件,然后在每个文件中使用命名空间功能添加每个模块。

如果您只打算拥有一个 JS 文件,那么 requireJS 不会比另一个提供太多好处。
如果您将拥有多个 JS 文件(每个模块一个,就像您可能在 Java 中拆分类一样),那么requireJS可以提供一些好处,但需要您学习接口。

于 2013-04-02T04:37:32.483 回答