for (var x in obj)
循环中显示)。是否可能实现这一点?for (var x in obj)
循环中显示)。是否可能实现这一点?在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
)。
defineProperty
在IE8几乎被支持,这里的“几乎”是指仅在DOM对象上支持。http://msdn.microsoft.com/en-us/library/dd548687(VS.85).aspx - Andy EObject.getOwnPropertyNames(obj)
仍将获取隐藏属性的名称。 - DDSobj.__age
,通常在迭代列表时会使用obj.hasOwnProperty(i)
,但是你可以使用obj.hasOwnProperty(i) && i.charAt(0)!=="_"
。命名约定也是记住哪些属性是公共/私有的好方法。但是,你肯定可以向上面的解决方案添加命名约定,并且它将同样有效。 - John ProestakesdefineProperty
运作良好,至少就原本的问题而言是如此。在 Chrome 中,age
属性略微轻一些,表示它不可枚举。仍然可以使用 Object.getOwnPropertyNames
列出对象的非可枚举属性,因此我会认为这就是控制台所做的事情。 - Tim Down为了保持内容的时效性,这里介绍的是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
循环枚举对象,并由库编写者用于执行各种其他操作。
Symbol
具有描述性文本,但它们是引用类型,具有唯一的标识。它们不像字符串,如果它们具有相同的值,则相等。要使两个符号相等,它们必须是完全相同的两个引用。
您可以使用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
这样的函数对于不可枚举属性有不同的处理方式。
Object.getOwnPropertyNames
无法获取对象的symbol
键,但类似命名的Object.getOwnPropertySymbols
可以解决问题。
在对象上隐藏属性的最强方法是根本不将其存储在对象上。在 ES6 之前,这有点棘手,但现在我们有了弱映射。
弱映射基本上是一个Map
,即一个键值存储,它不保留(强)引用以便被垃圾回收。弱映射非常有限,不允许枚举其键(这是设计如此)。但是,如果您获取到映射的其中一个键的引用,则可以获取与之相关联的值。
它们主要设计用于扩展对象而不实际修改它们。
基本思想是创建一个弱映射:
let weakMap = new WeakMap();
并使用您要扩展的对象作为键。然后值将是属性集,可以采用{}
对象或Map
数据结构的形式。
weakMap.set(myObject, {
"meaning of life" : 42
});
这种方法的优点是,如果想要获取值,甚至知道它们的存在,某人需要引用您的weakMap
实例和密钥。没有任何绕过它的方法。因此这是100%的安全保证。以这种方式隐藏属性可以确保没有用户会发现它们,您的Web应用程序也永远不会被黑客攻击*。class Example {
#thisIsPrivate;
constructor(v) {
this.#thisIsPrivate = v;
}
}
这个字段是真正私有的。它只能被在 Example
语法定义内的代码访问,其他地方无法访问。没有反射API或其他功能可以让您访问该字段。它永远不会出现在诸如 Object.getOwnPropertyNames
的函数的结果中。该字段的名称始终以 #
开头。
这有点棘手!
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');
现在如果你在firebug中查看a,它将会是一个对象...但是你知道的更好!;-)
a.constructor('hidden');
。 - Matt试试这个:
Object.defineProperty(
objectName,
'propertiesName', {
enumerable: false
}
)
)
。 - Melomanvar 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'
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
}
}
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