我将使用以下JavaScript代码来演示我要做的事情。
var App = function() {
var url
var width
var height
function init()
{
url = window.location.href
width = document.body.clientWidth
height = document.body.clientHeight
}
function run()
{
init()
alert(url + ' - ' + width + 'x' + height)
}
return {
run: run
}
}()
App.run()
(运行此代码:http://jsfiddle.net/zePq9/)
上面的代码实现了三件事:
App
除外)。url
,width
和height
变量以及init()
函数。run()
函数。但是,我不太喜欢这种方法,因为init()
和run()
函数必须使用url
,width
和height
等变量。在更复杂和更大的JavaScript代码中,很难在函数中看到变量url
并告诉它来自何处。人们必须做很多滚动来回答这样的问题:它是一个局部变量吗?它是一个全局变量吗?它只是局部的无功功能吗?
目前我正在使用以下代码解决此问题。
var App = function() {
var self = {
url: '',
width: 0,
height: 0
}
function init()
{
self.url = window.location.href
self.width = document.body.clientWidth
self.height = document.body.clientHeight
}
function run()
{
init()
alert(self.url + ' - ' + self.width + 'x' + self.height)
}
return {
run: run
}
}()
App.run()
(运行此代码:http://jsfiddle.net/cXALf/)
现在,我可以清楚地看到self.url
并告诉我这个变量来自self
对象,由于我使用{的编码约定,我所知道的范围仅限于无穷大函数{1}}对象将匿名函数中所有函数共享的变量保存在一起。请注意,self
仍隐藏在匿名函数中,并且在其外部不可见。
理想情况下,我想使用self.url
,this.url
等代替this.width
,self.url
等。但我无法找到解决方案会保持self.width
,this.url
等不受异常功能的影响。有可能吗?
答案 0 :(得分:2)
根据您认为不可见的内容,您可以使用多种解决方案之一。但是,每种解决方案都有其自身的缺陷。
您必须考虑的是为什么要使用与“私人变量”相关的关键字this
。在JavaScript中,并没有真正的私有变量概念(稍后会详细介绍)。任何可从一个上下文访问的内容都可以从另一个上下文访问。
此解决方案最接近您最初的解决方案,但使用Function.prototype.bind
强制上下文成为闭包内部对象。
function App() {
var self = {
url: '',
width: 0,
height: 0
};
var init = function() {
this.url = window.location.href;
this.width = document.body.clientWidth;
this.height = document.body.clientHeight;
}.bind(self);
var run = function() {
init();
console.log(this.url + ' - ' + this.width + 'x' + this.height);
}.bind(self);
return {
run: run
};
}
这种方法的缺点是每个“成员”函数必须是.bind()
到self
,否则这些函数的上下文不会是你的内部self
。宣布了。这种方法也不是非常 Javascript-y ,因为必须创建构造函数的每个方法和属性。这避免了JavaScript具有的原型继承系统。
如果您愿意坚持只使用符合ES5标准的JS引擎(这通常意味着没有小于9的IE),您可以使用ES5的Object.defineProperty()
来隐藏对象的属性。您仍然可以从其他上下文访问它们,但至少它们不会显示在对象属性的枚举中。
function App() {
this.url = window.location.href;
this.width = document.body.clientWidth;
this.height = document.body.clientHeight;
}
Object.defineProperties(App.prototype, {
url: {
enumerable: false,
configurable: true,
writable: true,
value: ""
},
width: {
enumerable: false,
configurable: true,
writable: true,
value: 0
},
height: {
enumerable: false,
configurable: true,
writable: true,
value: 0
}
});
App.prototype.run = function() {
console.log(this.url + ' - ' + this.width + 'x' + this.height);
};
new App().run();
这种技术的优势在于,您可以精确控制哪些属性是可枚举的(for...in
或Object.keys()
等枚举“可见”,哪些不可。此外,App
对象的构造发生在它的实际构造函数中,run()
的实现现在可以被子类覆盖。缺点是这需要ES5兼容引擎,并且这不会阻止外部代码访问或修改url
,width
或height
,如果他们有权访问实例App
。
在JavaScript中获得真正的隐私的唯一方法是在范围内声明变量,并提供也在同一范围内定义的访问器。 JavaScript中的作用域机制只是在范围之外无法引用这些变量。类似地,匿名实例可以做几乎相同的事情......如果你从未使用App
的实例超出对run
的唯一调用,例如:
new App().run();
然后,new App()
创建的实例将无法在其他任何位置访问。这不是真正的隐私,因为引用该实例仍允许您访问其所有属性,但这可能就足够了。
答案 1 :(得分:1)
您可以使用bind
永久设置每个功能的this
值:
var App = function() {
var self = {
url: '',
width: 0,
height: 0
};
var init = function()
{
this.url = window.location.href;
this.width = document.body.clientWidth;
this.height = document.body.clientHeight;
}.bind(self);
var run = function()
{
init();
alert(self.url + ' - ' + self.width + 'x' + self.height);
}.bind(self);
return {
run: run
}
}();
请注意旧版浏览器不支持bind,但您可以use a polyfill将其添加到不自动提供的浏览器中。
请注意,我不一定认为此代码是好主意,但它确实能够完全符合您的要求。我个人认为使用self
作为变量的显式容器没有任何问题,而使用this
可能会使您的代码更难以理解(因为他们可能认为App
是有实例)。
答案 2 :(得分:-1)
您的应用可以转换为类似的内容(它使用this
并且没有关闭):
function App() {
this.init();
}
App.prototype = {
url: '',
width: 0,
height: 0,
init: function() {
this.url = window.location.href
this.width = document.body.clientWidth
this.height = document.body.clientHeight
},
run: function() {
console.log(this.url + ' - ' + this.width + 'x' + this.height)
}
}
new App().run();