【问题描述】:

我有这门课:

function ctest() {
    this.var1 = "haha";
    this.func1 = function() {
        alert(this.var1);
        func2();
        alert(this.var1);
    }
    var func2 = function() {
        this.var1 = "huhu";
    }
}

并称之为:

    var myobj = new ctest();
    myobj.func1();

不应该第二个警报会弹出“huhu”吗? func2 是私有的,不能访问 var1 的公共变量吗?

如果私有函数无法访问公共变量,我该怎么办?

提前致谢!

【问题讨论】:

标签: javascript class private public


【解答1】:

您需要为调用 func2 提供上下文:

this.func1 = function() {
    alert(this.var1);
    func2.call(this);
    alert(this.var1);
}

如果没有上下文,调用将使用全局对象(即 window) - 当您运行当前代码时,您应该看到 window.var1 在两个警报。

【问题讨论】:

  • 如果我按照您的建议进行操作,则表明 func2() 不是函数
  • @MIrrorMirror 啊,嗯,是的 - 等等!
  • 那行不通:func2 是私有变量,而不是属性。
  • @xdazz 现已修复 - 您必须使用 .call 将上下文添加到本地范围的函数。
  • @MIrrorMirror 否,.call 的第一个参数设置函数内隐含的 this 值。
【解答2】:

函数与实例无关,因此您对 func2 的调用最终会作为调用而没有 this 指向预期的实例。

您可以修复调用以包含上下文:

function ctest() {
    this.var1 = "haha";
    this.func1 = function() {
        alert(this.var1);
        func2.call(this);
        alert(this.var1);
    }
    var func2 = function() {
        this.var1 = "huhu";
    }
}

或者您可以保留一个带有所需对象引用的变量:

function ctest() {
    var that = this;
    this.var1 = "haha";
    this.func1 = function() {
        alert(this.var1);
        func2();
        alert(this.var1);
    }
    var func2 = function() {
        that.var1 = "huhu";
    }
}

【问题讨论】:

【解答3】:

另一种选择是使用Function.bind

function ctest() {
    this.var1 = "haha";
    this.func1 = function() {
        alert(this.var1);
        func2();
        alert(this.var1);
    }
    var func2 = function() {
        this.var1 = "huhu";
    }.bind(this);
}

请注意,浏览器对此的支持并不完美。

【问题讨论】:

  • 为什么要同时使用that .bind()
  • @Alnitak:糟糕的复制和粘贴
【解答4】:

在 JS 中没有私有这样的东西,但是你可以使用 closures 来玩作用域。

例如,假设在您的示例中,您不需要将 var1 公开为公共属性。您可以轻松地重写您的代码,如下所示:

function ctest() {
    var var1 = "haha";

    this.func1 = function() {
        alert(var1);
        func2();
        alert(var1);
    }

    var func2 = function() {
        var1 = "huhu";
    }
}

因为 func1func2 共享相同的范围——它们被定义在相同的函数中,ctest——它们可以访问相同的变量。当然,在这种情况下,您没有暴露 var1,因此: myobj.var1 将是 undefined

如果您希望 var1 作为属性公开,那么您需要的是 bind func2 到您在构造函数中创建的对象实例:

function ctest() {
    this.var1 = "haha";
    this.func1 = function() {
        alert(this.var1);
        func2();
        alert(this.var1);
    }
    var func2 = function() {
        this.var1 = "huhu";
    }.bind(this);
}

否则 func2 将有不同的上下文对象 (this)。如果浏览器不支持 bind 并且您不想使用 shim(如上面的链接所示),您可以再次利用闭包:

function ctest() {
    this.var1 = "haha";
    this.func1 = function() {
        alert(this.var1);
        func2();
        alert(this.var1);
    }
    var context = this;
    var func2 = function() {
        context.var1 = "huhu";
    }
}

IMVHO 不那么优雅。

【问题讨论】:

    【解答5】:
    function ctest() {
        this.var1 = "haha";
        this.func1 = function() {
            alert(this.var1);
            func2(this);
            alert(this.var1);
        }
        var func2 = function(obj) {
            obj.var1 = "huhu";
        }
    }
    

    还有其他方法可以解决这个问题,请查看其他答案:)

    【问题讨论】:

    • @Alnitak 但这不会有问题,obj 是参数。