1

JavaScript のカプセル化の概念と、プロパティとメソッドをパブリックまたはプライベートにする方法を理解したいと思います。

私はこの例で遊んでいます:

    var person = function(newName, newAge) {

    // Private variables / properties
    var name = newName;
    var age = newAge;

    // Public methods
    this.getName = function() {
        return name;
    }

    // Private methods
    var getAge = function() {
        return age;
    }

    // Public method, has acces to private methods
    this.giveAge = function() {
        return getAge();
    }
}

var jack = new person("Jack", 30);

console.log(jack.name); // undefined
console.log(jack.getName); // Jack
console.log(jack.getAge()); // TypeError: jack.getAge is not a function
console.log(jack.getAge); // undefined
console.log(jack.giveAge()); // 30

したがって、変数var namevar ageは非公開です。それらにアクセスするには、参照を使用してパブリック メソッドを使用し.thisます。したがってvar、関数内にあるものはすべてプライベートであり.this、オブジェクト内にあるものはすべて外部に表示されます。

それが原因だと思います person が表示されているため、そのすべてのプロパティが公開されています。

私は正しい軌道に乗っていますか?これは、プロパティ/メソッドを非表示または公開する正しい方法ですか?

もう 1 つの質問ですが、なぜconsole.log(jack.getAge());エラーがスローされるのでしょうか。また、変数に「保存」した関数を参照する場合、その関数の最後に () を付ける必要があります。どちらの方法でも機能するため、何を使用するのかわかりませんか?

ありがとう!

4

2 に答える 2

4

それが原因だと思います person が表示されているため、そのすべてのプロパティが公開されています。

正しい。

私は正しい軌道に乗っていますか?これは、プロパティ/メソッドを非表示または公開する正しい方法ですか?

やりたい場合は、はい、これはかなり標準的な方法です。ES2015 の時点で少なくとも 1 つの方法がありますが、(おそらく) オーバーヘッドが大きくなります。

もう 1 つの質問は、なぜ console.log(jack.getAge()); を実行するのかということです。エラーをスローしますか?

jackオブジェクトにはgetAgeプロパティがないため、関数ではないjack.getAgeyieldsundefinedになります。クロージャーがアクセスできるコンテキスト内にgetAge 変数があります (およびとともに)が、プロパティはありません。giveAgeagenamejackgetAge

また、変数に「保存」した関数を参照する場合、その関数の最後に () を付ける必要があります。どちらの方法でも機能するため、何を使用するのかわかりませんか?

いいえ、どちらの方法でも機能しません。jack.getName関数への参照を取得します。jack.getName() 関数を呼び出して戻り値を取得します。

関数には意味がないことに注意してくださいgetAge。やare とperson同様に、関数内で定義されたクロージャからのみアクセスできます。したがって、使用するものはすべて代わりに使用し、関数呼び出しを回避します。agenamegetAgeage


完全を期すために、多くの人が JavaScript の真のプライベート プロパティについて心配せず、代わりに「慣習によるプライベート」を選択していることに注意してください_。これらに触れてください、それらはプライベートです。」もちろん、それは人々がそれらを使用するのを妨げるものではありません。もちろん、使用すべきではないことを示しているだけです。これを支持する人々は通常、「真の」プライベート プロパティ/フィールドを持つ多くの言語 (Java、C#) では、リフレクションを介してわずかな労力でそれらのプロパティ/フィールドにアクセスできることを指摘しています。したがって、命名規則を使用するだけで「非公開」になるという議論が続きます。

私はそれに同意していません (特に反対もしていません)。パブリック プロパティよりもプライベート プロパティにアクセスするには、Java または C# でかなり多くの作業が必要です。「慣習によるプライベート」アプローチは非常に一般的であり、多くの場合「十分」であることに注意してください。

于 2016-02-05T15:19:31.820 に答える
1

それが原因だと思います person が表示されているため、そのすべてのプロパティが公開されています。

ではない正確に。まず、 person は正規関数です。new-keyword なしで完全に呼び出すことができますが、その結果、アプリケーション全体が壊れてしまいます。

その理由を理解するには、まず new-keyword が舞台裏で何をしているのかを理解する必要があります。これは js 実装になります。

function fakeNew(constructor, ...args){
    if(typeof constructor !== "function"){
        throw new TypeError(constructor + " is not a constructor");
    }

    //create a new Instance of the constructors prototype-property
    var instance = Object.create(constructor.prototype);

    //invoke the constructor with the scope set to the instance and the provided arguments
    var result = constructor.apply(instance, args);

    //check wether the returned value is an Object (and functions are considered as Objects)
    if(result === Object(result)){
        //then return the result-value in favor to the instance
        return result;
    }

    //otherwise return the instance
    return instance;
}

一方、どの関数もコンストラクターになることができます。特別な必要はありません。すべてあなた次第です。

では、ジャックに戻ります

var jack = person("Jack", 30);  //would result in the following desaster:
console.log(jack);      //undefined, since person doesn't return anthing

console.log(jack.getName());    
//will throw, since jack is still undefined, and therefore doesn't have any properties

//BUT:
console.log(window.getName())   //will return "Jack" now

console.log(window.getAge);     //undefined, but this is fine 
//global scope has not been polluted with this one, cause getAge was a local variable inside the function-call

console.log(window.giveAge())   //can still call the enclosed (private) function getAge()

それから

var jill = person("Jill", 28);
//will overwrite the global functions and expose new values now
console.log(window.getName(), window.giveAge()) //"Jill", 28    
//and Jack is kind of gone, well the variable is left but the variable contained undefined, so...

次はスコーピングです。あなたがこれを正しくしたとしましょう

//first let's add a function that executes on the scope
//inside the constructor
this.getNameAndAge = function(){
    return this.getName() + ": " + getAge();
}

.

var andy = new person("Andy", 45);
var joe = new person("Joe", 32);

//let's make Andy a little younger
andy.getNameAndAge = joe.getNameAndAge;
console.log(andy.getNameAndAge(), andy.getName() + ": " + andy.giveAge());
//will result in "Andy: 32", "Andy": 45

うわぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁぁ

public メソッド getNameAndAge をオーバーライドしました。名前は、現在の Object
で (これも public) メソッド getName() を呼び出すことによってアクセスされます。 しかし、giveAge() は、この特定の「getNameAndAge 関数のインスタンス」が宣言されたスコープからの囲まれた変数のままであるため、Joe の関数呼び出しからのものです。

この影響を理解するために、スコーピングをさらに奇妙にしましょう

funciton Animal(species, _name){
    //species is likewise a local variable and can be enclosed, modified, or whatever
    //we don't need to write it to some different variable

    //but we want to expose the name of this animal, since it should be possible to change it later
    //without the need to create a getter and a setter just to change the property of _name
    this.name = _name;

    this.whoAreYou = function(){
        //so we concat the enclosed value from species with the name-argument on this object
        //in the hope that everything will be alright.
        return species + " " + this.name;
    }
}

var jack = new Animal("dog", "Jack");
var jill = new Animal("cat", "Jill");
var joe = new Animal("fish", "Joe");

console.log(jack.whoAreYou());  //"dog Jack"
console.log(jill.whoAreYou());  //"cat Jill"
console.log(joe.whoAreYou());   //"fish Joe"

//as far so good; till now ...
//since these properties are still writable someone will start and move them around

//maybe like a callback
function someFunction(someArg, callback){
    console.log(someArg, callback());
}
someFunction("Who are you?", jack.whoAreYou);

//or sth. like this:
//you may not believe that someone would ever do that, but it will happen!
jack.whoAreYou = jill.whoAreYou;
console.log(jack.whoAreYou());

//and now the poor dog has an Identity-crisis.
//the first one will result in:
"Who are you?", "dog undefined"

//the latter will log "cat Jack"

or even more fummy if sth. like this happens:

var fn = joe.whoAreYou;
console.log(fn.call(jack), fn.call(jill), fn.call(joe), fn.call(Animal));
//cause now they are all fishes, even the Animal-constuctor

これが悪いとか、避けるべきだと言いたいわけではありませんが、それが機能する方法があり、それを考慮する必要があります。

この方法により、プロトタイプの継承が提供され、常にラッパーメソッドを作成することなく、ミックスインを作成する優れた方法が提供されます。

これは、「私は自分のプライベートな状態を確保する必要がある」または「あなたが私に提供する環境で仕事をしている」と見なすことができます。

もう 1 つの質問は、なぜ console.log(jack.getAge()); を実行するのかということです。エラーをスローしますか?

jack.getAge は undefined で undefined は関数ではないため

var getAge = function() {
    return age;
}

この行への別のコメント

JS では、関数宣言と変数宣言が巻き上げられるため、関数の最初から使用できます。式はそうではありません。

var person = function(){
    //...
    foo();
    bar();
    baz();

    function foo(){ console.log("this will work"); }
    var bar = function(){ console.log("this will fail"); }
    //because to this point, bar was delared and assigned with undefined, 
    //and we remember? undefined can't be invoked

    return whatever;

    function baz(){ console.log("event this would work"); }
    //unless some preprocessor decided (falsely), that this function can be removed 
    //since it is after the return-statement, and is therefore unreachable
}
于 2016-02-05T17:32:40.993 に答える