【问题标题】:Why can't class methods call other class methods or themselves?为什么类方法不能调用其他类方法或自己?
【发布时间】:2019-11-15 00:01:07
【问题描述】:

我很好奇为什么方法不能在 javascript 中调用其他方法或它们自己。例如,这会产生一个引用错误,说明 add is not defined。

class sum {
  add(x, amt) {
    if(amt == 0) return x
    return add(x+1, amt-1)
  }
}
summer = new sum()
console.log(summer.add(5,5))

您必须改用 this.add()。

现在我知道方法会被转换为原型上的函数,但我不明白这如何解释我指出的这个限制?

在定义 add 时,它可能引用自身或其他带有闭包捕获的方法,这不是一个原因。

为什么会这样?

【问题讨论】:

  • 你必须来自Java....(另外,add 不是类方法,它是实例方法)
  • 如果您有一个返回a-b 的全局add 函数并想使用它,该怎么办? jsfiddle.net/gh97ynt3 之所以这样,是因为 global 变量在 javascript 中的工作方式。
  • 因为这就是语言的定义方式,句号。规则与其他语言不同。
  • 对象方法呢? { foo() { bar() }, bar() {} } ?如果决定遵循您的规则的权力,他们要么必须使对象方法与类方法工作相同(以保持一致性),但对象方法和对象函数属性({foo: function() {}})中的范围规则将不一致,或者他们会不理会对象,并且在对象和类方法之间存在不一致。通过使方法(大部分)像任何其他函数一样工作,它们可以避免这种混淆。
  • 一般来说,很少有“硬”的理由说明必须以某种方式完成某事。大多数设计决策都是权衡取舍,并且任何因素都可能影响该决策(易于实施、设计一致性、最小意外原则、历史背景、其他语言的优先级、向后兼容性等)。

标签: javascript ecmascript-6 ecmascript-2017


【解决方案1】:

@briosheje 发表评论时,我正在说明这个缺点:

function add() {
   console.log('called the wrong add!');
}

class sum {
  add(x, amt) {
    if(amt == 0) return x
    return add(x+1, amt-1)
  }
}
summer = new sum()
console.log(summer.add(5,5))

【讨论】:

  • (附带说明,add 可以是字面上的任何东西,而不仅仅是一个函数)
  • @Adam 好的,但是!为什么一定要模棱两可?为什么不能说 class {} 大括号是最内部的范围,因此它应该选择 add 方法而不是全局 add?
  • 扮演魔鬼的拥护者,这本身并不是真正的争论。即使外部 add 被遮蔽(例如 \add() 或其他东西),也可能存在访问外部 add 的消歧规则……
【解决方案2】:

我会尽量让它“稍微”更理论一些,而不是深入研究文档等等。

您的问题的主要答案可以通过将您的代码转换为纯 javascript 来找到。为此,您可以使用 babel online 或 typescript online playground。 无论哪种情况,转译后的代码都将如下所示:

"use strict";
var sum = /** @class */ (function () {
    function sum() {
    }
    sum.prototype.add = function (x, amt) {
        if (amt == 0)
            return x;
        return add(x + 1, amt - 1);
    };
    return sum;
}());
var summer = new sum();
console.log(summer.add(5, 5));

如您所见,add 方法属于 sum 原型,是一个函数。因此,您可以猜测在add 范围内访问add 只是不能隐式导致调用sum.prototype.add 函数。

不同的是,如果您查看正确代码:

class sum {
  add(x, amt) {
    if(amt == 0) return x
    return this.add(x+1, amt-1)
  }
}
var summer = new sum()
console.log(summer.add(5,5))

您会看到转译后的代码将调用this 方法:

"use strict";
var sum = /** @class */ (function () {
    function sum() {
    }
    sum.prototype.add = function (x, amt) {
        if (amt == 0)
            return x;
        return this.add(x + 1, amt - 1);
    };
    return sum;
}());
var summer = new sum();
console.log(summer.add(5, 5));

这并不是真正的模棱两可,而是在 javascript 中允许这种调用,因为 add 方法在全局范围内隐式可用。能够访问函数作用域中的全局作用域(因为请记住,无论发生什么,javascript 中的 class总是 转译为函数)允许继承函数的标准行为,即可以访问其父级,拥有自己的范围并授予对全局范围的访问权限。

有点好奇:如果你真的可以使用add 访问this.add,你将无法使用undefined,因为它是一个全局变量,因此你不会能够访问和使用它,因为它隐含地是this.undefined

所以,再次强调,不是关于歧义,而是关于 javascript functions 的工作原理。

【讨论】:

  • 如果您在将函数分配给该转译代码中的原型之前在作用域中声明该函数,则可以利用函数提升。然后“方法”可以互相引用:pastebin.com/2nqipgMP
  • @Thomas 确实可以做到这一点,尽管它涉及在纯 javascript 中处理转译代码。作为旁注,您应该小心所谓的“阴影”。如果你有一个全局的 add 函数,它会被内部的 add 函数遮住,你可以在这里看到:jsfiddle.net/8gfxpyLj 重点是 javascript 中可用的 OOP 与其他一些函数略有不同,因为语言是经过设计的。
【解决方案3】:

从根本上说,Javascript 中的 OOP 与 和其他语言非常不同。实际上并没有强烈的“类”概念。 Javascript 中的类和实例等经典 OOP 概念并不是真正来自“类”,而是来自围绕属性查找的规则以及 thisnew 关键字。其他一切都只是函数、对象和属性——其中一些是“特殊的”,例如prototype

例如你可以像这样组装一个“类”:

function Foo() {
    this.bar = 42;
}

function baz() {
    return this.bar;
}

Foo.prototype.baz = baz;

let foo = new Foo;
console.log(foo.baz());

这里没有真正的凝聚力,baz 本质上不属于任何类,但像这样组装起来它们就像一个。而class 语法只是这个机制的薄薄的一层。所以,如果你在一个函数中写add,它会遵循变量作用域规则,并且会在一些周围的作用域中查找一个变量,它不会在原型上找到一个方法。

【讨论】:

    猜你喜欢
    • 2014-01-30
    • 2014-01-22
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多