2

我知道有几种模式可以使 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 () {
    }

}
4

4 回答 4

4

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

function foo() {
    var i = 0;

    function bar() {
        return true;
    }
}

这里发生了什么:每次foo调用函数时,它都会在其范围内创建一个新变量i和一个新函数bar。函数bar和变量i在其范围内,这意味着它们是本地的:使用此代码,无法访问函数中的任何一个ibar 外部foo。也是因为,一旦函数foo终止,两者都ibar处理掉了。

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

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
于 2013-10-17T07:24:40.723 回答
0

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

关于你的代码检查这个答案: 在对象类声明中设置javascript原型函数

ps

    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 填充测试,并且 ST_B 将随时随地可用。在第二遍之后,浏览器将开始执行这段时间内的代码 ST_A 将不可见,直到浏览器执行 Test.prototype.ST_A = 1;

于 2013-10-17T06:54:55.917 回答
0

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

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

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

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

于 2013-10-17T06:58:53.723 回答
0

你所做的工作完美。

在覆盖它们或直接访问它们方面,您不能在任何 OOP 语言中继承私有方法。他们是私人的。因此,将它们原型化以用于继承目的是没有意义的。您已将它们包装在函数范围内,因此它们是所需的“私有”。

于 2013-10-17T07:01:01.960 回答