13

私はダグラス・クロックフォードの「Javascript:The Good Parts」を読んでいます。少し極端ですが、彼の言うことをたくさん読んでいます。

第3章では、オブジェクトについて説明し、ある時点で、組み込みの「new」キーワードの使用に伴う混乱/問題の一部を単純化および回避するためのパターン(ここにもあります)をレイアウトします。

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
newObject = Object.create(oldObject);

そのため、作業中のプロジェクトでこれを使用しようとしましたが、ネストされたオブジェクトから継承しようとすると問題が発生することに気付きました。このパターンを使用して継承されたネストされたオブジェクトの値を上書きすると、プロトタイプチェーンのずっと上にあるネストされた要素が上書きされます。

Crockfordの例はflatObj、次の例のように機能します。ただし、動作はネストされたオブジェクトと一致しません。

var flatObj = {
    firstname: "John",
    lastname: "Doe",
    age: 23
}
var person1 = Object.create(flatObj);

var nestObj = {
    sex: "female",
    info: {
        firstname: "Jane",
        lastname: "Dough",
        age: 32  
    }
}
var person2 = Object.create(nestObj);

var nestObj2 = {
    sex: "male",
    info: {
        firstname: "Arnold",
        lastname: "Schwarzenneger",
        age: 61  
    }
}
var person3 = {
    sex: "male"
}
person3.info = Object.create(nestObj2.info);

// now change the objects:
person1.age = 69;
person2.info.age = 96;
person3.info.age = 0;

// prototypes should not have changed:
flatObj.age // 23
nestObj.info.age // 96 ???
nestObj2.info.age // 61

// now delete properties:
delete person1.age;
delete person2.info.age;
delete person3.info.age;

// prototypes should not have changed:
flatObj.age // 23
nestObj.info.age // undefined ???
nestObj2.info.age // 61

フィドルでも)

私は何か間違ったことをしていますか、それともこれはこのパターンの制限ですか?

4

3 に答える 3

11

矛盾はありません。ネストされたオブジェクトについては考えないでください。オブジェクトの直接プロパティは、常にそのプロトタイプまたは独自のプロパティのいずれかにあります。プロパティ値がプリミティブであるかオブジェクトであるかは関係ありません。

だから、あなたがするとき

var parent = {
    x: {a:0}
};
var child = Object.create(parent);

child.x同じオブジェクトを参照しますparent.x-その1つの{a:0}オブジェクト。そして、あなたがそれのプロパティを変更するとき:

var prop_val = child.x; // == parent.x
prop_val.a = 1;

両方が影響を受けます。「ネストされた」プロパティを個別に変更するには、最初に独立したオブジェクトを作成する必要があります。

child.x = {a:0};
child.x.a = 1;
parent.x.a; // still 0

あなたにできることは

child.x = Object.create(parent.x);
child.x.a = 1;
delete child.x.a; // (child.x).a == 0, because child.x inherits from parent.x
delete child.x; // (child).x.a == 0, because child inherits from parent

つまり、それらは完全に独立しているわけではありませんが、それでも2つの異なるオブジェクトです。

于 2012-04-12T20:25:22.627 に答える
1

何が起こっているのかと思うと、を作成するとperson2、そのプロパティはのプロパティを参照します。を参照すると、プロパティが再定義されないため、プロトタイプに渡され、そこでオブジェクトが変更されます。sexinfonestObjperson2.infoperson2info

それを行う「正しい」方法は、ビルドする方法のように見えますperson3。そのため、オブジェクトには変更する独自のinfoオブジェクトがあり、プロトタイプには到達しません。

私も(ゆっくりと)本を読んでいるので、お見舞い申し上げます。:)

于 2012-04-12T20:27:40.140 に答える
1

ここで何が起こっているかをよりよく示すために、例を変更しました。Demo

まず、3つのプロパティを持つオブジェクトを作成します。数値、文字列、および文字列値を持つ1つのプロパティを持つオブジェクト。

次に、Object.create();を使用して最初のオブジェクトから2番目のオブジェクトを作成します。

var obj1 = { 
    num : 1,
    str : 'foo',
    obj : { less: 'more' }
};
var obj2 = Object.create( obj1 );

console.log( '[1] obj1:', obj1 );
console.log( '[1] obj2:', obj2 );
"[1] obj1:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "foo"
}
"[1] obj2:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "foo"
}

よさそうですか?最初のオブジェクトと2番目のコピーされたオブジェクトがあります。

そんなに早くない; 最初のオブジェクトの値のいくつかを変更するとどうなるか見てみましょう。

obj1.num = 3;
obj1.str = 'bar';
obj1.obj.less = 'less';

console.log( '[2] obj1:', obj1 );
console.log( '[2] obj2:', obj2 );
"[2] obj1:"
[object Object] {
  num: 3,
  obj: [object Object] {
    less: "less"
  },
  str: "bar"
}
"[2] obj2:"
[object Object] {
  num: 3,
  obj: [object Object] {
    less: "less"
  },
  str: "bar"
}

ここでも、変更を加えた最初のオブジェクトと、そのオブジェクトのコピーがあります。ここで何が起こっているのですか?

オブジェクトに独自のプロパティがあるかどうかを確認しましょう。

for( var prop in obj1 ) console.log( '[3] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[3] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[3] obj1.hasOwnProperty( num ): true"
"[3] obj1.hasOwnProperty( str ): true"
"[3] obj1.hasOwnProperty( obj ): true"
"[3] obj2.hasOwnProperty( num ): false"
"[3] obj2.hasOwnProperty( str ): false"
"[3] obj2.hasOwnProperty( obj ): false"

obj1定義したように、すべての独自のプロパティがありますが、そうでobj2はありません。

obj2のプロパティの一部を変更するとどうなりますか?

obj2.num = 1;
obj2.str = 'baz';
obj2.obj.less = 'more';

console.log( '[4] obj1:', obj1 );
console.log( '[4] obj2:', obj2 );
for( var prop in obj1 ) console.log( '[4] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[4] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[4] obj1:"
[object Object] {
  num: 3,
  obj: [object Object] {
    less: "more"
  },
  str: "bar"
}
"[4] obj2:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "baz"
}
"[4] obj1.hasOwnProperty( num ): true"
"[4] obj1.hasOwnProperty( str ): true"
"[4] obj1.hasOwnProperty( obj ): true"
"[4] obj2.hasOwnProperty( num ): true"
"[4] obj2.hasOwnProperty( str ): true"
"[4] obj2.hasOwnProperty( obj ): false"

だから、私たちが望んでいたようにではなく、オンにnum変更strしましたが、必要のないときに変更しました。obj2obj1obj1.obj.less

チェックから、hasOwnProperty()変更したにもかかわらず、最初obj2.obj.lessに設定しなかったことがわかります。obj2.objこれは、まだ参照していることを意味しobj1.obj.lessます。

からオブジェクトを作成しobj1.objて割り当て、それobj2.objが私たちが探しているものを提供するかどうかを確認しましょう。

obj2.obj = Object.create( obj1.obj );

console.log( '[5] obj1:', obj1 );
console.log( '[5] obj2:', obj2 );
for( var prop in obj1 ) console.log( '[5] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[5] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[5] obj1:"
[object Object] {
  num: 3,
  obj: [object Object] {
    less: "more"
  },
  str: "bar"
}
"[5] obj2:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "baz"
}
"[5] obj1.hasOwnProperty( num ): true"
"[5] obj1.hasOwnProperty( str ): true"
"[5] obj1.hasOwnProperty( obj ): true"
"[5] obj2.hasOwnProperty( num ): true"
"[5] obj2.hasOwnProperty( str ): true"
"[5] obj2.hasOwnProperty( obj ): true"

それは良いことですが、今でobj2は独自のobj特性があります。今変わるとどうなるか見てみましょうobj2.obj.less

obj2.obj.less = 'less';

console.log( '[6] obj1:', obj1 );
console.log( '[6] obj2:', obj2 );
"[6] obj1:"
[object Object] {
  num: 3,
  obj: [object Object] {
    less: "more"
  },
  str: "bar"
}
"[6] obj2:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "less"
  },
  str: "baz"
}

つまり、作成されたオブジェクトのプロパティがまだ変更されていない場合、そのプロパティの作成されたgetオブジェクトへのリクエストはすべて元のオブジェクトに転送されます。

前のコードブロックからのリクエストsetには、最初にのリクエストが必要ですが、その時点では存在しないため、に転送され、次にに転送されます。obj2.obj.less = 'more'getobj2.objobj2obj1.objobj1.obj.less

obj2最後にもう一度読んだとき、リクエストが転送されて以前に変更した設定が返されるobj2.objように設定されていないため、2番目のオブジェクトオブジェクトの子のプロパティを変更すると両方が変更されるように見えますが、実際には実際には最初の変更のみです。getobj1.obj


この関数を使用して、元のオブジェクトから完全に分離された新しいオブジェクトを再帰的に返すことができます。

Demo

var obj1 = { 
    num : 1,
    str : 'foo',
    obj : { less: 'more' }
};
var obj2 = separateObject( obj1 );

function separateObject( obj1 ) {

    var obj2 = Object.create( Object.getPrototypeOf( obj1 ) );
    for(var prop in obj1) {
        if( typeof obj1[prop] === "object" )
            obj2[prop] = separateObject( obj1[prop] );
        else
            obj2[prop] = obj1[prop];
    }

    return obj2;
}

console.log( '[1] obj1:', obj1 );
console.log( '[1] obj2:', obj2 );
for( var prop in obj1 ) console.log( '[1] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[1] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[1] obj1:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "foo"
}
"[1] obj2:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "foo"
}
"[1] obj1.hasOwnProperty( num ): true"
"[1] obj1.hasOwnProperty( str ): true"
"[1] obj1.hasOwnProperty( obj ): true"
"[1] obj2.hasOwnProperty( num ): true"
"[1] obj2.hasOwnProperty( str ): true"
"[1] obj2.hasOwnProperty( obj ): true"

ここでいくつかの変数を変更するとどうなるか見てみましょう。

obj1.num = 3;
obj1.str = 'bar';
obj1.obj.less = 'less';

console.log( '[2] obj1:', obj1 );
console.log( '[2] obj2:', obj2 );
"[2] obj1:"
[object Object] {
  num: 3,
  obj: [object Object] {
    less: "less"
  },
  str: "bar"
}
"[2] obj2:"
[object Object] {
  num: 1,
  obj: [object Object] {
    less: "more"
  },
  str: "foo"
}

すべてが期待どおりに機能します。

于 2015-08-24T01:08:33.413 に答える