JavaScriptのコンテキスト
2014年11月15日 コメントを残す
JavaScriptのプロトタイプに関して理解を整理するためにまとめてみた。
すべてのJavaScriptのオブジェクトは内部的な属性として[[Prototype]]属性をもっている。[[Prototype]]属性はnew
演算子によってオブジェクトが作成されたときにコンストラクターとして使われた関数のproperty
属性の値が設定される。
function MyObject() {} var x = new MyObject; MyObject.prototype.foo = "This is a MyObject."; console.log("" + x.prototype); // -> "undefined" console.log("" + x.foo); // -> "This is a MyObject." MyObject.prototype = {}; MyObject.prototype.foo = "This is a MyObject. (2)"; var y = new MyObject; console.log("" + y.foo); // -> "This is a MyObject. (2)" console.log("" + x.foo); // -> "This is a MyObject."
[[Prototype]]属性とprototype
属性は違うもので、スクリプトからは[[Prototype]]属性を直接参照することはできない。JavaScriptのObject
オブジェクトやArray
オブジェクトのコンストラクターもprototype
属性を持ち、その値が、オブジェクトの[[Prototype]]属性として設定される。
var obj = new Object; var arr = new Array; Object.prototype.foo = "This is a Object."; Array.prototype.foo = "This is an Array."; console.log("" + obj.foo); // -> "This is a Object." console.log("" + arr.foo); // -> "This is an Array."
{}
や[]
などのオブジェクトリテラルや配列リテラルは、それぞれnew Object
、new Array
を実行したのと同様に[[Prototype]]属性が設定される。
var obj = {}; var arr = []; Object.prototype.foo = "This is a Object."; Array.prototype.foo = "This is an Array."; console.log("" + obj.foo); // -> "This is a Object." console.log("" + arr.foo); // -> "This is an Array."
ECMA-262の仕様を見る限りはそうだとは読み取れなかったが、{}
や[]
はグローバルオブジェクトのObject
属性やArray
属性を参照するのではなく、コンストラクターを直接使うらしく、Object
やArray
を上書きしても、{}
や[]
の評価結果には影響しない。
Object.prototype.foo = "This is a Object."; var x = {}; Object = function MyObject() {}; Object.prototype.foo = "This is a new Object."; var y = {}; var z = new Object(); console.log("" + x.foo); // -> "This is a Object." console.log("" + y.foo); // -> "This is a Object." console.log("" + z.foo); // -> "This is a new Object."
Object
やArray
のprototype
属性は変更することはできないが、prototype
のtoString
などの属性は変更することができる。
Object.prototype.foo = "This is a Object."; Object.prototype = { foo: "This is not a Object." }; console.log("" + Object.prototype.foo); // -> "This is a Object." Object.prototype.toString = function toString() { return "This is a Object."; }; var x = {}; console.log(x.toString()); // -> "This is a Object."
ブラウザのJavaScriptの実装ではページごとにグローバルオブジェクトが作られるため、オブジェクトリテラルや配列リテラルを持つ関数のスコープによって、別の値が[[Prototype]]属性として設定される。
test6.html
<!DOCTYPE html> <script> window.onload = function () { var x = []; Array.prototype.foo = "This is an Array of the parent."; var child = document.getElementById("child").contentWindow; var y = child.test(); console.log(x.foo); // -> "This is an Array of the parent." console.log(y.foo); // -> "This is an Array of the child." }; </script> <iframe id="child" src="test6a.html">
test6a.html
<!DOCTYPE html> <script> function test() { var arr = []; Array.prototype.foo = "This is an Array of the child."; return arr; } </script>
Object.keys
のように配列を生成する関数も同様に、その関数の属しているページのものが使われる。
test7a.html
<!DOCTYPE html> <script> window.onload = function () { var x = []; Array.prototype.foo = "This is an Array of the parent."; var child = document.getElementById("child").contentWindow; var keys = Object.keys; var childKeys = child.test(); console.log(keys(x).foo); // -> "This is an Array of the parent." console.log(childKeys(x).foo); // -> "This is an Array of the child." }; </script> <iframe id="child" src="test7a.html">
test7a.html
<!DOCTYPE html> <script> function test() { Array.prototype.foo = "This is an Array of the child."; return Object.keys; } </script>