javascript无法访问私有媒体资源

时间:2014-09-26 14:22:47

标签: 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(),但肯定不是正确的方法吗?会喜欢任何帮助。

3 个答案:

答案 0 :(得分:28)

那是对的。部分原因是因为javascript没有私有属性。你正在做的不是宣布私人财产。您正在使用使用闭包来模拟私有属性的设计模式。

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

因此,在讨论闭包之前,我们先简单讨论一下范围。

筹码:

范围与堆栈框架相关(在计算机科学中它被称为"激活记录"但是大多数熟悉C或汇编的开发人员更好地将其作为堆栈框架知道)。范围是堆栈框架类对象的类型。我的意思是,在对象是类的实例的情况下,堆栈帧是范围的实例。

让我们用一种虚构的语言作为例子。在这种语言中,就像在javascript中一样,函数定义范围。让我们看看示例代码:

var global_var

function b {
    var bb
}

function a {
    var aa
    b();
}

当我们阅读上面的代码时,我们说变量aa在函数a的范围内,而变量bb在函数b的范围内。请注意,我们不会将此事称为私有变量。因为私有变量的反义是公共变量,并且都引用绑定到对象的属性。相反,我们会调用aabb local variables。局部变量的反面是全局变量(不是公共变量)。

现在,让我们看看当我们致电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 */ ┆
 ╞════════╡           └╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶┘
 ┆        ┆

正如您所看到的,由于b()所属的范围无法访问它,因此无法从调用c重新获得对堆栈框架的访问权限

解决方法:

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

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

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

答案 1 :(得分: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}

答案 2 :(得分:0)

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

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

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