Object.keys returns 私人财产

Object.keys returns private properties

我用 Typescript 创建了 class/viewModel。 我将此 class 中的一个字段设为私有,以便在我尝试获取所有其他 class 属性时跳过它。

这样对吗?如何跳过我的私人属性?

Object.keys(myObject).forEach(property => {
        //some stuff
     }
});

我的 class 示例:

class MyModel{
    id: any = ko.observable('');
    name: any = ko.observable('');

    address: any = ko.observable('');
    city: any = ko.observable('');
    state: any = ko.observable('');
    country: any = ko.observable('');

    private secretField= ko.observable('');
}

TypeScript 像普通属性一样编译私有属性,私有性的强制执行只在编译时完成,它们在运行时仍然存在。

在 github 上有很多要求即使在运行时也无法访问私有属性,但由于设计限制 and/or 哲学问题,这尚未实现,而且可能永远不会实现。

您可以阅读一些设计讨论历史here

这意味着您必须使用自己的约定来处理这个问题,例如在名称前加上下划线并在循环中过滤它。

private 关键字仅影响 TypeScript 中的可见性,不影响 JS 输出。

对于未在原型上定义的 class 属性,因此无法使用 class 属性 装饰器进行修改,最直接的方法是使用 _ 私有属性的命名约定:

class MyModel {
    // ...
    private _secretField = ko.observable('');
}

Object.keys(myObject)
    .filter(key => !(typeof key === 'string' && key.charAt(0) === '_'))
    .forEach(property => {
        // some stuff
    });

从ES6开始可以使用Symbols。 它可以存储值并且不会出现在Object.keys结果

Js代码

const privateStuff = Symbol()
var obj = {
  name: "Andrew",
  age: 23,
  [privateStuff]: "Don't show it"  
}

var keys = Object.keys(obj);
keys.forEach((k)=>{console.log(k)});

//get value
var serverStuff=obj[privateStuff] 

TL;DR: 使用 private identifiers 定义您的私有字段。这样,它们的可访问性也将在运行时强制执行。


正如已经指出的那样,可访问性仅由 TypeScript 转换器静态地(在编译时)强制执行。
因此,所有属性,无论是 public 还是私有属性,都作为正常的 JavaScript 属性发出。这里没有魔法,除了使用 Proxy 来捕获 ownKeys() 方法或使用 Object.defineProperties 而不是以 TypeScript 方式声明它们之外,您无法做任何事情来隐藏它们。对于后一种想法,我想出了一个例子:

class Foo {

    constructor() {

        Object.defineProperties(this, {
            bar: {
                enumerable: false,
                value: "Hello world"
            }
        })

        console.log((this as any).bar)
    }
}

上面的例子可以在TypeScript Playground.

中测试

但是,我认为这样做是一种反模式,因为它破坏了所有 TypeScript 的安全性,这是选择它而不是仅仅写出 JavaScript 代码的唯一原因。

因此,我们剩下的唯一解决方案是使用 private identifiers。这是一个 TypeScript 特性,因此任何名称以 # 开头的字段都被强制为私有,不仅在编译时,甚至在运行时也是如此。

class Foo {

    #bar = "Hello world"

    constructor() {
        console.log(this.#bar)
    }
}

console.log(Object.keys(new Foo()))

上面的例子可以在TypeScript Playground.

中测试

这是如何运作的?好吧,您可能只是看一下转译后的 JavaScript 代码,您会突然注意到一个 WeakMap。实际上,在 class 中的每个字段都定义了对新 WeakMap 的引用,每个字段都有一个私有标识符,在定义它们的声明 class 的同一词法范围内。无论在何处访问私有字段,都是通过调用 getter 或 setter 函数来完成的,该函数使用给定映射(在引用该字段时传递)来获取或设置给定键的值,这是class 访问字段的实例。在 getter 和 setter 函数中也会进行运行时检查,以便在使用未注册的接收器调用时抛出 TypeError 以防止使用的实例访问私有字段不同类型或根本没有实例。