4

私の意図は、アルゴリズムの設計、パフォーマンス、およびクロスブラウザーの互換性に関して、以下のスクリプトについてのあなたの考えと批判を得ることです.

私は JavaScript を使い始めたばかりで、長い間その素晴らしさを見逃していました。私のバックグラウンドと経験は、C/C++/PHP ベースの RESTful バックエンドの開発です。

言語とその正しい使い方を理解するために、私はこれまでに何度もやったことがあるはずのことをすることにしました。しかし、新しい言語とパラダイムを使用することを学ぶことは、とにかく苦痛を伴うことがよくあります.

これは、通常のフォーム処理および検証スクリプト/関数を作成する私の試みです。複雑さを軽減し、コードをシンプル/クリーンに保つために、HTML5 カスタム データ属性 (data-*) を使用して、フォーム内の各要素にメタデータを割り当てることにしました。

  1. データ必須: 真または偽。true に設定すると、このパラメーターはフォーム フィールドを必須にするため、空にすることはできません。false に設定された値は、フィールドがオプションであることを示します。デフォルトは false です。 >

  2. Data-Type: 実行する検証のタイプ。例には、「email」、「password」、「numbers」、またはその他の「regexp」が含まれます。

そのようなフォームの非常に単純な例は次のとおりです。

<form action="postlistings" id="postlistings" enctype='multipart/form-data' method="post" class="postlistings">
    <ul class="login-li">
        <li>
            <input class="title" name="title" type="title" id="title" data-required="true" data-type="title"></a>
        </li>
        <li>
            <textarea name="body" id="elm1" class="elm1" name="elm1" data-type="body" data-required="true" >
            </textarea>
        </li>
        <li>
        <span class="nav-btn-question">Add Listing</span>
        </li>
    </ul>
</form>

注意: これは私の最初の JavaScript コードです。アイデアは、フォーム名を渡して Form を呼び出し、パフォーマンスのために 1 つのループですべてのフィールド値を取得して検証することです。上記の Data-* 属性から推測できるように、検証には 2 つの手順が含まれます。

私。必須フォーム フィールドを確認します。

値がステップ 1 の要件を満たしていない場合、構成からのエラー メッセージが特定のフォーム値に対して取得されます。したがって、この要件を満たさないすべての値について、一連のエラー メッセージが収集され、ビューに渡されます。

ii. それぞれの検証を実行します。

検証は、すべての値がステップ 1 に合格した場合にのみ実行されます。それ以外の場合は、上記の 1 と同じステップに従います。

function Form(){

    var args = Array.prototype.slice.call(arguments),
        formName = args[0],
        callback = args.pop(),
        userError = [{type: {}, param: {}}],
        requiredDataParam = 'required',
        typeDataParam = 'type',
        form = document.forms[formName],
        formLength = form.length || null,
        formElement = {id: {}, name: {}, value: {}, required: {}, type: {}};

    function getFormElements(){
        var num = 0;
        var emptyContent = false;
    
        for (var i = 0; i < formLength; i += 1) {
        
            var formField = form[i];
            formElement.id[i] = inArray('id', formField) ? formField.id : null;
            formElement.name[i] = inArray('name', formField) ? formField.name : null;
            formElement.value[i] = inArray('value', formField) ? formField.value : null;
            formElement.required[i] = getDataAttribute(formField, requiredDataParam);
            formElement.type[i] = getDataAttribute(formField, typeDataParam);
        
        
            if (formElement.required[i] === true){
                if(!formElement.type[i]) {
                    error('Validation rule not defined!');
                }
                else if (!formElement.value[i]) {
                    userError[num++] = {'type': 'required', 'param': form[i]};
                    emptyContent = true;
                }
            }
            
            if (emptyContent === false) {
                // Perform validations only if no empty but required form values were found.
                // This is so that we can collect all the empty
                // inputs and their corresponding error messages.
            }
    
        }
    
        if (userError) {
            // Return empty form errors and their corresponding error messages.
        }

        return formElement;
    };

    // Removed the getFormParam function that was not used at all.

    return {
        getFormElements: getFormElements
    }
};

上記の JS スクリプトで使用される 2 つの外部関数 (JQuery ソースから):

var inArray = function(elem, array){
    if (array.indexOf){
        return array.indexOf(elem);
    }

    for (var i = 0, length = array.length; i < length; i++){
        if (array[i] === elem){
            return i;
        }
    }

    return -1;
} 


// This is a cross-platform way to retrieve HTML5 custom attributes.
// Source: JQuery

var getDataAttribute = function(elem, key, data) {
    if (data === undefined && elem.nodeType === 1) {
        data = elem.getAttribute("data-" + key);
    
        if (typeof data === "string") {
            data = data === "true" ? true :
            data === "false" ? false :
            data === "null" ? null :
                !CheckType.isNaN ? parseFloat(data) :
                CheckType.rbrace.test(data) ? parseJSON(data) :
                data;
        }
        else {
            data = undefined;
        }
    }
    return data;
}

構成エラー メッセージの例は、次のように設定できます。

var errorMsgs = {
    ERROR_email: "Please enter a valid email address.",
    ERROR_password: "Your password must be at least 6 characters long. Please try another",
    ERROR_user_exists: "The requested email address already exists. Please try again."
};

レビューのためにこれを投稿するので、私が従わなかった可能性のあるスタイリング規則は無視してください. 私の意図は、コード自体とアルゴリズムに関して、私が異なることを行うべきか、またはより良くできることについて、専門家のレビューを取得することです。

スタイリングの慣例に加えて、すべての批判と質問を歓迎します。

4

2 に答える 2

9

まず、よくある誤解を解きたいと思います。あなたがすでにこれを明確に理解しているなら、私を許してください。多分それは他の誰かに役立つでしょう。

jQuery または同様のライブラリを学習して使用することは、JavaScript 言語の学習を妨げたり、妨げたりすることはありません。jQuery は、DOM を使用する際の多くの問題点を取り除く DOM 操作ライブラリです。ライブラリを使用して DOM の詳細の一部を抽象化したとしても、言語である JavaScript を学習して使用する余地は十分にあります。

実際、DOM は「JavaScript っぽい」API ではないため、DOM を直接使用すると、JavaScript コーディングの悪い習慣を教えてしまう可能性が高いと私は主張します。JavaScript と Java で同じように動作するように設計されており、他の言語でも機能する可能性があるため、JavaScript 言語の機能を完全に活用できません。

もちろん、あなたが言ったように、あなたはこれを学習演習として使用しています。多くの人が「JavaScript を学びたいから jQuery を学びたくない!」という考えに陥るのを目にしてきたような罠にはまってほしくありません。どちらの場合も JavaScript を学ぶ必要があり、DOM に jQuery を使用しても、それはまったく妨げられません。

今、いくつかの詳細...

オブジェクト リテラル内でプロパティ名を引用することは問題ありませんが、プロパティを参照するときは、有効な JavaScript 名である場合は引用しない方が慣例であり、より読みやすくなります。たとえば、あなたのformElementオブジェクトで

formElement = { id: {}, name: {}, value: {}, required: {}, type: {} };

(そこにも最後にセミコロンがありませんでした)

そして、あなたができる名前を使用する場所:

formElement.id[i] = ...
formElement.name[i] = ...

プログラム ロジックで必要な場合を除き、ループを逆方向に実行しないでください。おそらく非常にタイトなループの場合を除いて、コードを高速化することはありません。また、時期尚早に最適化しているだけなのか、逆方向のループが実際に必要なのかが不明確になります。

最適化といえば、そのループにはいくつかのinArray()呼び出しがあります。これらのループはそれぞれ配列をループするため、外側のループよりもパフォーマンスに影響を与える可能性があります。これらの配列はおそらくかなり短いと思いますか?とにかくパフォーマンスはまったく問題になりませんが、これは、より長い配列とオブジェクトがある場合に考慮すべきことです。場合によっては、検索を高速化するためにプロパティ名と値を持つオブジェクトを使用できます。

とにかく使い方がinArray()悪い!しかし、あなたのせいではありません.jQueryのばかげた名前の関数です。名前は明らかにブール値の戻り値を示唆していますが、関数はゼロベースの配列インデックスを返す-1か、値が見つからない場合に返します。indexOf()この関数の名前を、ネイティブArrayメソッドや などに一致するように変更することを強くお勧めしarrayIndex()ます。

同じループがform[i]何度も繰り返されました。ループの先頭でこれを行うことができます:

var field = form[i];

の代わりにfield、全体を通して使用します。これは、問題がある場合は一般的に高速ですが (おそらくここではそうではありません)、読みやすくなっています。field.idform[i].id

本当に必要な場合を除きif( foo === true )、 andのような厳密なブール比較を使用しないでください。これらのケースはまれです。if( bar === false)このコードは、通常のブール値テストとは異なる何かが起こっているというシグナルをリーダーに送信します。これらの特定のテストを使用する必要があるのは、ブール値を含む可能性のある変数または他のタイプの値を含む可能性がある変数があり、どちらがどれであるかを区別する必要がある場合のみです。

これらのようなテストを使用する必要がある場合の良い例は、デフォルトで に設定されるオプションのパラメーターですtrue

// Do stuff unless 'really' is explicitly set to false, e.g.
// stuff(1) will do stuff with 1, but stuff(1,false) won't.
function stuff( value, really ) {
    if( really === false ) {
        // don't do stuff
    }
    else {
        // do stuff
    }
}

この特定の例はあまり意味がありませんが、アイデアは得られるはずです。

同様に、実際のブール値を他の「真の」値=== trueと区別する必要がある場合にも、テストを使用できます。true実際、この行はその有効なケースのようです。

if (formElement['required'][i] === true){

これは、ブール値またはその他の型を返す可能性のある関数に由来if (formElement['required'][i]します。getDataAttribute()

ただし、真実性をテストするだけの場合は (ほとんどの場合これが必要です)、単純にif( foo )orを使用しますif( ! foo )。または同様に条件式で: foo ? x : yor !foo ? x : y.

上記は、これを変更する必要があることを長々と言いました。

if (empty_content === false) {

に:

if (!empty_content) {

あなたの関数は、結果をgetFormParam()に変換するためにいくつかの作業を行います。通常、これを行う理由はありません。その関数が呼び出される場所が見当たらないので、具体的にアドバイスすることはできませんが、一般的には、このようなもので真実性をテストすることになるため、と の両方が として扱われます。または、 /を他の値 (たとえば、明示的な)と区別する必要がある場合は、orを使用して簡単に行うことができます。これは、とによって実行される「緩い」比較が非常に便利なケースの 1 つです。両方とは、これらの演算子で同じように評価されます。undefinednullnullundefinedfalsenullundefinedfalse!= null== null==!=nullundefined

あなたはコーディング スタイルを無視するように求めましたが、ここで 1 つの小さな提案: と が混在していcamelCaseNamesますnames_with_underscores。JavaScript では、camelCaseNames関数名と変数名のほうが慣用的でありPascalCaseNames、コンストラクター関数の場合です。もちろん、より意味のあるアンダースコアを自由に使用してください。たとえば、その形式のデータベース列で動作するコードを記述している場合、変数名を列名と一致させたい場合があります。

それが役立つことを願っています! 良い仕事を続けてください。

新しいコードの更新

コードのロジックをたどるのに少し問題がありますが、その理由の一部はわかっていると思います。これは、命名規則と裏返しのオブジェクトの組み合わせです。

まず、名前formElementが本当に紛らわしいです。JavaScript で見るelementと、DOM 要素 (HTMLElement) か配列要素のどちらかを思い浮かべます。これがformElementどちらか一方を表しているのか、どちらでもないのかはわかりません。

コードを見て何をしているのかを理解すると、プロパティがあることがわかりますが、コードは後でそれらのそれぞれを ではなくid:{}, name:{}, ...として扱います。ArrayObject

formElement.id[i] = ...
formElement.name[i] = ...
formElement.value[i] = ...
formElement.required[i] = ...
formElement.type[i] = ...

(iは整数インデックス)

そのコードが正しければ、代わりに配列にする必要があります: id:[], name:[], ....

しかし、これは危険信号です。JavaScript で並列配列を作成しているとしたら、それはおそらく間違っています。ほとんどの場合、並列配列を単一のオブジェクト配列に置き換える方が適切です。その配列内の各オブジェクトは、すべての並列配列を通る単一のスライスを表し、前の各配列のプロパティを持ちます。

したがって、このオブジェクト (現在の用途に合わせて から{}に修正しまし[]た):

formElement = { id: [], name: [], value: [], required: [], type: [] };

次のようにする必要があります。

formInfo = [];

そして、コードがある場所:

formElement.id[i] = ...;
formElement.name[i] = ...;
formElement.value[i] = ...;
formElement.required[i] = ...;
formElement.type[i] = ...;

そのはず:

var info = {
    id: ...,
    name: ...,
    value: ...,
    required: ...,
    type: ...
};
formInfo.push( info );

残りのコードを調整します。例えば:

formElement.required[i]

だろう:

formInfo[i].required

または同じ関数内にあるため、さらに簡単です。

info.required

info注:とは素晴らしい名前だと言っているわけではありませんformInfo:-) これらは単なるプレースホルダーであるため、より適切な名前を考えることができます。主なアイデアは、並列配列のセットではなく、オブジェクトの配列を作成することです。

最後にもう 1 つ、今は時間がありません。

そのgetDataAttribute()機能は、複雑で小さな作業です。あなたはそれを必要としません!基になる関数を必要な場所で直接呼び出す方が簡単です。

var info = {
    ...
    required: formField.getAttribute('data-required') === 'true',
    type: formField.getAttribute('data-type')
};

=== 'true'これにより、上記のテストのように、属性の解釈方法を完全に制御することもできます。(これにより適切なブール値が得られるため、後で値をテストするときに使用する必要はありません=== true。)

文体上の注意点として、はい、私は 2 つの'data-xxxx'名前をすぐにハード コードしました。そのほうが、より適切で明確な方法だと思います.C の経験に惑わされないでください。この特定のケースで文字列を「定数」と定義しても、構成可能にしたいものでない限り、利点はありませんが、これはそうではありません。

'data-whatever'また、文字列定数を作成したとしても、 だけではなく完全な文字列を持つことには小さな利点があり'whatever'ます。その理由は、誰かが HTML コードを読んだときに、その中に文字列が表示され、JS コードでその文字列を検索する可能性があるためです。しかし、プレフィックスが JS コードに自動的に追加されてdata-whateverいる場合、検索しても見つかりません。data-

あ、最後に一つ忘れてました。このコード:

function Form(){

    var args = Array.prototype.slice.call(arguments),
        formName = args[0],
        callback = args.pop(),

頑張りすぎ!代わりにこれを行うだけです:

function Form( formName, callback ) {

(そしてvarもちろん、残りの変数宣言のために保持します)

于 2013-05-10T17:31:40.243 に答える
3

まだコメントを追加できないので、ここにちょっとしたヒントがあります。getFormElements() を小さなプライベート関数に分割します。そして、フォーム関数に errorMsgs を追加します。

しかし、JavaScript の最初のスクリプトとしては、非常に印象的です。これが実際に私が答える本当の理由です。もっと多くの賛成票を投じる価値があると思います.JS忍者がこの質問に答えることに非常に興味があります.

幸運を!

于 2013-05-10T15:39:21.250 に答える