是否可以在javascript中创建隐藏属性

tmi*_*mim 24 javascript

我想创建一个具有隐藏属性的对象(一个不在for (var x in obj循环中显示的属性).是否有可能做到这一点?

Tim*_*own 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]);
}
Run Code Online (Sandbox Code Playgroud)

这适用于当前浏览器:有关旧版浏览器兼容性的详细信息,请参阅http://kangax.github.com/es5-compat-table/.

请注意,该属性也必须在调用中设置为可写,Object.defineProperty以允许正常分配(false默认情况下).

  • 在IE8中几乎支持+1 - `defineProperty`,这几乎意味着它只在DOM对象上.http://msdn.microsoft.com/en-us/library/dd548687(VS.85).aspx (3认同)
  • `Object.getOwnPropertyNames(obj)`仍然会得到隐藏属性的名称. (3认同)
  • 安迪:谢谢,这很有趣。我没有查到“几乎”是什么意思。 (2认同)

kri*_*ous 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');
Run Code Online (Sandbox Code Playgroud)

如果你是一个真正的专业人士,你可以这样做!

var a = new (secret())();

a.hello = 'world';
a.constructor('hidden', 'from the world');
Run Code Online (Sandbox Code Playgroud)

现在,如果你看一个萤火虫它将是一个对象...但你知道更好!;-)


Gre*_*Ros 8

为了保持最新状态,这就是ES6 +中的状态.我有点超出问题的范围,并讨论如何隐藏属性,而不仅仅是for ... in循环.

有几种方法可以创建可能被称为"隐藏属性"的东西,而不会查看由闭包关闭的变量,这些变量受到作用域规则的限制.

现在经典,不可枚举的财产

与以前版本的ECMAScript一样,您可以使用Object.defineProperty创建未标记的属性enumerable.当您使用某些方法(例如for ... in循环和Object.keys函数)枚举对象的属性时,这会使属性不显示.

Object.defineProperty(myObject, "meaning of life", {
    enumerable : false,
    value : 42
});
Run Code Online (Sandbox Code Playgroud)

但是,您仍然可以使用该Object.getOwnPropertyNames函数找到它,该函数甚至返回不可枚举的属性.当然,你仍然可以通过它的密钥访问该属性,理论上它只是一个任何人都可以构建的字符串.

(不可枚举的)symbol财产

在ES6中,可以使用新原始类型的键创建属性 - symbol.Javascript本身使用此类型来使用for ... of循环和库编写器枚举对象以执行各种其他操作.

Symbols有一个描述性文本,但它们是具有唯一标识的引用类型.它们不像字符串,如果它们具有相同的值则相等.要使两个符号相等,它们必须是两个完全相同的引用.

您创建一个symbol使用该Symbol功能:

let symb = Symbol("descriptive text");
Run Code Online (Sandbox Code Playgroud)

您可以使用该Object.defineProperty函数定义符号作为键的属性.

let theSecretKey = Symbol("meaning of life");
Object.defineProperty(myObject, theSecretKey, {
    enumerable : false,
    value : 42
});
Run Code Online (Sandbox Code Playgroud)

除非有人获得对该确切符号对象的引用,否则它们无法按键查找属性的值.

但您也可以使用常规语法:

let theSecretKey = Symbol("meaning of life");
myObject[theSecretKey] = 42;
Run Code Online (Sandbox Code Playgroud)

具有此键类型的属性将永远不会显示在for ... in循环等中,但仍可以是可枚举的且不可枚举的,因为函数Object.assign对于非可枚举属性的工作方式不同.

Object.getOwnPropertyNames不会得到symbol对象的键,但相似的命名Object.getOwnPropertySymbols将成功.

弱地图

隐藏对象上的属性的最强方法是不将它存储在对象上.在ES6之前,这有点棘手,但现在我们有了弱映射.

弱映射基本上是Map一个键值存储,它不会保留(强)对键的引用,因此它们可以被垃圾收集.弱映射非常有限,并且不允许您枚举其键(这是设计).但是,如果您获得对某个地图键的引用,则可以获得与其相关的值.

它们主要用于允许扩展对象而无需实际修改它们.

基本思路是创建一个弱映射:

let weakMap = new WeakMap();
Run Code Online (Sandbox Code Playgroud)

并使用您想要扩展为对象的对象.然后,值将是属性集,可以是{}对象的形式,也可以是Map数据结构的形式.

weakMap.set(myObject, {
    "meaning of life" : 42
});
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是,有人需要获取对您的weakMap实例密钥的引用,以便获取值,或者甚至知道它们存在.没有办法解决这个问题.所以它是100%,保证是安全的.以这种方式隐藏属性可确保用户不会发现它们,并且您的Web应用程序永远不会被黑客攻击*

当然,所有这一切中最大的缺陷是,这并不能创造出实际的财产.所以它不参与原型链等.

(*) 这是个谎言.

  • 只是想说非常感谢您如此详细且写得很好的回复!真的感觉我在这里学到了很多东西,并且第一次使用 Wea​​kMap 感到非常兴奋。我最终在我的用例中使用 Map() 而不是 WeakMap() (希望能够克隆地图以便稍后进行浅层比较),但想法是相同的。非常兴奋不会改变数据对象。 (3认同)