60

初心者の質問でごめんなさい。説明していただけますか、違いは何ですか:

1. var a = [];
   a['b'] = 1;

2. var a = {};
   a['b'] = 1;

インターネットで記事が見つからなかったので、ここに書きました。

4

5 に答える 5

204

リテラル

[]およびは、{}それぞれ配列リテラルおよびオブジェクトリテラルと呼ばれます。

var x = []の略ですvar x = new Array();

var y = {}略ですvar y = new Object();

配列

配列は、長さプロパティを持つ構造体です。数値インデックスを介して値にアクセスできます。

var x = [] or var x = new Array();
x[0] = 'b';
x[1] = 'c';

そして、あなたがするすべてのプロパティをリストしたい場合:

for(var i = 0; i < x.length; i++)
console.log(x[i]);// numeric index based access.

パフォーマンスの秘訣と落とし穴

1.長さプロパティの内部キャッシュ

標準の配列反復:

for (var i = 0; i < arr.length; i++) {
    // do stuff
};

あまり知られていない事実:上記のシナリオでは、arr.lengthプロパティはforループのすべてのステップで読み取られます。そこで呼び出す他の関数と同じように:

for (var i = 0; i < getStopIndex(); i++) {
     // do stuff
};

これにより、理由もなくパフォーマンスが低下します。救助のための内部キャッシング:

for (var i = 0, len = arr.length; i < len; i++) {
     // enter code here
};

これが上記の証拠です。

2.コンストラクターで配列の長さを指定しないでください。

// doing this:
var a = new Array(100);
// is very pointless in JS. It will result in an array with 100 undefined values.

// not even this:
var a = new Array();
// is the best way.

var a = [];
// using the array literal is the fastest and easiest way to do things.

配列定義のテストケースは、こちらから入手できます。

3. Array.prototype.push(arr.push)の使用は避けてください

大規模なコレクションを処理している場合は、Array.prototype.push();メソッドを使用するよりも直接割り当ての方が高速です。

myArray[i] = 0;jsPerf.comのテストケースmyArray.push(0);によると、はよりも高速です。

4.連想割り当てに配列を使用するのは誤りです。

それが機能する唯一の理由は、JS言語のコア内でクラスをArray拡張するためです。たとえば、またはオブジェクトをObject使用することもできます。違いはありません。 常にオブジェクトとともに使用する必要があります。Date();RegEx();x['property'] = someValue

配列には数値インデックスのみを含める必要があります。これを参照してください、Google JS開発ガイドラインfor (x in arr)ループやを避けてくださいarr['key'] = 5;

これは簡単にバックアップできます。例については、こちらをご覧ください。

var x = [];
console.log(x.prototype.toString.call);

 出力します:[object Array]

これにより、コア言語の「クラス」継承パターンが明らかになります。  

var x = new String();
console.log(x.prototype.toString.call);

を出力します[object String]

5.配列から最小値と最大値を取得します。

あまり知られていませんが、本当に強力なトリックです。

function arrayMax(arr) {
    return Math.max.apply(Math, arr);
};

、 それぞれ:

function arrayMin(arr) {
    return Math.min.apply(Math, arr);
};

オブジェクト

オブジェクトを使用すると、次のことしかできません。

var y = {}またvar y = new Object();

y['first'] = 'firstValue'と同じy.first = 'firstValue'ですが、配列では実行できません。オブジェクトは、Stringキーとの関連付けアクセス用に設計されています。

そして、反復は次のようなものです。

for (var property in y) {
    if (y.hasOwnProperty(property)) {
        console.log(y.property);
    };
};

パフォーマンスの秘訣と落とし穴

1.オブジェクトにプロパティがあるかどうかを確認します。

ほとんどの人が使用しますObject.prototype.hasOwnProperty。残念ながら、それはしばしば誤った結果をもたらし、予期しないバグにつながります。

これを行うための良い方法は次のとおりです。

function containsKey(obj, key) {
    return typeof obj[key] !== 'undefined';
};

2.switchステートメントを置き換えます。

シンプルだが効率的なJSのトリックの1つは、switch置換です。

switch (someVar) {
    case 'a':
        doSomething();
        break;
    case 'b':
        doSomethingElse();
        break;
    default:
        doMagic();
        break;
};

ほとんどのJSエンジンでは、上記は非常に遅いです。考えられる3つの結果を見ると、違いはありませんが、数十または数百の場合はどうでしょうか。

上記は簡単にオブジェクトに置き換えることができます。末尾を追加しないでください()。これは関数を実行するのではなく、単に関数への参照を格納するだけです。

var cases = {
    'a': doSomething,
    'b': doSomethingElse,
    'c': doMagic
};

の代わりにswitch

var x = ???;
if (containsKey(cases, x)) {
    cases[x]();
} else {
    console.log("I don't know what to do!");
};

3.ディープクローニングが容易になりました。

function cloneObject(obj) {
   var tmp = {};
   for (var key in obj) {
       tmp[key] = fastDeepClone(obj[key];
   };
   return tmp;
}

function cloneArr(arr) {
   var tmp = [];
   for (var i = 0, len = arr.length; i < len; i++) {
     tmp[i] = fastDeepClone(arr[i]);
   }
   return tmp;
}


function deepClone(obj) {
   return JSON.parse(JSON.stringify(obj));
};

function isArray(obj) {
   return obj instanceof Array;
}

function isObject(obj) {
  var type = typeof obj;
  return type === 'object' && obj !== null || type === 'function';
}

function fastDeepClone(obj) {
  if (isArray(obj)) {
    return cloneArr(obj);
  } else if (isObject(obj)) {
    return cloneObject(obj);
  } else {
    return obj;
  };
};

ここに、動作中のディープクローン機能があります。

オートボクシング

動的に型付けされた言語として、JavaScriptはネイティブオブジェクトタイプに関して制限されています。

  • 物体
  • 配列
  • 番号
  • ブール値
  • 日にち
  • 正規表現
  • エラー

Nullは型ではなく、typeof nullオブジェクトです。

キャッチは何ですか?プリミティブオブジェクトと非プリミティブオブジェクトには強い違いがあります。

var s = "str";
var s2 = new String("str");

それらは同じことをします、あなたはとですべての文字列メソッドを呼び出すことができsますs2。まだ:

type of s == "string"; // raw data type
type of s2  == "object" // auto-boxed to non-primitive wrapper type
s2.prototype.toString.call == "[object String]";

あなたはJSですべてがであると聞くかもしれませんobject。それは本当に簡単な間違いですが、それは正確には真実ではありません。

実際には、プリミティブとオブジェクトの2つのタイプがあり、を呼び出すs.indexOf("c")と、JSエンジンは自動的にs非プリミティブラッパータイプに変換します。この場合object String、すべてのメソッドはで定義されますString.prototype

これはと呼ばれauto-boxingます。このObject.prototype.valueOf(obj)方法は、キャストをプリミティブから非プリミティブに強制する方法です。これは、Javaのような言語が独自のプリミティブの多く、具体的にはペアint(-Integer、double-Double、float-Floatなど)に導入するのと同じ動作です。

なぜあなたは気にする必要がありますか?

単純:

function isString(obj) {
   return typeof obj === "string";
}
isString(s); // true
isString(s2); // false

したがって、s2作成されたvar s2 = new String("test")場合は、他の方法では単純なタイプチェックであっても、フォールスネガティブが発生します。より複雑なオブジェクトは、パフォーマンスを大幅に低下させます。

一部の人が言うようにマイクロ最適化ですが、文字列の初期化などの非常に単純なものでも、結果は本当に注目に値します。パフォーマンスの観点から、次の2つを比較してみましょう。

var s1 = "this_is_a_test" 

var s2 = new String("this_is_a_test")

おそらく全体的に一致するパフォーマンスが期待されますが、驚くべきことに、ここでnew String証明されているように、使用する後者のステートメントは最初のステートメントよりも92%遅くなります。

関数

1.デフォルトのパラメータ

演算子は、デフォルト設定の||最も簡単な方法です。なぜそれが機能するのですか?真実と偽りの価値のために。

論理条件で評価されるundefinedと、null値はに自動キャストされfalseます。

簡単な例(ここにコード):

function test(x) {
   var param = x || 5;
   // do stuff with x
};

2. OO JS

理解しておくべき最も重要なことは、JavaScriptthisオブジェクトは不変ではないということです。これは、非常に簡単に変更できる単なる参照です。

newオブジェクト指向JSでは、JSクラスのすべてのメンバーで暗黙のスコープを保証するために、キーワードに依存しています。それでも、とを介してスコープを簡単に変更できFunction.prototype.callますFunction.prototype.apply

もう1つの非常に重要なことはObject.prototypeです。オブジェクトのプロトタイプにネストされた非プリミティブ値は共有されますが、プリミティブ値は共有されません。

ここに例を含むコード。

簡単なクラス定義:

function Size(width, height) {
    this.width = width;
    this.height = height;
};

2つのメンバーを持つ単純なサイズのクラス、this.widthおよびthis.height

クラス定義ではthis、その前にあるものはすべて、Sizeのすべてのインスタンスに対して新しい参照を作成します。

クラスへのメソッドの追加と、「クロージャー」パターンやその他の「ファンシーネームパターン」が純粋なフィクションである理由

これはおそらく、最も悪意のあるJavaScriptアンチパターンが見つかる場所です。

Size2つの方法でクラスにメソッドを追加できます。

Size.prototype.area = function() {
   return this.width * this.height;
};

または:

function Size2(width, height) {
   this.width = width;
   this.height = height;
   this.area = function() {
      return this.width * this.height;
   }
}

var s = new Size(5, 10);
var s2 = new Size2(5, 10);



var s3 = new Size2(5, 10);
var s4 = new Size(5, 10);
 // Looks identical, but lets use the reference equality operator to test things:
s2.area === s3.area // false
s.area === s4.area // true

areaメソッドはSize2、インスタンスごとに作成されます。これは完全に役に立たず、遅く、かなり遅くなります。正確には89%。こちらをご覧ください。

上記のステートメントは、すべての既知の「ファンシーネームパターン」の約99%に有効です。JSで最も重要なことを覚えておいてください。これらはすべて、フィクションにすぎません。

主にデータのカプセル化とクロージャの使用を中心に展開できる、強力なアーキテクチャ上の議論があります。

残念ながら、JavaScriptではそのようなことは絶対に価値がなく、パフォーマンスの低下は単に価値がありません。私たちは90%以上について話している、それは無視できるものではない。

3.制限

定義はクラスのすべてのインスタンス間で共有されるためprototype、非プリミティブ設定オブジェクトをそこに配置することはできません。

Size.prototype.settings = {};

なんで?size.settingsすべてのインスタンスで同じになります。では、プリミティブとは何ですか?

Size.prototype.x = 5; // won't be shared, because it's a primitive.
// see auto-boxing above for primitive vs non-primitive
// if you come from the Java world, it's the same as int and Integer.

ポイント:

平均的なJSの人は、次のようにJSを記述します。

var x = {
    doStuff: function(x) {
    },
    doMoreStuff: 5,
    someConstant: 10
}

それがオブジェクトであると理解している限り、これは問題ありません(問題=品質が低く、コードを維持するのが難しい)。これらの関数は、内部Singletonを参照せずにグローバルスコープでのみ使用する必要があります。this

しかし、それは絶対にひどいコードになります:

var x = {
   width: 10,
   height: 5
}
var y = {
   width: 15,
   height: 10
}

あなたは逃げることができたでしょう:var x = new Size(10, 5); var y = new Size(15, 5);

入力に時間がかかるため、毎回同じものを入力する必要があります。そして繰り返しますが、それはかなり遅いです。こちらをご覧ください。

全体的に貧弱な基準

これはほとんどどこでも見ることができます:

   function() {
      // some computation
      var x = 10 / 2;
      var y = 5;
      return {
         width: x,
         height: y
      }
    }

再び代替案で:

function() {
    var x = 10 / 2;
    var y = 5;
    return new Size(10, 5);
};

ポイント:適切な場所でクラスを使用してください!!

なんで?例1は93%遅いです。こちらをご覧ください。ここでの例は些細なものですが、JS、OOで無視されているものを示しています。

JSにクラスがないと思う人を雇わず、「オブジェクト指向」JSについて話しているリクルーターから仕事を得るのは大体の目安です。

クロージャ

多くの人は、データのカプセル化の感覚を与えるため、上記よりもそれらを好みます。パフォーマンスが大幅に90%低下することに加えて、これも同様に見落としがちなことです。メモリリーク。

function Thing(someParam) {
   this.someFn = function() {
     return someParam;
   }
}

のクロージャを作成しましたsomeParam。なぜこれが悪いのですか?まず、クラスメソッドをインスタンスプロパティとして定義する必要があるため、パフォーマンスが大幅に低下します。

第二に、クロージャが逆参照されることは決してないため、メモリを消費します。証拠はこちらをご覧ください。確かに、偽のデータカプセル化は行われますが、メモリを3倍使用すると、パフォーマンスが90%低下します。

または@private、アンダースコア関数名を追加して方法を取得することもできます。

クロージャを生成する他の非常に一般的な方法:

function bindSomething(param) {
   someDomElement.addEventListener("click", function() {
     if (param) //do something
     else // do something else
   }, false);
}

param閉鎖になりました!どうやってそれを取り除くのですか?さまざまなトリックがあり、そのうちのいくつかはここにあります。より厳密ではありますが、最善のアプローチは無名関数を一緒に使用しないことですが、これにはイベントコールバックのスコープを指定する方法が必要になります。

私の知る限り、このようなメカニズムはGoogleClosureでのみ利用できます。

シングルトンパターン

さて、私はシングルトンのために何をしますか?ランダムな参照を保存したくありません。これがGoogleClosureのbase.jsから恥知らずに盗まれた素晴らしいアイデアです

/**
 * Adds a {@code getInstance} static method that always return the same instance
 * object.
 * @param {!Function} ctor The constructor for the class to add the static
 *     method to.
 */
function addSingletonGetter(ctor) {
  ctor.getInstance = function() {
    if (ctor.instance_) {
      return ctor.instance_;
    }
    return ctor.instance_ = new ctor;
  };
};

これはJava風ですが、シンプルで強力なトリックです。これで、次のことができます。

project.some.namespace.StateManager = function() {
   this.x_ = 5;
};
project.some.namespace.prototype.getX = function() { return x; }
addSingletonGetter(project.some.namespace.StateManager);

これはどのように役立ちますか?単純。他のすべてのファイルでは、参照する必要があるたびにproject.some.namespace.StateManager、次のように記述できます project.some.namespace.StateManager.getInstance()。これは見た目よりも素晴らしいです。

クラス定義(継承、ステートフルメンバーなど)の利点を備え、グローバル名前空間を汚染することなく、グローバル状態を設定できます。

シングルインスタンスパターン

あなたは今これをしたくなるかもしれません:

function Thing() {
   this.someMethod = function() {..}
}
// and then use it like this:
Thing.someMethod();

これは、JavaScriptのもう1つの大きなノーノーです。キーワードが使用されthisた場合にのみ、オブジェクトが不変であることが保証されることを忘れないでください。new上記のコードの背後にある魔法は興味深いものです。thisは実際にはグローバルスコープであるため、意味を持たずにグローバルオブジェクトにメソッドを追加しています。そして、あなたはそれを推測しました、それらのものは決してガベージコレクションされません。

JavaScriptに他のものを使用するように指示するものは何もありません。Afunction自体にはスコープがありません。staticプロパティの操作には十分注意してください。私がかつて読んだ引用を再現するために、JavaScriptグローバルオブジェクトは公衆トイレのようなものです。そこに行くしかない場合もありますが、表面との接触をできるだけ最小限に抑えるようにしてください。

上記のパターンに固執するかSingleton、名前空間の下にネストされた設定オブジェクトを使用してください。

JavaScriptでのガベージコレクション

JavaScriptはガベージコレクションされた言語ですが、JavaScriptGCはよく理解されていません。ポイントは再びスピードです。これはおそらくあまりにも馴染み深いものです。

// This is the top of a JavaScript file.
var a = 5;
var b = 20;
var x = {};//blabla

// more code
function someFn() {..}

それは悪い、パフォーマンスの悪いコードです。理由は簡単です。JSは変数をガベージコレクションし、その変数のスコープが解除された場合にのみ、保持しているヒープメモリを解放します。たとえば、メモリ内のどこにも変数への参照がありません。

例えば:

function test(someArgs) {
   var someMoreStuf = // a very big complex object;
}
test();

三つのこと:

  • 関数の引数はローカル定義に変換されます
  • 内部宣言は引き上げられます。
  • 関数の実行が終了すると、内部変数に割り当てられたすべてのヒープメモリが解放されます。

なんで?それらは「現在の」スコープに属していないためです。それらは作成され、使用され、破壊されます。クロージャもありませんので、ガベージコレクションによって使用したすべてのメモリが解放されます。

そのため、グローバルスコープはメモリを汚染し続けるため、JSファイルがこのように表示されないようにする必要があります。

var x = 5;
var y = {..}; //etc;

さて、今何?

名前空間

JSには名前空間がないため、これはJavaに相当するものではありませんが、コードベース管理の観点からは、必要なものを取得できます。

var myProject = {};
myProject.settings = {};
myProject.controllers = {};
myProject.controlls.MainController = function() {
    // some class definition here
}

美しい。1つのグローバル変数。適切なプロジェクト構造。ビルドフェーズでは、プロジェクトをファイル間で分割し、適切な開発環境を取得できます。

ここから達成できることに制限はありません。

ライブラリを数える

数え切れないほどのコードベースで作業することができたので、最後の最も重要な議論は、コードの依存関係に非常に注意することです。プログラマーが、単純なアニメーション効果などのためにスタックのミックスにjQueryを何気なく追加しているのを見てきました。

依存関係とパッケージ管理は、Bowerのようなツールが作成されるまで、JavaScriptの世界で最も長い間対処されていなかったものです。ブラウザはまだやや遅いです、そしてそれらが速いときでさえ、インターネット接続は遅いです。

たとえば、Googleの世界では、バイトを節約するためだけにコンパイラ全体を作成するという長さを経ており、そのアプローチは多くの点でWebプログラミングに適した考え方です。そして、私はGoogleを非常に高く評価しています。彼らのJSライブラリは、めちゃくちゃ複雑であるだけでなく、どこでも機能するGoogleマップのようなアプリを強化しているからです。

おそらくJavaScriptには、その人気、アクセス可能性、およびエコシステム全体が受け入れようとしている非常に低品質のバーを考えると、非常に多様なツールが利用可能です。

Hacker Newsの購読者にとって、新しいJSライブラリがなければ1日は過ぎません。確かに便利ですが、多くの人がまったく同じ懸念を再実装するという事実を無視することはできません。キラーアイデアと改善。

エコシステム全体に斬新さと有用性を証明する前に、すべての新しいおもちゃを混ぜる衝動に抵抗し、日曜日のコーディングの楽しさと本番環境の展開を強く区別することは、経験則です。

タグがこの投稿よりも長い場合<head></head>は、すべて間違っています。

JavaScriptの知識をテストする

いくつかの「完璧主義」レベルのテスト:

于 2012-10-19T12:02:49.297 に答える
8

オブジェクトのコレクション?この表記を使用します(JavaScript配列):

var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ];

コレクションに新しい要素を追加するには:

collection.push( {name:"object 4"} );
于 2012-10-19T11:56:51.870 に答える
1

JavaScriptでは、すべてのオブジェクトは連想配列です。最初のケースでは配列を作成し、2番目のケースでは配列でもある空のオブジェクトを作成しました:)。

したがって、JSでは、配列と同様に任意のオブジェクトを操作できます。

var a = {};
a["temp"] = "test";

そしてオブジェクトとして:

var a = {};
a.temp = "test";
于 2012-10-19T11:57:11.673 に答える
1

オブジェクトの配列を使用します。

collection = [ 
    { "key":"first key", "value":"first value" }, 
    { "key":"second key", "value":"second value" } 
];

于 2012-10-19T11:59:14.657 に答える
1

1)配列です2)オブジェクトです

配列を使用すると、他の言語と同様にすべてが通常です

オブジェクトもあります。-値ab==1を取得できます-しかし、JSでは、このような構文a ["b"]==1を使用して値を取得することもできます。

  • これは、キーがこの「何らかのキー」のように見える場合に役立つ可能性があります。この場合、「チェーン」は使用できません。
  • キーが変数の場合もこれは便利です

あなたはこのように書くことができます

    function some(f){
var Object = {name: "Boo", age: "foo"}, key;
if(f == true){
   key = "name";
}else{
   key = "age";
}
 return Object[key];
}

しかし、私はそれをコレクションとして使用したいのですが、それを選択する必要がありますか?

これは、保存するデータによって異なります

于 2012-10-19T12:03:36.437 に答える