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

チームで作業する場合、この方法で名前空間を作成するのは良い方法です。誰もあなたの名前空間を上書きしません。また、チームが名前空間を作成するときに 1 つの場所 (ファイル) を簡単に指定できるため、どの名前が使用されているかどうかを簡単に確認できます。

次に、クロージャを使用して、必要に応じてオブジェクトのプライベート コンテキストを分離しています (例にもあるように)。クロージャーは、おそらく JS (関数型プログラミング) で最も強力なツールです。すべての内部オブジェクトが削除されるまで持続するコンテキストを作成できます。

また、 getInstance JavaScript パターンと Revelation パターンも気に入っています (これは、あっ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

私が見ることができるいくつかの簡単な問題があります。

このことを考慮:

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

2 つ目の問題は、この初期化を分割して、定義が不十分な順序で初期化しようとすると (つまり、複数の 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.logDOM 操作 (または、必要に応じて) を使用する必要があります。.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 ファイルを 1 つだけ持つことを計画している場合、requireJS は他のものよりも多くの利点を提供しません。
複数の JS ファイル (モジュールごとに 1 つ、Java でクラスを分割するのと同じ方法)requireJSを使用する場合は、インターフェースを習得する必要がある代わりに利点があります。

于 2013-04-02T04:37:32.483 に答える