隐藏匿名JavaScript函数中的变量,但使用“this”访问它们
Hide variables within an anonymous JavaScript function but access them using `this`
我将使用以下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
对象,我所知道的范围仅限于匿名函数,因为我的编码约定是使用self
对象将匿名函数中所有函数共享的变量放在一起。注意,self.url
仍然隐藏在匿名函数中,在它之外不可见
理想情况下,我希望使用this.url
、this.width
等来代替self.url
、self.width
等。但我无法找到一个解决方案,使this.url
、this.width
等在匿名函数之外不可见。有可能吗?
根据您认为的不可见,您可以使用几种解决方案之一。然而,每种解决方案都有其自身的缺陷。
您必须考虑的是,为什么要在"私有变量"中使用关键字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特性定义
如果你愿意坚持只使用符合ES5的JS引擎(这主要意味着IE不小于9),你可以使用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兼容的引擎,并且如果外部代码可以访问App
的实例,这不会阻止它们访问或修改url
、width
或height
。
在JavaScript中实现真正的隐私的唯一方法是在一个范围内声明变量,并提供同样在同一范围内定义的访问器。JavaScript中的作用域机制只是使在作用域之外无法引用这些变量。类似地,匿名实例可以做几乎相同的事情。。。如果除了对run
的唯一调用之外,从未使用过App
的实例,例如:
new App().run();
那么new App()
创建的实例在其他任何地方都无法访问。这不是真正的隐私,因为引用该实例仍然可以访问其所有属性,但这可能已经足够了。
您可以使用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
}
}();
请注意,旧的浏览器不支持绑定,但您可以使用polyfill将其添加到不自动提供的浏览器中。
请注意,我不一定认为这段代码是一个好主意,但它确实可以满足您的要求。我个人认为使用self
作为变量的显式容器没有错,而使用this
可能会使其他人更难理解您的代码(因为他们可能认为App
意味着有实例)。
你的应用程序可以转换成这样的东西(它使用this
,没有闭包):
编辑这个解决方案忽略了隐藏的部分,我跳过了问题的第一部分。IMO真实变量隐藏不值得在JS中花费精力和开销。与其他代码相比,此代码更精简,开销更少,但没有隐藏。
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();
- 如何使用(this)访问Angular 2 http rxjs catch函数中的对象属性
- 为什么我不能在 ajax 成功中访问 $(this)
- 在接受功能上访问燃料UX标语牌中的“this”
- $.each 中的“this”,当需要使用“this”访问对象的函数时
- 在jQuery中,我如何从$.ajax回调函数访问$(this)
- TypeScript:在对象文字字段中的访问器中访问外部“this”
- JavaScript 访问函数构造函数中的“this”
- React Native can'无法在render()方法之外访问this.ops
- 通过函数传递“this”时,我是否可以无法访问“this”的属性
- 如何使用“self = this”访问类属性
- 使用“this”访问对象的值
- arguments属性可以由this访问.some_function.arguments?实际上我无法解释
- 使用let初始化的函数在被this访问时不被替换
- 隐藏匿名JavaScript函数中的变量,但使用“this”访问它们
- 如何使用$(this)访问调用Javascript函数的按钮的id
- 在Javascript中无法通过this访问const变量
- 如何在jQuery中使用$(this)访问子元素?
- 无法使用“this”访问对象.“this”指向“窗口”对象
- 使用jQuery的$(this)访问内部文本
- 使用this访问对象内的对象名称