使用 CoffeeScript 在“公共函数”类中获取“私有方法”

Posted

技术标签:

【中文标题】使用 CoffeeScript 在“公共函数”类中获取“私有方法”【英文标题】:Getting “private method” in a “public function” class using CoffeeScript 【发布时间】:2013-07-28 18:50:15 【问题描述】:

我正在使用类和 CoffeeScript/javascript 进行一系列测试。见以下代码:

class Example

    someFunction = ->
        alert @getText()

    constructor: ->
        @text = 'Hello world! ;)'
        someFunction()

    getText: ->
        @text


### Instance ###
example = new Example

这只是一个例子,编译时出现错误:

Uncaught TypeError: Object [object global] has no method 'getText'

你知道我该如何解决这个问题吗? http://jsfiddle.net/P4Xdz/

【问题讨论】:

没有Example.getText(),但有@getTextthis.getText()。看看编译好的 JS 就知道是怎么回事了。 @elclanrs 我试过了,没用,你也可以试试:jsfiddle.net/uJ9xd 您声明了Example.text,但访问了this.text。看编译代码gist.github.com/elclanrs/6102222 @elclanrs 你在这一点上是对的,但这仍然不能解决我的问题。我更新了问题。 jsfiddle.net/P4Xdz 我知道你在做什么,但不确定它会起作用。 getText 是实例方法,不能这样访问。 【参考方案1】:

如果你真的想做这种事情,你必须手动提供正确的@(又名this)和callapply

constructor: ->
    @text = 'Hello world! ;)'
    someFunction.call(@)

演示:http://jsfiddle.net/ambiguous/6KZrs/

问题在于someFunction 不是任何一种方法,它只是一个简单的函数。如果您需要它表现得像一个方法,那么您必须在调用它时通过提供所需的@ 手动“方法化”它。这(和epidemian)提出了一种替代方法:明确地将对象作为参数传递:

someFunction = (ex) ->
    console.log ex.getText()

constructor: ->
    @text = 'Hello world! ;)'
    someFunction(@)

演示:http://jsfiddle.net/ambiguous/hccDr/

请记住,JavaScript 中没有公共或私有,因此 CoffeeScript 中也没有公共或私有。您可以伪造它,但伪造有漏洞并且往往需要更多的诡计(例如手动提供@call)才能使其工作。如果您查看代码的 JavaScript 版本,您会发现 someFunction 就是这样:

var someFunction = function()  ... ;

只是变量中的一个函数,作用域为类函数,仅此而已。还要记住,由于someFunctionExample 类函数的本地函数,因此它不会以任何方式对子类可见。

【讨论】:

或者,或者,您可以让闭包可见函数(又名“私有”)采用参数,而不是在其中使用 thissomeFunction = (ex) -> alert ex.getText() @epidemian:没错,总体上可能更清楚一点:将函数视为函数,而不是使用诡计来假装它是一种方法。 是的。当我不想将某些函数作为对象的成员公开时,我通常会这样做(只是简单地使用独立函数)。太糟糕了,没有简单的方法可以对数据属性做同样的事情。【参考方案2】:

这可能很明显,但是...从概念上讲,coffescript 不能做任何你在 javascript 中做不到的事情。现在您的 someFunction 定义是一个局部变量,并且没有声明为实例上的属性(与 getText 不同)。

当您在 someFunction 中使用“@”时,我假设您希望它引用 Example 的实例,这在您的情况下会很方便,但是 someFunction 没有在示例中定义。

如果您使用 => 表示法,它仍然不会将其绑定到实例(它将引用类函数)。现在这可能看起来不方便,或者是一个奇怪的设计选择,但它实际上是一致的。再一次, someFunction 不在实例上,它被定义为 Example 类函数中的局部变量。

如果你使用 ->,'@' 指的是那个函数的 javascript 'this'(它是局部变量,显然不包含 getText)。如果您使用 => 它在定义时引用 javascripts 'this',此时它是 Example 类函数。您想要引用的 Example 实例甚至还没有创建(尽管您希望引用它)。

@ 引用 getText 等函数中的示例实例的原因是因为 JavaScript 中的 this 关键字引用了您定义的对象。 Coffeescript 确实没有什么不同,除了在函数定义时为您提供一种方便的语法来引用“this”。

TLDR:

您无法真正实现您的目标,并且您可能不得不放弃在实例上使用“私有”函数的想法 我能看到你做的最好的就是你在上面的 cmets 中已经描述的 Example.prototype.getText() 因为您可以引用此方法的两种方式是通过实例和 Example.prototype(函数在其上定义)。由于您的方法未在实例上定义,因此您不能使用“this”。但是,如果您从原型调用该方法,您的 getText 函数无论如何都会失败。

getText: ->
        @text

@text 指的是 getText 的定义,在这种情况下它是原型(而不是实例)。并且原型上的文本是未定义的。

如果你想让这个方法按照你期望的方式运行,你可能不得不让它不是“私有的”。 Javascript/Coffeescript 没有 public 和 private 之类的访问修饰符,私有方法实际上是在特定范围内定义的函数。在这种情况下,该范围无法访问您想要的内容,并且 this 关键字没有引用您需要的内容。

【讨论】:

【参考方案3】:
    您使用的是someFunction = 而不是someFunction:。这不会达到您的预期。 你打电话给someFunction,而实际上你可能想打电话给@someFunction

【讨论】:

我真的很想使用someFunction =。而且我不能使用this @Caio:你希望如何确定你想引用哪个Exampletext 我使用Example.prototype.getText() 获得了访问权限。我不确定这是否是最好的方法。 @Caio:这将使您可以访问getText,但是当您调用它时,您仍然需要给它一个this。如果你不给它一个合理的this,它将引用不是Example 实例的东西,它可能没有text【参考方案4】:

按照您编写示例的方式,“someFunction”是一个匿名函数,没有绑定到任何东西。所以,'someFunction's 'this' 绑定到全局对象,这解释了你的错误。您可以通过使用粗箭头定义“someFunction”并将“someFunction”放置在示例的构造函数中来修复它。这将导致“someFunction”绑定到您的 Example 实例。如果您要使用粗箭头绑定“someFunction”,但将其留在原来的构造函数之外,则“someFunction”将绑定到 Example 构造函数,导致“someFunction”调用不存在的静态方法 --getText -- 例如。

以下是消除错误的方法:

class Example
    constructor: ->
        someFunction = =>
            alert @getText()

        @text = 'Hello world! ;)'
        someFunction()

    getText: =>
        @text


### Instance ###
example = new Example

【讨论】:

以上是关于使用 CoffeeScript 在“公共函数”类中获取“私有方法”的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 A 类中声明为友元的函数必须在 B 类中定义为公共函数?

如何在 ES6 类中声明公共函数?

CoffeeScript 中的私人成员?

放置在另一个文件中的子类的继承(CoffeeScript)

公共函数与 CodeIgniter 中的函数

是否/为啥 Rails 6 仍在使用/推荐 CoffeeScript?