3

Javascript オブジェクトのパブリック変数とプライベート変数に関する質問があります。これは、変数のスコープとプライベート プロパティとパブリック プロパティを理解するために私が試した簡単なコードです。

var fred = new Object01("Fred");
var global = "Spoon!";

function Object01(oName) {
    var myName = oName;
    this.myName = "I'm not telling!";
    var sub = new subObject("underWorld");
    this.sub = new subObject("Sewer!");

    Object01.prototype.revealName = function() {
        return "OK, OK, my name is: " + myName + ", oh and we say " + global;
    }

    Object01.prototype.revealSecretName = function() {
        console.log ("Private: ");
        sub.revealName();
        console.log("Public: ");
        this.sub.revealName();
    }
}

function subObject(oName) {
    var myName = oName;
    this.myName = "My Secret SubName!";

    subObject.prototype.revealName  = function() {
        console.info("My Property Name is: " + this.myName);
        console.info("OK, my real name is: " + myName + ", yeah and we also say: " + global);
    }
}

これまでに観察した面白い点は、オブジェクト内にあり、単純な var はプライベートとして扱われ (関数ブロック内にあるため)、thisバージョンはパブリックです。this.xxxしかし、同じ名前の変数が別の変数と見なされているように見えることに気付きました。したがって、上記の例では、 my オブジェクトは、 my をプルする my 関数と比較して、fredfor とは異なるものを報告します。this.myNamevar myName

しかし、これと同じ動作は、私が作成するサブオブジェクトでは同じではありません。上記のvar subvsthis.subの両方の場合、new subObject呼び出しを使用して、おそらく 2 つのサブオブジェクトを作成します。しかし、両方ともバージョンthis.subvar sub返すようです。Sewer!

Strings for を使用して 2 つの異なる結果が得られたのに、別のオブジェクトで同じことを試みても同様の結果が得られない理由について少し混乱してthis.myNamevar myNameます。私はそれらを間違って使用しているか、 athisvarversion の違いを理解していない可能性があると思います。

4

5 に答える 5

4

プライベートまたはパブリックはなく、変数とオブジェクト プロパティがあります。

変数とオブジェクト プロパティは、変数スコープを持つ変数と変数スコープを持たないオブジェクト プロパティよりも多くの点で異なります。変数のスコープは、プロパティではなく変数であるため、オブジェクトのプライベート プロパティと同じではありません。

変数はどのオブジェクトにも属していませんが、クロージャーを通じて維持できます。これらのクロージャーを任意のオブジェクトのプロパティとして、またはオブジェクトをまったく使用せずに呼び出すことができ、想定されるプライベート プロパティが機能します。

function A() {
    var private = 0;

    this.setPrivate = function( value ) {
        private = value;    
    };

    this.getPrivate = function() {
        return private;
    };
}

var a = new A();

a.getPrivate() //0;

var b = [];

b.fn = a.setPrivate; //The function is fully promiscuous, especially since the data is closed over by it,
                    //so it doesn't matter at all where or how it's invoked.

b.fn(1);

a.getPrivate(); //1

コンストラクターが呼び出されるたびに、プロトタイプ オブジェクトの関数を再定義しています。プロトタイプの要点は、特定の関数オブジェクトを一度だけ作成すればよいということです。関数内のプロトタイプ オブジェクトにメソッドを割り当てているため、その関数が呼び出されるたびに関数が再作成され、特定の状態を参照する新しいクロージャが形成されます。

上で示したように、クロージャはクローズド オーバー変数で状態を保持するため、どのように呼び出されるかは気にしません。したがって、クロージャーをプロパティとしてプロトタイプに割り当てると、割り当てられた最新のクロージャーを参照するすべてのインスタンスがあり、その状態を取得しています。

JS で「クラス」を定義する標準的な方法を使用し、クロージャーと混同しないことをお勧めします。

function A() {
    this._private = 1;
}
//Note, this code is outside any function
//The functions assigned to prototype are therefore only defined once.
A.prototype.getPrivate = function() {
    return this._private;
};

A.prototype.setPrivate = function( value ) {
    this._private = value;
};

var a = new A();

ここで良いチュートリアルを見つけることができます: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model

于 2012-09-10T20:19:45.247 に答える
4

thisここでの最大の問題は、実際には、ベースのオブジェクト プロパティとvar宣言された変数の違いではありません。

あなたの問題は、メイン クラスのインスタンスは言うまでもなく、サブクラスで使用できる保護されたクラス プロパティを提供するラッパーとしてプロトタイプを機能させようとしていることです。

prototypeクラスのメンバーではまったく機能しません"private"(それは、返される構築されたオブジェクトに追加されたプロパティではなく、コンストラクター関数のスコープ内で定義された変数です)。

function Person (personName) {
    var scoped_name = personName;

    this.name = "Imposter " + scoped_name;
}


Person.prototype.greet = function () { console.log("Hi, I'm " + this.name + "!"); };


var bob = new Person("Bob");
bob.greet(); // "Hi, I'm Imposter Bob!"

文字列のポイントは、prototypeオブジェクトのパブリックにアクセス可能なプロパティで動作するメソッドを提供することです (の値を変更しthis.nameたいが、非表示の参照を永久に失う場合などscoped_name)...

...または、同じ種類のオブジェクトのすべてが同じ値にアクセスできるようにする場合。

function Student (name, id) {
    function showIDCard () { return id; }
    function greet () { console.log("I'm " + name + ", and I attend " + this.school); }

    this.showID = showIDCard;
    this.greet = greet;
}


Student.prototype.school = "The JS Academy of Hard-Knocks";
Student.prototype.comment_on_school = function (feeling) {
    console.log("I " + feeling + " " + this.school);
}

var bob = new Student("Bob", 1);
var doug = new Student("Doug", 2);
var mary = new Student("Mary", 1);


mary.school = "The JS School of Closure";



bob.greet(); // I'm Bob and I attend The JS School of Hard-Knocks
mary.greet(); // I'm Mary and I attend the JS School of Closure
mary.comment_on_school("love"); // I love The JS School of Closure

prototypeには、独自の値が与えられていない の schoolデフォルト値が定義されています。関数はオブジェクトの実際のプロパティにアクセスするために使用されるため、オブジェクト間で共有できる関数も提供されました。Studentprototypethis

関数の内部変数は、関数の内部で定義されたプロパティまたはメソッドによってのみアクセスできます。

したがって、この場合、prototypeメソッドはを介するid場合を除いて、 にアクセスすることはできません。これは、 が関数への参照this.showIDであるためです。これは、独自の一意の を持ち、その関数の独自のコピーが自分のへの参照を持っているすべての生徒ごとに作成されるためです。その引数の独自のコピーを作成します。this.showIDshowIDCardid

大規模な「クラス」方法論を JS に適用するための私の提案は、オブジェクトの構成を優先するスタイルを採用することです。サブクラスにする場合は、各サブクラスを、独自の公開インターフェイスと独自のプライベート スコープの変数を備えたモジュールにしてから、そのモジュールを、作成しようとしていたもののプロパティにします。継承のチェーンを機能させようとするよりも。

つまり、基本クラスから継承し、それを 8 世代または 10 世代拡張するようなことを行うと予想している場合、JS での作業が多すぎます。それは涙で終わり、JS は「OOP」ではないという不満 (あなたが望むスタイル) で終わります。

于 2012-09-10T20:56:11.610 に答える
1

実際、私は JavaScript クラスの定義に非標準のアプローチを使用することを推奨しています。次のコーディング規則により、オブジェクト指向のバックグラウンドを持つすべての人にとってコードが読みやすく、理解しやすくなります。Method.prototype=function(){};クラスの名前を変更したり、メソッドを追加したり、クラスの階層を理解したり、自分のコードが何をしているかを再解釈したりするときはいつでも面倒なメソッドとは異なり、維持するのも非常に簡単です。

代わりに、次のアーキテクチャを使用してオブジェクト指向構造を宣言できます。

/**
* public class Animal
**/
(function(namespace) {
    var __class__ = 'Animal';

    /**
    * private static:
    **/
    var animalCount = 0;

    /**
    * public Animal(string name)
    **/
    var constructor = function(name) {

        // here you can assert arguments are correct
        if(arguments.length == 0) {
            return global.error('needs a name');
        }

        /**
        * private:
        **/
        var animalIndex = animalCount++;

        /**
        * public:
        **/
        var operator = {
            speak: function() {
                console.log('?');
            },
            getName: function() {
                return name;
            },
            getAnimalIndex: function() {
                return animalIndex;
            },
        };

        return operator;
    };

    /**
    * public static Animal()
    **/
    var global = namespace[__class__] = function() {
        // new Animal();
        if(this !== namespace) {
            // construct a new instance of this class
            instance = constructor.apply(this, arguments);
            return instance;
        }
        // Animal();
        else {
            // return the last instantiation of this class
            return instance; // or do whatever you want
        }
    };

    /**
    * public static:
    **/
    // overrides the default toString method to describe this class from a static context
    global.toString = function() {
        return __class__+'()';
    };

    // prints a message to the console's error log
    global.error = function() {
        var args = Array.prototype.slice.apply(arguments);
        args.unshift(__class__+':');
        console.error.apply(console, args);
    };
})(window);

/**
* publc class Dog extends Animal
**/
(function(namespace) {
    var __class__ = 'Dog';

    /**
    * private static:
    **/
    var dogCount = 0;

    /**
    * public Dog()
    **/
    var construct = function(name) {

        /**
        * private:
        **/
        var dogIndex = dogCount++;

        /**
        * public operator() ();
        **/
        var operator = new Animal(name);

        /**
        * public:
        **/

        // overrides parent method 'speak'
        operator.speak = function() {
            console.log(operator.getName()+': bark!');
        };

        // method returns value of private variable
        operator.getSpeciesIndex = function() {
            return dogIndex;
        };

        return operator;
    };

    /**
    * public static Dog()
    **/
    var global = namespace[__class__] = function() {

        // new Dog();
        if(this !== namespace) {
            // construct a new instance of this class
            instance = construct.apply(this, arguments);
            return instance;
        }

        // Dog();
        else {
            // return the last instantiation of this class
            return instance; // or do whatever you want
        }
    };
})(window);


/**
* publc class Cat extends Animal
**/
(function(namespace) {
    var __class__ = 'Cat';

    /**
    * private static:
    **/
    var catCount = 0;

    /**
    * public Cat()
    **/
    var construct = function(name) {

        // here you can assert arguments are correct
        if(arguments.length == 0) {
            return global.error('needs a name');
        }

        /**
        * private:
        **/
        var catIndex = catCount++;

        /**
        * public operator() ();
        **/
        var operator = new Animal(name);

        /**
        * public:
        **/

        // overrides parent method 'speak'
        operator.speak = function() {
            console.log(name+': meow!');
        };

        // method returns value of private variable
        operator.getSpeciesIndex = function() {
            return catIndex;
        };

        return operator;
    };

    /**
    * public static Cat()
    **/
    var global = namespace[__class__] = function() {

        // new Cat();
        if(this !== namespace) {
            // construct a new instance of this class
            instance = construct.apply(this, arguments);
            return instance;
        }

        // Cat();
        else {
            // return the last instantiation of this class
            return instance; // or do whatever you want
        }
    };
})(window);

上記のクラスが宣言されたので、Animal、Dog extends Animal、Cat extends Animal ... 次の結果が得られます。

new Dog(); // prints: "Animal: needs a name" to error output

var buddy = new Dog('Buddy');
buddy.speak(); // prints: "Buddy: bark!"

var kitty = new Cat('Kitty');
kitty.speak(); // prints: "Kitty: meow!"

var oliver = new Dog('Oliver');
oliver.speak(); // prints: "Oliver: bark!"


buddy.getSpeciesIndex(); // returns 0;
buddy.getAnimalIndex(); // returns 0;

kitty.getSpeciesIndex(); // returns 0;
kitty.getAnimalIndex(); // returns 1;

oliver.getSpeciesIndex(); // returns 1;
oliver.getAnimalIndex(); // returns 2;

この JavaScript コーディング規則は、組織化されたオブジェクト指向構造を維持する手段としてのみ提供しています。このようなコーディング スタイルのパフォーマンスが他の慣習より優れているとは言えませんが、コードのパフォーマンスが必要な場合は、同じコードを最適化するGoogle の Closure Compilerを使用することを強くお勧めします。

私はこの JavaScript コーディング スタイルを、自分自身での長年のコーディング経験と、他の人のコードを批判することの同化から導き出しました。私はその堅牢性とモジュール性を誓い、それ以外に関するコメントを歓迎します。

于 2012-09-10T22:03:53.513 に答える
0

あなたはだまされました。コンストラクターはプロトタイプを変更しないでください。また:

function subObject(oName)
{
    var myName = oName;
    this.myName = "My Secret SubName!";

}

subObject.prototype.revealName  = function()
{
    console.info("My Property Name is: " + this.myName);
    console.info("OK, my real name is: " + myName + ", yeah and we also say: " + global);
}

または:

function subObject(oName)
{
    var myName = oName;
    this.myName = "My Secret SubName!";

    subObject.revealName  = function()
    {
        console.info("My Property Name is: " + this.myName);
        console.info("OK, my real name is: " + myName + ", yeah and we also say: " + global);
    }
}
于 2012-09-10T20:24:02.650 に答える
0

ブレイクの答えは私にインスピレーションを与えましたが、私が望んでいたすべてのことをしていないことがわかったので、シンプルでエレガントな構文で C++ の OOP 機能のほとんどをカバーするものができるまでハッキングしました。

現時点でサポートされていない唯一のもの (ただし、実装の問題です):

  • 多重継承
  • 純粋仮想関数
  • 友達クラス

例と重要な readme については、github リポジトリを参照してください。

于 2013-07-21T23:29:51.740 に答える