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());

我一直在努力寻找正确的方法来做这件事,但我似乎无法找到正确的组合。 我确实有eval() ,但肯定不能是正确的方式? 会爱任何帮助。


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

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

因此,在讨论闭包之前,让我们对范围进行简短的讨论。

堆栈:

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

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

var global_var

function b {
    var bb
}

function a {
    var aa
    b();
}

当我们读取上面的代码时,我们说变量aa在函数a中的作用域中,变量bb在函数b中的作用域中。 请注意,我们不会将这个事物称为私有变量。 因为私有变量的相反是公共变量,并且都指向绑定到对象的属性。 相反,我们调用aabb局部变量。 局部变量的对立面是全局变量(不是公共变量)。

现在,让我们看看当我们调用a时会发生什么:

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

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

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

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

在大多数编程语言中,这包括JavaScript,一个函数只能访问它自己的堆栈框架。 因此a()无法访问本地变量b()和既不可以在全球范围内访问的变量的任何其他功能或代码a() 唯一的例外是全球范围内的变量。 从实现的角度来看,这是通过在不属于堆栈的内存区域中分配全局变量来实现的。 这通常称为堆。 所以要完成这个图片,在这一点上的内存看起来像这样:

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

(作为一个附注,你也可以使用malloc()或new来在函数内部的堆上分配变量)

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

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

a()完成相同的事情发生在它的堆栈帧。 这就是本地变量如何自动分配和释放 - 通过推送和弹出堆栈中的对象。

闭包:

闭包是一个更高级的堆栈框架。 但是,一旦函数返回,普通的堆栈框架就会被删除,一个带有闭包的语言只会在堆栈框架(或者它所包含的对象)与堆栈之间的链接关系不变的情况下进行,只要需要的话就可以保持堆栈框架的引用。

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

function b {
    var bb
    return function {
        var cc
    }
}

function a {
    var aa
    return b()
}

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

var c = a()

调用第一个函数a() ,然后调用b() 。 堆栈帧被创建并推送到堆栈上:

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

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

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

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

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

还要注意,由于我们没有实际调用函数c() ,变量cc尚未分配到内存中的任何位置。 在我们调用c()之前,它目前只是一个范围,而不是一个堆栈框架。

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

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

b()的堆栈帧附加到c()的堆栈帧。 所以从函数c()的角度来看,它的栈也包含了调用函数b()时创建的所有变量(再次注意,不是函数b()中的变量,而是函数b()所谓的 - 换句话说,不是b()的作用域,而是调用b()时创建的栈帧,其含义是只有一个可能的函数b(),但是很多调用b()创建了许多栈帧)。

但是局部和全局变量的规则仍然适用。 b()所有变量都成为c()局部变量,而没有其他变量。 调用c()的函数无法访问它们。

这意味着当你在调用者的范围中重新定义c ,就像这样:

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

有时候是这样的:

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

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

解决方法:

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

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

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

那么,你刚刚说过,不能访问私有财产。 在IIFE中定义的变量不能从外部定义的函数访问。

如果你不想公开提供cfg变量,也许你可以这样做:

(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”作为变量。 这意味着您在该范围内声明的其他函数将有权访问“cfg”对象,但是,它不会从任何其他上下文中运行。

要解决此问题,请将var cfg =替换为a.cfg =

链接地址: http://www.djcxy.com/p/84357.html

上一篇: javascript can't access private properties

下一篇: X86 Allocation of Space on Stack for Local Variables