JavaScriptのコンテキスト

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 Objectnew 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属性を参照するのではなく、コンストラクターを直接使うらしく、ObjectArrayを上書きしても、{}[]の評価結果には影響しない。

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."

ObjectArrayprototype属性は変更することはできないが、prototypetoStringなどの属性は変更することができる。

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>

紹介 kiidax
元、環境非依存な職業プログラマー。Blenderで遊んでいます。

コメントを残す