(五)データタイプのオブジェクトベース

8883 ワード

1概要
1.1生成方法
対象(object)はJavaScript言語の核心概念であり、最も重要なデータタイプである.
相手は何ですか?簡単に言えば、対象は「キーペア」の集合であり、無秩序な複合データの集合である.
var obj = {
  foo: 'Hello',
  bar: 'World'
};
説明:上のコードでは、大かっこは変数objに割り当てられたオブジェクトを定義していますので、変数objはオブジェクトを指します.このオブジェクトの内部には2つのキーペア(2つの「メンバー」とも呼ばれる)が含まれており、最初のキーペアはfoo:'Hello'であり、fooは「キー名」であり、文字列ハローは「キー値」である.キーの名前とキーの値はコロンで区切られます.二番目のキーはbar:'World'で、barはキーで、Worldはキーの値です.二つのキーの間をコンマで区切ってください.
1.2キー名
オブジェクトのキー名はすべて文字列ですので、引用符を付けなくてもいいです.上のコードも下記のように書くことができます.
var obj = {
  'foo': 'Hello',
  'bar': 'World'
};
キーの名前が数値の場合、自動的に文字列に変換されます.
var obj = {
  1: 'a',
  3.2: 'b',
  1e2: true,
  1e-2: true,
  .234: true,
  0xFF: true
};

obj
// Object {
//   1: "a",
//   3.2: "b",
//   100: true,
//   0.01: true,
//   0.234: true,
//   255: true
// }

obj['100'] // true
上のコードの中で、オブジェクトのobjのすべてのキー名は数値のように見えますが、実際には自動的に文字列に変換されます.
キーの名前が識別名の条件(例えば、最初の文字が数字であるか、あるいはスペースや演算子が含まれているか)と一致していない場合は、引用符を付けなければなりません.そうでなければ、エラーが発生します.
//   
var obj = {
  1p: 'Hello World'
};

//    
var obj = {
  '1p': 'Hello World',
  'h w': 'Hello World',
  'p+q': 'Hello World'
};
上のオブジェクトの3つのキーは、いずれもマーク名の条件に合わないので、カギカッコを付けなければなりません.
オブジェクトの各キー名は「プロパティ」とも呼ばれ、その「キー値」は任意のデータタイプであってもよい.属性の値が関数である場合、通常はこの属性を「方法」と呼び、関数のように呼び出すことができます.
var obj = {
  p: function (x) {
    return 2 * x;
  }
};

obj.p(1) // 2
上のコードの中で、オブジェクトのobjの属性pは、関数を指します.
属性の値がオブジェクトである場合は、チェーン参照が形成されます.
var o1 = {};
var o2 = { bar: 'hello' };

o1.foo = o2;
o1.foo.bar // "hello"
上のコードでは、オブジェクトo 1の属性fooがオブジェクトo 2を指すので、チェーン式でo 2の属性を参照することができます.
オブジェクトの属性はカンマで区切られ、最後の属性はコンマで区切られます.
var obj = {
  p: 123,
  m: function () { ... },
}
上のコードの中で、m属性の後ろのカンマがあっても大丈夫です.
属性はオブジェクト宣言時に指定せずに動的に作成できます.
var obj = {};
obj.foo = 123;
obj.foo // 123
上記のコードでは、Objオブジェクトのfoo属性に直接値を付け、結果として実行時にfoo属性を作成します.
1.3オブジェクトの参照
異なる変数名が同じオブジェクトを指す場合、それらはすべてこのオブジェクトの参照であり、つまり同じメモリアドレスを指す.変数の一つを変更すると、他のすべての変数に影響を与えます.
var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2
上のコードでは、o 1とo 2は同じオブジェクトを指していますので、いずれかの変数に属性を追加し、他の変数に属性を読み書きできます.
この場合、元のオブジェクトに対する変数の参照をキャンセルすると、他の変数に影響を与えません.
var o1 = {};
var o2 = o1;

o1 = 1;
o2 // {}
上のコードの中で、o 1とo 2は同じ対象を指し、o 1の値は1になります.o 2には影響がありません.o 2は元の対象を指します.
しかし、この参照は対象に限定され、二つの変数が同じ元のタイプの値を指す場合.では、変数はこの時は値のコピーです.
var x = 1;
var y = x;

x = 2;
y // 1
上のコードの中でxの値が変化するとyの値は変わりません.これはyとxが同じメモリアドレスを指していないことを表します.
1.4表式ですか?それとも文ですか?
このオブジェクトは大かっこで表現されています.これにより、問題があります.行頭が大括弧の場合、それは式ですか?それとも文ですか?
{ foo: 123 }
JavaScriptエンジンは上の行のコードを読むと、二つの意味があるかもしれません.一つ目は、foo属性を含むオブジェクトを表す表現です.第二の可能性は、コードブロックを表す文であり、ラベルfooがあり、表式123を指す.このような曖昧さを避けるために、JavaScriptでは、行頭が大括弧であれば、すべて語句として解釈されます.表式(オブジェクト)として解釈するには、大かっこの前に括弧を入れる必要があります.
({ foo: 123})
この違いはeval文(作用は文字列の値を求める)において最も顕著に反映されている.
eval('{foo: 123}') // 123
eval('({foo: 123})') // {foo: 123}
上のコードの中で、括弧がない場合、evalはコードブロックとして理解します.丸括弧を入れると、一つの対象となります.
2属性の操作
2.1属性の読み込み
オブジェクトの属性を読み取るには、点演算子を使う方法と、角括弧演算子を使う方法があります.
var obj = {
  p: 'Hello World'
};

obj.p // "Hello World"
obj['p'] // "Hello World"
上のコードはそれぞれ点演算子と角括弧演算子を使用して、属性pを読みます.
注意してください.四角い括弧の演算子を使うと、キー名はカギカッコに入れなければなりません.変数として扱われます.
var foo = 'bar';

var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2
上のコードの中で、オブジェクトのobjのfoo属性を参照する場合、点演算子を使用すると、fooは文字列です.括弧演算子を使用する場合、引用符を使用しないと、fooは変数です.文字列barを指します.エラーが発生しないように引用符を使用することをお勧めします.
括弧演算子の内部には、表式も使用できます.
obj['hello' + ' world']
obj[3 + 3]
数字キーは引用符をつけないことができます.文字列に自動的に変換されます.
var obj = {
  0.7: 'Hello World'
};

obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"
上のコードでは、オブジェクトobjの数字キー0.7に引用符を加えなくてもいいです.自動的に文字列に変換されます.
注意してください.数値キーは点演算子を使用することができません.小数点として扱われますので、四角い括弧演算子だけを使用します.
var obj = {
  123: 'hello world'
};

obj.123 //   
obj[123] // "hello world"
上のコードの最初の表式で、数値キー123に点演算子を使って、エラーが発生しました.二番目の表式は四角い括弧演算子を使用します.結果は正しいです.
2.2属性の割り当て
点演算子と角括弧演算子は、値を読み取るだけでなく、値を割り当てるためにも使用できます.
var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';
上のコードでは、点演算子と角括弧演算子をそれぞれ使用して、属性に値を割り当てます.
JavaScriptは属性の「後バインディング」を許可します.つまり、任意の時刻に属性を追加できます.対象を定義する時に、属性を定義する必要はありません.
var obj = { p: 1 };

//    

var obj = {};
obj.p = 1;
2.3すべてのプロパティを表示する
オブジェクト自体の属性をすべて表示します.Object.keysメソッドを使用できます.
//    
var obj = {
  key1: 1,
  key2: 2
};

Object.keys(obj);
// ['key1', 'key2']
2.4 deleteコマンド
deleteコマンドは、オブジェクトの属性を削除し、削除に成功したらtrueに戻ります.
var obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []
上のコードでは、deleteコマンドは、オブジェクトobjのp属性を削除します.削除後、p属性を読み込むとundefinedに戻り、Object.keysメソッドの戻り値もこの属性を含まない.なお、存在しない属性を削除しても、deleteはエラーなくtrueに戻ります.
var obj = {};
delete obj.p // true
上のコードでは、オブジェクトobjにp属性はありませんが、deleteコマンドはそのままtrueに戻ります.したがって、deleteコマンドの結果から、ある属性が存在するとは認められません.
一つだけの場合、deleteコマンドはfalseに戻ります.この属性が存在し、削除できません.
var obj = Object.defineProperty({}, 'p', {
  value: 123,
  configurable: false
});

obj.p // 123
delete obj.p // false
上記のコードの中で、対象のObjectのp属性は削除できませんので、deleteコマンドはfalseに戻ります.なお、deleteコマンドは、オブジェクト自体の属性のみ削除でき、引き継ぎの属性は削除できません.
var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }
上のコードでは、toStringは対象objが継承した属性であり、deleteコマンドはtrueに戻るが、この属性は削除されておらず、依然として存在する.この例では、deleteがtrueに戻っても、この属性は依然として値を読み取ることができると説明している.
2.5 in演算子
in演算子は、オブジェクトに属性が含まれているかどうかを確認するために使用されます.これが含まれている場合はtrueに戻ります.そうでなければfalseに戻ります.
var obj = { p: 1 };
'p' in obj // true
in演算子の問題の一つは、どの属性がオブジェクト自身のものか、どの属性が継承されているかを識別できないことです.
var obj = {};
'toString' in obj // true
上のコードでは、toStringメソッドは、オブジェクトobj自身の属性ではなく、継承された属性です.ただし、in演算子は識別できず、継承属性もtrueに戻ります.
2.6 for…inサイクル
for...inサイクルは、オブジェクトのすべての属性を巡回するために使用されます.
var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log(obj[i]);
}
// 1
// 2
// 3
for...inループを使用して、オブジェクト属性名を抽出する例を示します.
var obj = {
  x: 1,
  y: 2
};
var props = [];
var i = 0;

for (var p in obj) {
  props[i++] = p
}

props // ['x', 'y']
for…inサイクルは使用上の注意点が二つあります.
これは、オブジェクトのすべての巡回可能な属性を巡回して、巡回不能な属性をスキップします.オブジェクト自体の属性だけでなく、継承された属性も巡回します.
例えば、オブジェクトはString属性を継承しているが、for...inサイクルはこの属性を巡回しない.
var obj = {};
// toString       
obj.toString // toString() { [native code] }

for (var p in obj) {
  console.log(p);
} //       
上記のコードにおいて、オブジェクトobjはString属性を継承しています.属性はfor...inループで巡回されません.これはデフォルトでは「巡回不可」です.
継承された属性が遍歴可能であれば、for...inで巡回されます.ただし、一般的には対象自体の属性だけを遍歴したいので、for…inを使う場合は、ハスOwnProperty方法を組み合わせて、ある属性が対象自体の属性かどうかを循環内部で判断します.
var person = { name: '   ' };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name
3 with文
with文の書式は以下の通りです.
with (  ) {
    ;
}
同じオブジェクトの複数の属性を操作する際に、書き込みの便利さを提供する役割があります.
//   
var obj = {
  p1: 1,
  p2: 2,
};
with (obj) {
  p1 = 4;
  p2 = 5;
}
//    
obj.p1 = 4;
obj.p2 = 5;

//   
with (document.links[0]){
  console.log(href);
  console.log(title);
  console.log(style);
}
//    
console.log(document.links[0].href);
console.log(document.links[0].title);
console.log(document.links[0].style);
なお、withブロックの内部に変数の割り当てがある場合は、現在のオブジェクトが既に存在する属性である必要があり、そうでないと現在のスコープのグローバル変数が作成されます.
var obj = {};
with (obj) {
  p1 = 4;
  p2 = 5;
}

obj.p1 // undefined
p1 // 4
上のコードでは、オブジェクトのobjはp 1属性を持っていません.p 1の割当値はグローバル変数p 1を作成したものと同じです.正しい書き方は、オブジェクトobjの属性p 1を先に定義してから、withブロック内で動作することであるべきである.
これは、withブロックが機能領域を変えていないためであり、その内部は依然として現在の作用領域である.これはwith文の大きな弊害をもたらしました.結合対象が不明です.
with (obj) {
  console.log(x);
}
上のコードブロックだけでは、xがグローバル変数なのか、それともオブジェクトobjの属性なのかは全く判断できません.これは非常にコードの誤りとモジュール化に不利です.コンパイラもこのコードを最適化できません.運行時に判断するしかないです.これで運転速度が遅くなります.このため、with文を使わないでください.withの代わりに臨時変数を使うことが考えられます.
with(obj1.obj2.obj3) {
  console.log(p1 + p2);
}

//     
var temp = obj1.obj2.obj3;
console.log(temp.p1 + temp.p2);
この文章は多く参考にしました.阮一峰先生のブログ『JavaScript標準参考教程』を参考にしました.彼の文章は私に多くの利益をもたらしました.ここで心から感謝します.