Javascript:函数和类有什么区别

IT技术 javascript ecmascript-6
2021-02-22 02:25:59

随着 2015 年 6 月 ECMAScript 6 的发布,引入了 Javascript 类语法。

此语法:

class Polygon {
      constructor(width, height) {
        this.width = width;
        this.height = height;
      }
}

基本相同:

function Polygon(width, height) {
    this.width = width;
    this.height = height;
}

那么使用类代替传统函数有什么好处呢?在什么情况下我应该使用类而不是函数?

2个回答

Class 和 Function 之间有一些区别——大多数人会首先说 Class 是“只是语法糖”,但这种糖确实很重要。当 JS 解析器处理 JavaScript 代码时,解析器会将它们保存在不同的 AST 节点中,如下所示,ClassDeclarationClassExpression在生成的 AST 树中是不同的节点类型:

https://github.com/estree/estree/blob/master/es2015.md#classes

你可以看到,对于这个解析器,新的 ES6 Classes 规范在语法中引入了许多新的 AST 元素:

  • 类体
  • 方法定义
  • 类声明
  • 类表达式
  • 元属性

由于 AST 语法不是标准的,根据解析器可以有更多或更少的类型,但重要的是要注意,当代码进入类声明或类表达式时,JavaScript 引擎会对其进行不同的解释。

这意味着不能交换 Class 和 Function 声明。如果您尝试编写,您可以看到这一点

class notWorking {
  return 1;  // <-- creates a parser error
};

这是因为当解析器遇到 class -keyword 时,它将开始将以下代码视为 ClassDeclaration 或 ClassExpression 的 ClassBody,然后它期望找到 MethodDefinitions。

这是一个小问题,因为创建私有变量变得更具挑战性。函数声明可以像这样巧妙地定义一个私有变量:

function myClass() {
    var privateVar;
}

类声明不能有这个:

class myClass {
    var privateVar; // ERROR: should be a method
}

这是因为类的语法只允许在类体内声明方法。至少现在是这样。

但是,存在创建私有字段的提议:

https://github.com/zenparsing/es-private-fields

因此,将来你可能会说

class myClass {
   #privateVar; // maybe this works in the future?
}

考虑到 ES6 类中的私有属性,有一个单独的答案,它提出了一些解决方法,例如使用符号:

JavaScript ES6 类中的私有属性

var property = Symbol(); // private property workaround example
class Something {
    constructor(){
        this[property] = "test";
    }
}

当然,类和函数之间存在更多差异。其中之一是提升 1 - 与函数不同,您不能在范围内的任何地方声明类:

函数声明和类声明之间的一个重要区别是函数声明被提升而类声明不是。您首先需要声明您的类,然后访问它

类声明和函数声明非常相似;

function foo1() {} // can be used before declaration
class  foo2{}      // new foo2(); works only after this declaration

类表达式的工作方式与函数表达式非常相似,例如它们可以分配给变量:

var myClass = class foobar {};

更多的差异是1

  1. Class 表达式/声明体总是在严格模式下执行- 无需手动指定
  2. 类有特殊的关键字构造函数——只能有一个,否则会抛出错误。函数可以有多个名为“构造函数”的函数变量定义
  3. 类具有与父类构造函数相关的特殊关键字super如果你在构造函数中,你可以调用super(x, y); 要调用父类构造函数,但在 Method 内部,您可以调用super.foobar()来创建对任何父类函数的调用。这种功能不适用于标准函数,尽管您可以使用一些自定义黑客来模拟它。
  4. 在类体内,您可以使用static关键字定义函数,因此可以仅使用ClassName.FunctionName() -syntax来调用它
  5. 类声明和表达式都可以使用extends关键字,如class Dog extends Animal
  6. MethodDeclaration 不需要函数前缀,因此您可以在类“m”中定义函数“ok”,如下所示:class m { ok() { } }实际上甚至不允许将函数定义为class m { function ok() { } }

然而,在解析器完成它的工作后,类实例本质上与任何其他对象以相同的方式运行。

新的 ES6 Class 语法本质上是更清晰的以传统 OOP 方式表达对象的方式,如果你喜欢它,那么你应该使用它。

编辑:此外,ES6 类语法还有另一个限制:它不允许成员函数使用使用粗箭头进行词法绑定。ES7 似乎具有允许它的实验性功能例如,当将方法绑定到事件处理程序时,这可能很有用,相关问题是here

1 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

@TeroTolonen:这几乎不是区分类和函数的“特性”。您可以在构造函数中使用箭头函数,而不能在原型上使用。
2021-04-16 02:25:59
我还添加了关于 ES6 类声明缺少粗箭头语法的通知。
2021-04-21 02:25:59
@TeroTolonen:那应该是什么?这显然是一个语法错误,就像function n = (val) => {}那样。您需要使用class a { constructor() { this.n=val=>{…}; } }- 与您在使用function关键字创建的构造函数中所做的相同
2021-04-27 02:25:59
(new a()).n() 在 babel repl 中很好用,但这仍然是实验性的,就像在文本和其他答案中解释的那样
2021-04-27 02:25:59
@Bergi:我的意思是这样的:class a { n = (val) => { } }
2021-05-14 02:25:59

class它只不过是使用 .js 创建 javascript 逻辑类的语法糖function如果您使用 afunction作为class整个函数作为构造函数,如果您想放置其他成员函数,则需要在构造函数中执行此操作,例如this.something = ...,或者var something = ...在私有成员的情况下(如果您不是从外部注入,请假设您是使用其他方法/属性创建对象),但在类的情况下,整个函数实际上并不充当构造函数,您可以将其与其他成员函数和数据显式分开。