我想创建一个具有隐藏属性的对象(一个未在for (var x in obj
循环中显示的属性)。有可能这样做吗?
答案 0 :(得分:44)
在ECMAScript 3中是不可能的(这是主要的浏览器在2010年提出这个问题时实现的)。但是,在ECMAScript 5中,所有主流浏览器的当前版本都可以实现,可以将属性设置为不可枚举:
var obj = {
name: "Fred"
};
Object.defineProperty(obj, "age", {
enumerable: false,
writable: true
});
obj.age = 75;
/* The following will only log "name=>Fred" */
for (var i in obj) {
console.log(i + "=>" + obj[i]);
}
这适用于当前浏览器:有关旧浏览器兼容性的详细信息,请参阅http://kangax.github.com/es5-compat-table/。
请注意,在调用Object.defineProperty
时,该属性也必须设置为可写,以允许正常分配(默认情况下为false
)。
答案 1 :(得分:9)
这有点棘手!
function secret() {
var cache = {};
return function(){
if (arguments.length == 1) { return cache[arguments[0]];}
if (arguments.length == 2) { cache[arguments[0]] = arguments[1]; }
};
}
var a = secret();
a.hello = 'world';
a('hidden', 'from the world');
如果你是真正的专业人士,你可以这样做!
var a = new (secret())();
a.hello = 'world';
a.constructor('hidden', 'from the world');
现在如果你看一个萤火虫它会成为一个对象......但你知道的更好! ;-)
答案 2 :(得分:6)
为了保持最新状态,这是ES6 +中的状态。我有点超出了问题的范围,并讨论了如何隐藏属性,而不仅仅是for ... in
循环。
有几种方法可以创建可能被称为"隐藏的属性",而不会查看由闭包关闭的变量之类的东西,这些变量受到范围规则的限制。
与以前版本的ECMAScript一样,您可以使用Object.defineProperty
创建未标记为enumerable
的属性。当您使用某些方法枚举对象的属性时,这会使属性不显示,例如for ... in
循环和Object.keys
函数。
Object.defineProperty(myObject, "meaning of life", {
enumerable : false,
value : 42
});
但是,您仍然可以使用Object.getOwnPropertyNames
函数找到它,该函数甚至返回不可枚举的属性。当然,你仍然可以通过它的密钥访问该属性,这只是一个任何人都可以构建的字符串。
symbol
属性在ES6中,可以使用新原始类型的键 - symbol
创建属性。 Javascript本身使用此类型来使用for ... of
循环和库编写器枚举对象来执行各种其他操作。
Symbols
有一个描述性文字,但它们是具有唯一标识的引用类型。它们不像字符串,如果它们具有相同的值则相等。要使两个符号相等,它们必须是两个完全相同的参考。
您使用symbol
函数创建Symbol
:
let symb = Symbol("descriptive text");
您可以使用Object.defineProperty
函数定义符号作为键的属性。
let theSecretKey = Symbol("meaning of life");
Object.defineProperty(myObject, theSecretKey, {
enumerable : false,
value : 42
});
除非有人获得对该确切符号对象的引用,否则他们无法按键查找该属性的值。
但您也可以使用常规语法:
let theSecretKey = Symbol("meaning of life");
myObject[theSecretKey] = 42;
具有此键类型的属性永远不会出现在for ... in
循环等中,但仍然可以枚举且不可枚举,因为像Object.assign
这样的函数对于非可枚举属性的工作方式不同。 / p>
Object.getOwnPropertyNames
无法获取对象的symbol
个键,但名称相同的Object.getOwnPropertySymbols
将会解决问题。
隐藏对象上的属性的最强方法是不将它存储在对象上。在ES6之前,这有点棘手,但现在我们的地图很弱。
弱映射基本上是Map
,即键值存储,它不会保留(强)对键的引用,因此可以对它们进行垃圾回收。弱映射非常有限,并且不允许您枚举其键(这是设计)。但是,如果您获得了某个地图键的引用,则可以获得与其相关的值。
它们主要用于扩展对象而不实际修改它们。
基本思路是创建一个弱映射:
let weakMap = new WeakMap();
并使用您想要扩展为对象的对象。然后,值将是属性集,可以是{}
个对象的形式,也可以是Map
数据结构的形式。
weakMap.set(myObject, {
"meaning of life" : 42
});
这种方法的优势在于,某人需要获取对您的weakMap
实例和密钥的引用,以便获取值,或者甚至知道它们存在于那里&#39我没办法解决这个问题。所以它是100%,保证是安全的。以这种方式隐藏属性可确保用户不会发现它们,并且您的Web应用程序永远不会被黑客攻击*
当然,所有这一切中最大的缺陷是,这并没有创造出实际的财产。所以它没有参与原型链等。
(*)这是谎言。
答案 3 :(得分:0)
var Foo=function(s){
var hidden
this.setName=function(name){theName=name}
this.toString=function(){return theName}
this.public=s
}
var X=new Foo('The X')
X.setName('This is X')
X // returns 'This is X'
X.public // returns 'The X'
X.hidden // returns 'undefined'
答案 4 :(得分:0)
尝试一下:
Object.defineProperty(
objectName,
'propertiesName', {
enumerable: false
}
)
答案 5 :(得分:0)
这是使用Proxy对象的解决方案。
事件发射器示例:
class Event {
constructor(opts = {}) {
this.events = new Map
this.proxy = new class {}
Object.defineProperty(this.proxy, 'on', { value: this.on.bind(this) })
Object.defineProperty(this.proxy, 'emit', { value: this.emit.bind(this) })
Object.defineProperty(this.proxy, 'length', { get: () => this.length })
Object.defineProperty(this.proxy.constructor, 'name', {
value: this.constructor.name
})
return new Proxy(this.proxy, {})
}
on(topic, handler) {
if (!this.events.has(topic))
this.events.set(topic, new Set)
this.events.get(topic).add(handler)
return this.remove.bind(this, topic, handler)
}
emit(topic, ...payload) {
if (!this.events.has(topic))
return
const set = this.events.get(topic)
for (const fn of set)
fn(...payload)
}
remove(topic, handler) {
if (!this.events.has(topic))
return
const set = this.events.get(topic)
if (set.has(handler))
set.delete(handler)
}
get length() {
return this.events.size
}
}
注意,在构造函数中,该构造函数将返回带有对proxy属性的引用的新Proxy。我“装饰”了代理对象,使其看起来像原始类。您可以得到长度,因为我公开了该吸气剂,但是(据我所知)无法直接访问事件Map并遍历键。我猜这有点像反向关闭吗?我不确定这在垃圾回收方面如何工作。但这确实可以将功能封装在远离用户的位置,这样他们就无法将事情搞糟。
更新:
因此,该方法正在干扰原型继承。在这里,我通过创建类时使用了构造方法并提升了“隐藏”变量events
,从而找到了一种类似但更好的技术。
let Event =
class Event {
on(topic, handler) {
if (!events.has(topic))
events.set(topic, new Set)
events.get(topic).add(handler)
return this.remove.bind(this, topic, handler)
}
emit(topic, ...payload) {
if (!events.has(topic))
return
const set = events.get(topic)
for (const fn of set)
fn(...payload)
}
remove(topic, handler) {
if (!events.has(topic))
return
const set = events.get(topic)
if (typeof handler === 'undefined')
return events.delete(topic)
if (set.has(handler))
set.delete(handler)
}
get length() {
return events.size
}
}
let events
Event = new Proxy(Event, {
construct (target, args, self) {
events = new Map
return Reflect.construct(target, args, self)
}
})
以下是使用此概念的功能更全的事件发射器的要点: https://gist.github.com/aronanda/18b6397c355da88ca170d01e0cc02628