Javascript 中的封装/数据隐藏?
Encapsulation / Data Hiding in Javascript?
我想了解 JavaScript 中封装的概念,以及如何使我的属性和方法 public 或私有。
我正在玩这个例子:
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 name
和 var age
是私有的。为了访问它们,我使用 public 方法通过使用 .this
引用。所以在我的函数中 var
的任何东西都是私有的,而在我的对象中 .this
的任何东西在外面都是可见的。
我猜这是因为 person 是可见的,所以它公开了它的所有属性。
我走在正确的轨道上吗?这是隐藏或公开您的属性/方法的正确方法吗?
还有一个问题,为什么 console.log(jack.getAge());
会抛出错误?当在变量中引用我 "stored" 的函数时,我应该把 () 放在那个函数的末尾吗,它是双向的,所以我不知道有什么用?
谢谢!
I guess that's cause person is visible, so it exposes all of its properties.
正确。
Am I on the right track? Is this the right way to hide or expose your properties / methods?
如果你想这样做,那么是的,这是一个相当标准的方法。从 ES2015 开始,至少还有一种其他方式,但(可能)开销更大。
And one more question, why does console.log(jack.getAge()); throws an error?
因为 jack
对象没有 getAge
属性,所以 jack.getAge
产生 undefined
,这不是函数。 getAge
变量 在 giveAge
闭包可以访问的上下文中(连同 age
和 name
),但是 jack
没有 getAge
属性.
And when referencing functions I "stored" in variables, should I put () at the end of that function, it works both ways, so I don't know what do use?
不,这不是双向的。 jack.getName
获取函数的引用。 jack.getName()
调用 函数并获取其 return 值。
我应该注意到 getAge
函数没有意义。它只能被 person
函数中定义的闭包访问,就像 age
和 name
一样。所以任何会使用 getAge
的东西都会使用 age
来代替函数调用。
为了完整起见,我会注意到很多人并不担心 JavaScript 中的真正私有属性,而是选择 "private by convention"——例如,他们使用命名约定(例如以 _
) 开头的名称意味着 "don't touch these, they're private." 这并不能阻止人们使用它们,当然,它只是表明他们不应该使用它们。提倡这一点的人通常会指出,在许多具有 "true" private properties/fields(Java、C#)的语言中,那些 properties/fields 只需通过反射进行少量努力即可访问。因此,争论是,仅使用命名约定是 "as private."
我不同意(也不是特别不同意)这一点,与 public 相比,在 Java 或 C# 中访问私有属性确实需要更多的工作。我只是注意到 "private by convention" 方法很常见,而且经常 "good enough."
I guess that's cause person is visible, so it exposes all of its properties.
不完全是。首先,person 是一个常规函数。它可以在没有 new 关键字的情况下完美调用,但结果会炸毁你的整个应用程序。
要了解原因,您首先应该了解 new-关键字在幕后做了什么。这将是一个 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
waaaaat?
你已经覆盖了 public 方法 getNameAndAge。
通过在 当前对象 .
上调用(也是 public)方法 getName() 来访问该名称
但 giveAge() 仍然是声明此特定 "instance of the getNameAndAge-function" 范围内的封闭变量,因此它来自 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
我不想说这很糟糕,或者你应该避免它但是它有它的工作方式,你应该考虑到这一点。
因为这种方式为我们提供了原型继承,以及编写混合宏的好方法,而无需一直编写包装器方法。
您可以将其视为 "i need to secure my private state",或 "I work in whatever enviorment you provide to me"
And one more question, why does console.log(jack.getAge()); throws an error?
因为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
}
我想了解 JavaScript 中封装的概念,以及如何使我的属性和方法 public 或私有。
我正在玩这个例子:
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 name
和 var age
是私有的。为了访问它们,我使用 public 方法通过使用 .this
引用。所以在我的函数中 var
的任何东西都是私有的,而在我的对象中 .this
的任何东西在外面都是可见的。
我猜这是因为 person 是可见的,所以它公开了它的所有属性。
我走在正确的轨道上吗?这是隐藏或公开您的属性/方法的正确方法吗?
还有一个问题,为什么 console.log(jack.getAge());
会抛出错误?当在变量中引用我 "stored" 的函数时,我应该把 () 放在那个函数的末尾吗,它是双向的,所以我不知道有什么用?
谢谢!
I guess that's cause person is visible, so it exposes all of its properties.
正确。
Am I on the right track? Is this the right way to hide or expose your properties / methods?
如果你想这样做,那么是的,这是一个相当标准的方法。从 ES2015 开始,至少还有一种其他方式,但(可能)开销更大。
And one more question, why does console.log(jack.getAge()); throws an error?
因为 jack
对象没有 getAge
属性,所以 jack.getAge
产生 undefined
,这不是函数。 getAge
变量 在 giveAge
闭包可以访问的上下文中(连同 age
和 name
),但是 jack
没有 getAge
属性.
And when referencing functions I "stored" in variables, should I put () at the end of that function, it works both ways, so I don't know what do use?
不,这不是双向的。 jack.getName
获取函数的引用。 jack.getName()
调用 函数并获取其 return 值。
我应该注意到 getAge
函数没有意义。它只能被 person
函数中定义的闭包访问,就像 age
和 name
一样。所以任何会使用 getAge
的东西都会使用 age
来代替函数调用。
为了完整起见,我会注意到很多人并不担心 JavaScript 中的真正私有属性,而是选择 "private by convention"——例如,他们使用命名约定(例如以 _
) 开头的名称意味着 "don't touch these, they're private." 这并不能阻止人们使用它们,当然,它只是表明他们不应该使用它们。提倡这一点的人通常会指出,在许多具有 "true" private properties/fields(Java、C#)的语言中,那些 properties/fields 只需通过反射进行少量努力即可访问。因此,争论是,仅使用命名约定是 "as private."
我不同意(也不是特别不同意)这一点,与 public 相比,在 Java 或 C# 中访问私有属性确实需要更多的工作。我只是注意到 "private by convention" 方法很常见,而且经常 "good enough."
I guess that's cause person is visible, so it exposes all of its properties.
不完全是。首先,person 是一个常规函数。它可以在没有 new 关键字的情况下完美调用,但结果会炸毁你的整个应用程序。
要了解原因,您首先应该了解 new-关键字在幕后做了什么。这将是一个 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
waaaaat?
你已经覆盖了 public 方法 getNameAndAge。
通过在 当前对象 .
上调用(也是 public)方法 getName() 来访问该名称
但 giveAge() 仍然是声明此特定 "instance of the getNameAndAge-function" 范围内的封闭变量,因此它来自 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
我不想说这很糟糕,或者你应该避免它但是它有它的工作方式,你应该考虑到这一点。
因为这种方式为我们提供了原型继承,以及编写混合宏的好方法,而无需一直编写包装器方法。
您可以将其视为 "i need to secure my private state",或 "I work in whatever enviorment you provide to me"
And one more question, why does console.log(jack.getAge()); throws an error?
因为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
}