gpt4 book ai didi

javascript - 与私有(private)成员创建对象时的技术差异

转载 作者:行者123 更新时间:2023-12-04 14:46:38 24 4
gpt4 key购买 nike

Here我找到了一种启用对象的私有(private)成员的javascript模块模式。如果我做对了,可以这样写:

var myObject1 = (function(){
var privateMember = 42;
return {
publicMember: function() {
return privateMember;
}
}
})();

但是还有一种更有效的方法:
var myObject2 = new function() {
var privateMember = 42;
this.publicMember = function() {
return privateMember;
}
}

两者之间有什么区别吗?还有其他实现私有(private)成员的可能性吗?

这是Chrome调试器的结果:

最佳答案

两者之间只有几个真正的技术差异(Bergi在评论中指出)。除非您要进行成千上万的处理,否则差异并不大。有一个风格上的差异,这是主观的,但是技术上的差异很小。有关血腥细节,请参见下面的分割。

在较高的级别上,两种形式都创建一个新对象,它们都依赖于私有(private)成员的闭包。第二种方法使用更多的内存,因为该函数在使用后不能被垃圾回收,而在第一种方法中可以。同样,在第一个版本中可以回收创建为函数的prototype属性的对象,而第二个版本则不能。第二种方式也许也比第一种方式不清楚(见证者来自@djechlin,下面的问题),这不仅是因为隐式的返回值,而且这是一个风格点。

再问第二个问题:

Are there any other possibilities to implement private members?



对于像您这样的一次性对象,您执行操作的方式可能是最好的(实际上是两种形式)。但是现在,还有另一种方法,从ES.Next(ECMAScript的下一个版本)开始,有一种新的真正的私有(private)方法。但是,在这两种情况下,当您执行对象类而不是一次性操作时,它都更有用。

this entry on my blog中有更多内容(还讨论了ES.Next附带的内容),但在这里我将介绍最重要的细节。

对象类中私有(private)成员的典型模式如下所示:
function Foo(toBePrivate) {
var privateMember = toBePrivate;

this.method1 = function() {
// ...do something with the truly private `privateMember`
};
}
Foo.prototype.method2 = function() {
// ...doesn't have access to the truly private `privateMember`
};

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() cannot

它的问题在于,通过 new Foo创建的每个对象都具有自己的 自己的 method1函数。我们没有获得与oj​​it_code一起获得的重用,那里只有 method2创建的所有对象共享它们中的一个。 (对于现代引擎而言,这不一定是一件大事,即使为每个 new Foo对象创建了一个新的 method1对象,这些引擎也能够重用 method1的代码。但是有些开发模式会动态更改原型(prototype),这很明显无法对上面的 Foo采取措施。)

这是我们今天可以执行的近乎私有(private)的模式:
var Foo = (function() {
// Create a private name object for our private property
var privateKey = makeRandomString();

// Our constructor
function Foo(toBePrivate) {
this[privateKey] = toBePrivate;
}

// Define the private property so it's non-enumerable
Object.defineProperty(Foo.prototype, privateKey, {
writable: true
});

// Methods shared by all Foo instances
Foo.prototype.method1 = function() {
// ...use this[privateKey] here...
};
Foo.prototype.method2 = function() {
// ...use this[privateKey] here...
};

return Foo;
})();

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() can too!
// Both `method1` and `method2` are *reused* by all `Foo` objects

... method1正是这样做的,每次调用它时,它都会为我们提供一个新的随机字符串。

我们创建的属性不是私有(private)的,但实际上是晦涩的。它不会出现在 makeRandomString循环中(因为我们创建的属性是不可枚举的),并且每次代码运行时其名称都会更改。因此,任何尝试使用私有(private)数据的代码都必须首先弄清楚属性名称,这是不平凡的练习,因为该代码无法获取对象的不可枚举的属性名称的列表。但是,自然而然地,在调试器中浏览一下该对象即可看到该属性及其值。该属性(property)确实是晦涩难懂的,但并不是十分真正的私有(private)。

ES.Next的新版本ECMAScript显着改善了这种模式。我们将获得 private name objects,它本身就很有用,并且也被 the new classses使用。

这是上面提到的私有(private)名称的用法:
// **ES.Next, not yet available in the wild**
import Name from "@name";
var Foo = (function() {
// Create a private name object for our private property
var privateKey = new Name();

function Foo(toBePrivate) {
this[privateKey] = toBePrivate;
}
Foo.prototype.method1 = function() {
// ...use this[privateKey] here...
};
Foo.prototype.method2 = function() {
// ...use this[privateKey] here...
};

return Foo;
})();

var f = new Foo("private stuff");
// f.method1() can use the private data
// f.method2() can too!
// Both `method1` and `method2` are *reused* by all `Foo` objects

使用私有(private)名称对象创建的属性永远不会出现在所有的 for-in枚举 中,并且它们的名称不是字符串。如果没有特定的名称对象,则代码不能访问名称为私有(private)名称对象的属性。由于上面的for-in变量是privateKey类完全私有(private)的,因此其他任何代码都不能使用该属性。它是完全私有(private)的。自然,这些将显示在调试器中,但是,调试器没有什么 secret 。

@djechlin要求详细分割私有(private)成员(member)的每种表单的工作方式,这有助于我们理解Bergi强调的表单之间的区别:

您的第一个示例:
var myObject1 = (function(){
var privateMember = 42;
return {
publicMember: function() {
return privateMember;
}
}
})();

(此列表列出了一些我认为不相关的细节。)
  • 在当前变量绑定(bind)对象(如果是全局的,则可能是Foo)上创建一个名为myObject1的属性,其值为window
  • 函数表达式被求值:
    A)一个要创建的undefined对象。
    B)创建一个空白对象,并将其分配给新函数的Function属性。
    C)在该对象上创建一个prototype属性,并给该函数一个引用。
    D)对当前变量绑定(bind)对象的引用存储在函数中。
  • 调用该函数,除其他外,为调用的执行上下文创建变量绑定(bind)对象。
  • 创建一个名为constructor的属性,并将其分配给步骤3中的变量绑定(bind)对象。
  • 值42被分配给VBO的privateMember属性。
  • 创建一个空白对象,并根据privateMember提供原型(prototype)。
  • 对内部函数表达式进行求值,创建一个Object.prototype对象(其Function属性为空白对象,并在该对象上放置prototype属性,并引用当前变量绑定(bind)对象[来自步骤3的对象])。
  • 将该函数作为constructor分配给第5步中空白对象的属性。
  • 从主匿名函数返回对步骤6中对象的引用。
  • 该对象引用存储在步骤1中创建的publicMember属性中。
  • 主要的匿名函数(来自第2步)没有未完成的引用,因此可以由GC进行回收。因此,由其myObject1属性引用的对象也可以由GC回收。

  • 您的第二个示例:
    var myObject2 = new function() {
    var privateMember = 42;
    this.publicMember = function() {
    return privateMember;
    }
    }

    (同样,遗漏了一些无关的细节。)
  • 在当前变量绑定(bind)对象(如果是全局的,则可能是prototype)上创建一个名为myObject2的属性,其值为window
  • 函数表达式求值:
    A)一个要创建的undefined对象。
    B)创建一个空白对象,并将其分配给新函数的Function属性。
    C)在该对象上创建一个prototype属性,并给该函数一个引用。
    D)对当前变量绑定(bind)对象的引用存储在函数中。
  • 将创建一个新的空白对象,并从匿名函数的constructor属性为其分配原型(prototype)。
  • 调用该函数,并将步骤3中的对象作为prototype传入,并(除其他外)为调用的执行上下文创建变量绑定(bind)对象。
  • 创建一个名为this的属性,并将其分配给步骤4中的变量绑定(bind)对象。
  • 值42被分配给VBO的privateMember属性。
  • 对内部函数表达式进行求值,创建一个privateMember对象(其Function属性为空白对象,并在该对象上放置prototype属性,并引用当前变量绑定(bind)对象[步骤4中的对象])。
  • 将该函数作为constructor分配给第5步中空白对象的属性。
  • 该函数返回,因为它不返回任何对象,所以publicMember表达式的结果是对在步骤3中创建的对象的引用。
  • 该对象引用存储在步骤1中创建的new属性中。
  • GC无法回收主要的匿名函数(来自第2步),因为myObject2的基础原型(prototype)在myObject2属性上具有对其的引用(因此该函数以及分配给其constructor属性的对象都保留在内存中)。

  • 您可以通过在函数中添加以下行来释放该函数(但不分配给其prototype属性的对象):
    delete this.constructor.prototype.constructor;

    这将从分配给其prototype属性的对象中删除对该函数的引用。该对象仍然是prototype的基础原型(prototype),但不再引用该函数,因此该函数适用于GC。

    但是到那时,我们已经进入默默无闻的领域。 :-)

    结论

    因此它们几乎相同,只是主要匿名函数及其myObject2属性上的对象不符合GC条件。在现实世界中,要花上成千上万的钱才有意义。

    (旁注:有些实现可能会推迟创建函数的某些步骤-例如为其prototype属性创建一个空白对象并设置其prototype--直到/除非使用了constructor属性,因为当然在绝大多数情况下情况下,它从未使用过,因为绝大部分函数都从未用作构造函数。因此,您的第一种形式可能会略微提高一点效率,因为它可以跳过这些步骤。重新做成千上万个。)

    FWIW,如果您关心的是行数,括号数或不喜欢对象字面量等,则第一个也可以这样写:
    var myObject1 = function(){
    var obj = {};
    var privateMember = 42;
    obj.publicMember = function() {
    return privateMember;
    };
    return obj;
    }();

    就样式而言,我更喜欢显式 yield ,但这是样式问题。

    关于javascript - 与私有(private)成员创建对象时的技术差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16552991/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com