ES6类的私有属性只是语法糖吗?

6

使用 # 语法,现在我们可以像这样在 ES6 类中创建私有属性

class Person {
    #name;
    constructor(name) {
        this.#name = name;
    }
    getName() { return this.#name; }
}

let ron = new Person('ron')
ron.#name // undefined
ron.getName(); // ron

以前,在ES5中,私有属性可以通过以下方式“伪造”:

function Person(name) {
  var name = name;
  this.getName = function() {
    return name;
  }
}
(new Person('ron')).name // undefined
(new Person('ron')).getName() // ron

上面的版本使用了“var”不属于Person实例的事实。因此,使用“closure”的力量,getName可以访问name。但是,这样做的问题是,this.getName()不是原型链的一部分,所以为了将getName添加到原型中,我们必须执行:
Person.prototype.getName = function() { return this.getName(); }

这让人感到困惑,而且闻起来相当糟糕。

另一个选项是这样做:(使用ES6符号,但仍不使用类)

function Person(name) {
  const nameSymbol = Symbol();
  this[nameSymbol] = name;
  this.getName = function() {
    return this[nameSymbol];
  }
}

但仍未解决getName不是原型的一部分的问题。另一个问题是,使用Object.getOwnPropertySymbols,这个“虚假”的私有成员是可访问的。

另一种ES5选项是使用“揭示模块模式”,并公开如下的publicAPI:

const myEs5Module = (function () { 
  var _name = 'yos';
  function getName() {
    return _name;
  }
  const publicAPI = { getname };
  return publicAPI;
})();

但那不是类或构造函数,更像是一个模块。

所以我想知道在ES6类中使用 '#' 语法表示私有属性是否只是语法糖,并且是否可以通过某种方式为像我一样喜欢函数的人提供polyfill。

顺便说一句:我读了如下的帖子: JavaScript ES6类中的私有属性 但仍然感到不满意。 还要注意的是:我不需要ES6模块/babel解决方案。


在JavaScript中,与类有关的一切都是“语法糖”。 - andy mccullough
私有属性是可复用的,但问题在于,如果您在调试器中记录类实例,则可能会看到其私有属性。 - Akxe
@andymccullough - 在私有属性和方法出现之前,这是正确的。 但现在不再是这样了。 - T.J. Crowder
1个回答

5
“ES6类的私有属性只是语法糖吗?” 不是。它们是对象在内部工作方式的基本补充(称为Private fields),存储在以前提案中不存在的对象的新槽中,并且不能以其他方式访问。
“所以我想了解在ES6类中使用'#'语法表示私有属性是否是语法糖,是否可以像我这样的函数爱好者一样进行polyfill。” 您无法在没有“class”语法的情况下使用私有属性。(未来的提案可能会改变这一点。)相反,您需要继续做您正在做的事情(闭包方案),或者使用一个仅您的函数可以访问并以对象为键的WeakMap。 您已经完成了自己的闭包示例,因此这里是使用WeakMap而不是私有属性的Person类:

const Person = (() => {
    const names = new WeakMap();
    function Person(name) {
        names.set(this, name);
    }
    Person.prototype.getName = function getName() {
        return names.get(this);
    };
    return Person;
})();

let ron = new Person("ron")
console.log(ron.name);      // undefined
console.log(ron.getName()); // "ron"


如果你感兴趣的话,可以在我的个人资料中找到链接。顺便说一句,在我最近的书籍《JavaScript:新玩具》第18章中,我详细介绍了私有字段(以及其他可能在今年进入“完成”阶段4的class特性)。 - T.J. Crowder
1
这是一个非常有帮助的答案,对我理解来说是一个极好的奖励。非常感谢。 - Eva Cohen
我感觉需要进一步澄清一些事情。我看到你使用了WeakMap,将“this”作为键,“name”作为值。这样每个实例都会根据需要拥有自己的“name”。但是对于以下代码,怎么处理呢? const Person = (() => { let _name = null; function Person(name) { _name = name; } Person.prototype.getName = function getName() { return _name; }; return Person; }); const PersonFactory = function(name) { const PersonMaker = Person(); return new PersonMaker(name); } - Eva Cohen
我建议的不是IIFE,而只是一个函数表达式。对于可能使您困惑的代码格式化,我很抱歉。我已经测试了它,它确实创建了不同的Mary和Joe。 - Eva Cohen
1
@EvaCohen - 抱歉,我读得太快了。但这比你需要的要复杂得多,每次创建实例时都会创建一个全新的Person(和Person.prototype),没有重用任何东西。最好直接使用你问题中第二个代码块中的Person函数,并直接关闭参数。 - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接