2

新しい JavaScript ECMA 5 標準に従ってオブジェクトを作成する方法を学び、機能を損なうことなく現在のプロジェクトでそれらを使用したいと考えています。でも説明のつかないものを見ると怖くなる

次のコードを検討してください。

var foo = {
        oldProp: 'ECMA Script 3',
        newProp: 'ECMA Script 5'
    };

// The new way of inheritance
var bar = Object.create( foo );

// and of assigning properties, if I am right
var bar2 = Object.create( Object.prototype, {
        oldProp: { value: 'ECMA Script 3'},
        newProp: { value: 'ECMA Script 5'}
    });

// and then
bar; 
// console output
Object {oldProp: "ECMA Script 3", newProp: "ECMA Script 5"}

// whereas !!
bar2;
Object {} // Why this?

また、

bar.hasOwnProperty('oldProp');  // false, whereas
bar2.hasOwnProperty('oldProp'); // true !! It should be the other way round, right? 

さらに、プロパティを変更することはできませんbar2

bar2.oldProp = "Marry had a little lamb";
bar2.oldProp; // "ECMA Script 3"

もっと -

for (k in bar2) console.log(bar2[k]) // undefined  

オブジェクトを正しい方法で作成していますか? 私が基本的に達成したいのは、コンストラクターを取り除くことです。

古いコード:

var Slideshow = function(type) {
    this.type = type;
    this.deleteSlideshow = function() {
        // some code
    }
}
Slideshow.prototype.nextSlide = function() {
    // lotsa code
}
var vanityFairSlideshow = new Slideshow('wide-screen');

に変更したいのですObject.createが、次のようなものかもしれません:

var Slideshow = Object.create({ /* prototype */ }, { /* properties and methods */ });
var vanityFairSlideshow = Object.create( Slideshow , { /* properties and methods */ });

それを行う適切な方法は何ですか?

4

4 に答える 4

2

私はこの質問に答えようとします:

オブジェクトを正しい方法で作成していますか? 私が基本的に達成したいのは、コンストラクターを取り除くことです。

最初の小さな例。この単純な構造があると想像してください。

var Slideshow = function(type) {
  this.type = type;
}

Slideshow.prototype.nextSlide = function() {
    console.log('next slide');
}

Slideshow.prototype.deleteSlideshow = function() {
    console.log('delete slideshow');
}

var AdvancedSlideshow = function(bgColor) {
    this.bgColor = bgColor;
}

AdvancedSlideshow.prototype.showMeBgColor = function() {
    console.log('bgColor iS: ' + bgColor);
}

AdvancedSlideshowから継承したいと思いSlideshowます。したがって、親のすべての関数をその子のインスタンスで使用できるようにする必要があります。私Object.createたちがそれをしなかった場合:

// THIS CODE ISNT CORRECT
// code for slideshow is same

var AdvancedSlideshow = function(type, bgColor) {
    // this line can supply whole Slideshow constructor
    Slideshow.call( this, type ); 

    this.bgColor = bgColor;
}

AdvancedSlideshow.prototype = Slideshow.prototype; // there problems start!!!!

AdvancedSlideshow.prototype.showMeBgColor = function() { // we did augumentation of Slideshow
    console.log('bgColor is: ' + bgColor);
}

AdvancedSlideshowは からすべてを継承するようになりましSlideshowたが、からSlideshowすべてを継承しAdvancedSlideshowます。それは私たちが望んでいないことです

var simpleSlideshow = new Slideshow('simple');
simpleSlideshow.showMeBgColor(); // ReferenceError: bgColor is not defined
                                 // but function exists and that's wrong!

そのため、より複雑なものを使用する必要があります。Douglas Crockford が昔作ったポリフィルがあります。

if (!Object.create) {
    Object.create = function (o) {
        if (arguments.length > 1) {
            throw new Error('Object.create implementation only accepts the first parameter.');
        }
        function F() {}
        F.prototype = o;
        return new F();
    };
}

最初はポリフィルではなく、小さなパターンで、それ以外のように見えました。しかし、パターンは進化し​​ていて、JavaScript の一部になったのはとても良いことでした。これで、ecmascript-5 をサポートしないブラウザ用のポリフィルができました。そして、次のようにコーディングできます。

// THIS IS CORRECT
AdvancedSlideshow.prototype = Object.create(Slideshow.prototype);

それ以外の

function F() {};
F.prototype = Slideshow.prototype;
AdvancedSlideshow.prototype = new F();

次にAdvancedSlideshowから継承しますSlideshowが、から継承SlideshowしませんAdvancedSlideshow

したがって、の目的はObject.create、コンストラクターや「新しい」キーワードを取り除くことではありません。その目的は、正しいプロトタイプ チェーンを作成することです。

EDIT 20.2.2014: 数日前、そのconstructorプロパティがプロトタイプ チェーンの一部であることに気付きました。したがってObject.create、継承に使用する場合はconstructor、子オブジェクトのプロパティ (コンストラクター自体ではなく、単なるプロパティ) も変更します。したがって、子オブジェクトの新しいインスタンスのコンストラクタ プロパティは、その親を指します。これは simple で修正できますChild.prototype.constructor = Child

...
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

var first = new Child();
first.constructor === Child; // true
first.constructor === Parent; // false
first instanceOf Child; // true
first instanceOf Parent; // true 

現在、Object.create には 2 番目のパラメーターがあり、オブジェクトの「隠された秘密」を伝えることができます。ただし、必要がないため、これを行うことはお勧めしません。いくつかのパターンのようにも機能する高度なものです。

コンストラクター、「new」キーワード、または重要なものを避けたい場合は、おそらくファクトリーパターンを探しているでしょう。ただし、覚えておいてください。たとえば、"new" にはいくつかの弱点がありますが、組み込み機能を削除するパターンは通常より悪いです! とにかく、ここでインスピレーションを見つけることができるかもしれません。

http://www.2ality.com/2010/12/javascripts-prototypal-inheritance.html

于 2013-04-30T13:44:05.330 に答える
1

barはプロパティなしでfooプロトタイプとして作成されるため、bar.hasOwnProperty('oldProp');結果的に false を返します。

bar2記述子を提供しないため、および書き込み可能のデフォルト値はenumerableですfalsefor...inしたがって、プロパティを変更したり、ループで列挙したりすることはできません。

私は正しい方法でオブジェクトを作成していますか? :あなたのしていることは合理的です。すべてのスライドショーのプロトタイプとして作成Slideshowし、このプロトタイプと追加のプロパティを使用してスライドショーを作成します。

于 2013-04-24T08:44:56.107 に答える
1

ここでの問題は、各プロパティで省略した記述子(を除く) のデフォルトです。value

この構文 ( fordefinePropertyや などdefineProperties)を使用すると、デフォルトでwritableが、 がになります。falseenumerablefalse

これらの両方を に設定するtrueと、期待どおりの動作が得られます。

var bar2 = Object.create( Object.prototype, {
    oldProp: { value: 'ECMA Script 3', writable: true, enumerable: true}
});

http://jsfiddle.net/YZmbg/

また、これがすべてのブラウザーでサポートされているわけではないことを理解していただければ幸いです。の互換性表にObject.create注意してください。writableandプロパティに準拠するシムconfigurableは存在しませんし、存在することもできません。

最後に、そのようなよく書かれた質問に対して+1します。それは新鮮な空気の息吹です。

于 2013-04-24T08:35:27.090 に答える
0
bar2 // logs Object {}

bar2 のプロパティを変更できません

for (k in bar2) console.log(bar2[k]) // undefined

これは、- の 2 番目の引数がObject.create似ているためです。これは、プロパティ記述子Object.definePropertiesを使用していることを意味します。のみを設定している間、、およびフラグのデフォルトはです。enumerablewritableconfigurablefalse

それを行う適切な方法は何ですか?

enumberableおよびwritableフラグを明示的に に明示的に設定するかtrue、2 番目のパラメーターを使用しません。

 var bar2 = Object.create( Object.prototype ); // or Slideshow.prototype or whatever
 bar2.oldProp = 'ECMA Script 3';
 bar2.newProp = 'ECMA Script 5';

ただし、これが長すぎると思われる場合は、ファクトリ ユーティリティ関数 (基本的にはコンストラクター) を使用します。

于 2013-04-24T08:39:22.297 に答える