javascript无法访问私有属性

我有以下代码,我不明白为什么我重新声明get方法时不能访问私有属性。

(function(w,d,a,undefined){ var cfg = { currency: 'GBP', exponent: 2 }; var get = function () { return cfg; }; a.init = function (settings) { for (var k in settings) { cfg[k] = settings[k]; } }; a.set = function (args) { get = args.get || get; //eval(args) //works but why?? }; a.get = function () { return get(); }; })(window,document,window.fxc = {}); fxc.init({currency: 'EUR'}); // prints, Object { currency="EUR", exponent=2} console.log(fxc.get()); fxc.set({get: function(msg){ // cannot access private properties return cfg; }}); // prints, undefined console.log(fxc.get()); 

我一直在试图find正确的方法来做这个一段时间,但我似乎无法find正确的组合。 我有eval() ,但肯定不能是正确的方法? 会爱任何帮助。

这是正确的。 部分是因为JavaScript没有私有属性。 你在做什么不是宣布私人财产。 您正在使用使用闭包来模拟私有属性的devise模式。

闭合演变超出了范围。 范围是指variables的生命周期,而对象属性是指variables的绑定。

所以在讨论封闭之前,我们先简要讨论一下范围。

堆栈:

范围与堆栈框架相关(在“计算机科学”中称为“激活logging”,但大多数熟悉C或汇编程序的开发人员更熟悉堆栈框架)。 一个范围是一个堆栈框架类是一个对象。 我的意思是说,一个对象是一个类的实例,一个堆栈框架是一个范围的实例。

我们以一个制作好的语言为例。 在这个语言中,就像在javascript中一样,函数定义范围。 让我们看一个示例代码:

 var global_var function b { var bb } function a { var aa b(); } 

当我们读取上面的代码时,我们说variablesaa在函数a中的范围内,variablesbb在函数b中的范围内。 请注意,我们不把这个事情称为私有variables。 因为私有variables的相反是公共variables,并且都指向绑定到对象的属性。 相反,我们调用aabb 局部variables 。 局部variables的对立面是全局variables(不是公共variables)。

现在,让我们看看当我们调用a

a()被调用,创build一个新的栈帧。 为堆栈中的局部variables分配空间:

 The stack: ┌────────┐ │ var aa │ <── a's stack frame ╞════════╡ ┆ ┆ <── caller's stack frame 

a()调用b() ,创build一个新的栈帧。 为堆栈中的局部variables分配空间:

 The stack: ┌────────┐ │ var bb │ <── b's stack frame ╞════════╡ │ var aa │ ╞════════╡ ┆ ┆ 

在大多数编程语言中,这包括javascript,一个函数只能访问它自己的栈帧。 因此a()不能访问b()局部variables,全局作用域内的任何其他函数或代码也不能访问b()variables。 唯一的例外是全局范围内的variables。 从实现的angular度来看,这是通过在不属于堆栈的内存区域中分配全局variables来实现的。 这通常被称为堆。 所以要完成这个画面,这个内存看起来像这样:

 The stack: The heap: ┌────────┐ ┌────────────┐ │ var bb │ │ global_var │ ╞════════╡ │ │ │ var aa │ └────────────┘ ╞════════╡ ┆ ┆ 

(作为一个方面说明,你也可以使用malloc()或new)在函数内部的堆上分配variables,

现在b()完成并返回,它的栈帧被从堆栈中删除:

 The stack: The heap: ┌────────┐ ┌────────────┐ │ var aa │ │ global_var │ ╞════════╡ │ │ ┆ ┆ └────────────┘ 

a()完成相同的事情发生在其堆栈帧。 这就是本地variables如何自动分配和释放 – 通过推送和popup堆栈中的对象。

闭包:

闭包是一个更高级的堆栈框架。 但是,一旦函数返回,普通的堆栈框架就会被删除,一个闭包的语言只会在堆栈框架(或者它所包含的对象)与堆栈之间进行链接,而只要需要的时候就保持对堆栈框架的引用。

现在我们来看一个闭包语言的示例代码:

 function b { var bb return function { var cc } } function a { var aa return b() } 

现在让我们看看如果我们这样做会发生什么:

 var c = a() 

首先调用函数a() ,然后调用b() 。 堆栈帧被创build并压入堆栈:

 The stack: ┌────────┐ │ var bb │ ╞════════╡ │ var aa │ ╞════════╡ │ var c │ ┆ ┆ 

函数b()返回,所以它的堆栈框架被popup堆栈。 但是,函数b()返回一个匿名函数,它在闭包中捕获bb 。 所以我们popup堆栈框架,但不要从内存中删除它(直到所有引用都被完全垃圾回收):

 The stack: somewhere in RAM: ┌────────┐ ┌╶╶╶╶╶╶╶╶╶┐ │ var aa │ ┆ var bb ┆ ╞════════╡ └╶╶╶╶╶╶╶╶╶┘ │ var c │ ┆ ┆ 

a()现在将函数返回给c 。 所以调用b()的栈帧被链接到variablesc 。 请注意,这是链接的堆栈框架,而不是范围。 这就好比如果你从类创build对象,它是分配给variables的对象,而不是类:

 The stack: somewhere in RAM: ┌────────┐ ┌╶╶╶╶╶╶╶╶╶┐ │ var c╶╶├╶╶╶╶╶╶╶╶╶╶╶┆ var bb ┆ ╞════════╡ └╶╶╶╶╶╶╶╶╶┘ ┆ ┆ 

还要注意,因为我们还没有真正调用函数c() ,variablescc还没有被分配到内存中的任何地方。 它只是一个范围,而不是一个堆栈框架,直到我们调用c()

现在当我们调用c()时会发生什么? c()堆栈框架正常创build。 但这次有一个区别:

 The stack: ┌────────┬──────────┐ │ var cc var bb │ <──── attached closure ╞════════╤──────────┘ │ var c │ ┆ ┆ 

b()的堆栈帧被附加到c()的堆栈帧。 所以从函数c()的angular度来看,它的栈也包含了调用函数b()时创build的所有variables(再次注意,函数b()中不是variables,而函数b()换句话说,不是b()的作用域,而是调用b()时创build的堆栈框架,其含义是只有一个可能的函数b(),但是多次调用b()来创build多个堆栈帧)。

但是局部和全局variables的规则仍然适用。 b()所有variables都成为c()局部variables,而不是别的。 调用c()的函数不能访问它们。

这意味着当你在调用者的范围内重新定义c

 var c = function {/* new function */} 

这发生了:

  somewhere in RAM: ┌╶╶╶╶╶╶╶╶╶┐ ┆ var bb ┆ └╶╶╶╶╶╶╶╶╶┘ The stack: ┌────────┐ ┌╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶┐ │ var c╶╶├╶╶╶╶╶╶╶╶╶╶╶┆ /* new function */ ┆ ╞════════╡ └╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶┘ ┆ ┆ 

正如你所看到的,由于c所属的范围无权访问,因此不可能从调用b()重新获得对栈帧的访问。

解决方法:

解决方法,因为这是一个绑定(JavaScript调用它的上下文)问题不是范围问题,是使用对象绑定来存储您的cfg对象。

不幸的是,JavaScript没有私有variables。 所以只能把它作为一个公共variables进行绑定。 解决此问题的解决方法是使用Perl约定来告诉其他程序员不要触摸该对象,除非他们正在修改实现本身。 那个约定是用下划线开始一个variables名:

 // WARNING: Private! a._cfg = { currency: 'GBP', exponent: 2 }; 

那么,你刚刚说了,不能访问私人财产。 IIFE中定义的variables不能从外部定义的函数访问。

如果你不想公开cfgvariables,也许你可以这样做:

 (function(w, d, a, undefined) { var cfg = { currency: 'GBP', exponent: 2 }; var get = function() { return cfg; //must return cfg }; a.init = function(settings) { for (var k in settings) { cfg[k] = settings[k]; } }; a.set = function(args) { get = args.get(get) || get; }; a.get = function() { return get(); }; })(window, document, window.fxc = window.fxc || {}); fxc.set({ get: function(initialGet) { return function(msg) { var cfg = initialGet(); console.log('custom get'); return cfg; }; } }); console.log(fxc.get()); //custom get //{currency: "GBP", exponent: 2} 

Javascript没有私有属性。 如果我从第二个脚本标记中正确理解了您的问题,则需要访问在第一个脚本标记中声明的“cfg”对象。

为此,您需要了解闭包是如何工作的。 在这种情况下,您在匿名函数中声明了“cfg”作为variables。 这意味着在该范围中声明的其他函数将可以访问“cfg”对象,但是,它不能从任何其他上下文中运行。

要解决这个问题,请将var cfg =replace为a.cfg =