使JavaScript私有方法可供其公共方法访问

Making JavaScript private methods accessible to its public methods

本文关键字:方法 访问 JavaScript 有方法      更新时间:2023-09-26

我知道有几种模式可以使JavaScript"类化">
我想采取"原型扩展"的方式。。。只是因为它看起来更整洁。我不太担心这里的表现
在下面的例子中,我有一个类(基本上是函数(MetricsChart。我有两个公共方法和一个私有方法(基本上是一个可重用的方法(
在这里,从公共方法(drawOrAdd(我无法访问私有方法(_convertArrayToTable(,我该怎么做

function MetricsChart(containerId, chartType) {
    this._container = document.getElementById(containerId);
    this._chartType = chartType;
    this._isChartDrawn = false;
    this._chartData = null;
    var _convertArrayToTable = function (data) {
        return google.visualization.arrayToDataTable(data);
    }
}
MetricsChart.prototype.drawOrAdd = function(data)
{
    if (!this.isChartDrawn()) {
        var chart = new google.visualization.LineChart(this._container);
        if (chart) {
            var table = _convertArrayToTable(data);
            console.log(table);
            this._isChartDrawn = true;
        }
    }
}
MetricsChart.prototype.isChartDrawn = function () {
    return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}

我偶然发现的一种方法是将公共方法封装在MetricsChart类本身中
它对我有效:(:我可以访问外部的公共方法,公共方法可以访问私有方法(达到目的(。以下代码这样对吗?我做错什么了吗

function MetricsChart(containerId, chartType) {
    this._container = document.getElementById(containerId);
    this._chartType = chartType;
    this._isChartDrawn = false;
    this._chartData = null;
    var _convertArrayToTable = function (data) {
        return google.visualization.arrayToDataTable(data);
    }
    MetricsChart.prototype.drawOrAdd = function (data) {
        if (!this.isChartDrawn()) {
            var chart = new google.visualization.LineChart(this._container);
            if (chart) {
                var table = _convertArrayToTable(data);
                console.log(table);
                this._isChartDrawn = true;
            }
        }
    }
    MetricsChart.prototype.isChartDrawn = function () {
        return this._isChartDrawn;
    }
    MetricsChart.prototype.getChartData = function () {
    }
}

因此,这里有几件事,以便准确地了解您所做的工作。首先:

function foo() {
    var i = 0;
    function bar() {
        return true;
    }
}

这里发生的事情:每次调用函数foo时,它都会在其作用域中创建一个新变量i和一个新函数bar。函数bar和变量i在其作用域内,这意味着它们是本地:使用此代码,无法访问函数foo外部的ibar。还因为,一旦功能foo终止,ibar都被布置。

所以,这就是为什么你不能从"公共"方法访问"私人"方法,我希望现在更清楚了。函数访问函数或变量的唯一方法是在同一范围内共享一个引用。因此,这就是您在上一个示例中所做的:在定义"私有"方法的相同范围内定义"公共"方法。通过这种方式,他们可以相互访问。然而,你的做法有很大的缺点。正如我之前所说,每次调用函数foo时,都会创建函数bar。在"类"示例中,它的意思是:

function MyClass() {
    function myprivate() {}
    MyClass.prototype.mypublic = function () { return myprivate() }
}

这意味着,每次创建MyClass的实例时,都要创建两个新函数,并且要一直重写"类"的原型。这远不是一个好办法。事实上,如果你有这样的东西:

var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // false
console.log(_mypublic === a.mypublic) // false too!

所以,你猜对了,但你执行错了。这里你需要的是一个"模块模式":现在你可以在nodejs中使用CommonJS模块,或者在浏览器中使用AMD等等,但基本思想是定义一个"范围",并从这个范围导出你想要的东西。在你的情况下,你可以有:

// this is your "module"
;(function(exports) {
    // your local (private) function
    var _convertArrayToTable = function (data) {
        return google.visualization.arrayToDataTable(data);
    }
    function MetricsChart(containerId, chartType) {
        this._container = document.getElementById(containerId);
        this._chartType = chartType;
        this._isChartDrawn = false;
        this._chartData = null;
    }
    MetricsChart.prototype.drawOrAdd = function(data) {
        if (!this.isChartDrawn()) {
            var chart = new google.visualization.LineChart(this._container);
            if (chart) {
                var table = _convertArrayToTable(data);
                console.log(table);
                this._isChartDrawn = true;
            }
        }
    }
    // you decided to exports to the main scope what you want
    exports.MetricsChart = MetricsChart;
}(this)); // here `this` is the global object

就是这样。您已经使用"模块模式"创建了一个闭包,并且可以从"public"方法访问"private"函数,因为它们在同一范围内定义。但是,因为在"类"构造函数中没有这样做,所以不会在每次实例化新对象时重新定义它们。因此,前面以这种方式编写的示例,将给出正确的结果:

var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // true
console.log(_mypublic === a.mypublic) // true

你所做的并不一定是"错误的"。。。只是看起来很奇怪。此外,在创建"MetricsChart"实例之前,您将无法访问"MetricsCChart.protype.*"。根据您使用此对象的方式,它可能无关紧要。

话虽如此,另一种方法是保留原始结构,但将以下内容移出构造函数:

var _convertArrayToTable = function (data) {
    return google.visualization.arrayToDataTable(data);
}

它对您的模块仍然是私有的,这应该足够好(您正在使用模块,对吗?(。

您所做的一切都非常完美。

在任何OOP语言中,您都不能继承私有方法来覆盖它们或直接访问它们。它们是私人的。因此,为了继承的目的而对它们进行原型化是没有意义的。您已经将它们封装在函数范围中,因此它们是需要的"私有"。

要访问私有方法,请使用特权方法。检查此文档:http://javascript.crockford.com/private.html.

关于您的代码,请检查此答案:在对象类声明中设置javascript原型函数

p.s.

    function Test()
    {
        var p = function(pv)
        {
            //
        }
        this.e = function (ap) { p(ap) }
    }
    var n = new Test();
    n.e("e"); // It is legal call
    n.p();    // will throw

但若您在c-tor中声明了一个私有函数,它将在第一次创建该类型的对象时执行。当在原型中声明一个方法时,这些方法将在任何代码执行之前添加。通常,浏览器首先检查js文件,收集原型的所有方法,然后执行任何代码。因此,当您在c-tor中声明原型方法时,只有在第一次创建这些类型的对象之后,这些方法才可用。(对不起我的英语(。

检查这种情况:

        function Test()
    {
        alert(this.ST_A);//alert undefined
        alert(this.ST_B);//alert 2
        Test.prototype.ST_A = 1;
        alert( this.ST_A)//alert 1
    }
    Test.prototype.ST_B = 2;

在第一次通过中,浏览器将用ST_B填充Test,ST_B将在任何时间的任何地方可用。超过一秒后,浏览器将开始执行代码,此时ST_A将不可见,直到浏览器执行Test.prototype.ST_A=1;