ES2015 (ES6) `class` 语法有什么好处?

IT技术 javascript ecmascript-6
2021-02-04 14:11:41

我有很多关于 ES6 类的问题。

使用class语法有什么好处我读到 public/private/static 将成为 ES7 的一部分,这是一个原因吗?

此外,是class另一种 OOP 还是它仍然是 JavaScript 的原型继承?我可以修改它使用.prototype吗?或者它只是同一个对象,但有两种不同的声明方式。

有速度优势吗?如果你有一个像大应用程序这样的大应用程序,也许更容易维护/理解?

2个回答

新的class语法是,对于现在,大多是语法糖。(但是,你知道,这是一种很好的糖。)在 ES2015-ES2020 中class没有什么是你不能用构造函数和Reflect.construct(包括子类化ErrorArray¹)做的。(这很可能会有一些东西ES2021,您可以用做class,你不能这样做,否则:私人领域私有方法静态字段/私有静态方法。)

此外,是class另一种 OOP 还是它仍然是 JavaScript 的原型继承?

它与我们一直拥有的原型继承相同,只是如果您喜欢使用构造函数(new Foo等),可以使用更简洁、更方便的语法(特别是在从Arrayor派生的情况下Error,您在 ES5 及更早版本中无法做到这一点。您现在可以使用Reflect.construct[ spec , MDN ],但不能使用旧的 ES5 样式。)

我可以修改它使用.prototype吗?

是的,prototype一旦创建了类,您仍然可以在类的构造函数上修改对象。例如,这是完全合法的:

class Foo {
    constructor(name) {
        this.name = name;
    }
    
    test1() {
        console.log("test1: name = " + this.name);
    }
}
Foo.prototype.test2 = function() {
    console.log("test2: name = " + this.name);
};

有速度优势吗?

通过提供这种特定的成语,我想这是可能的发动机也许能做得更好优化。但他们已经非常擅长优化,我不希望有显着差异。

ES2015 (ES6)class语法有什么好处

简而言之:如果您一开始不使用构造函数,首选Object.create或类似的函数class对您没有用处。

如果您确实使用构造函数,则有一些好处class

  • 语法更简单,更不容易出错。

  • 这是很多容易(再次,不易出错)使用新的语法比旧的设置继承层次。

  • class保护您免受未能new与构造函数一起使用的常见错误(如果构造函数不是构造函数this的有效对象,则让构造函数抛出异常)。

  • 使用新语法调用方法的父原型版本比旧语法(super.method()而不是ParentConstructor.prototype.method.call(this)or Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)简单得多

这是层次结构的语法比较:

// ***ES2015+**
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    personMethod() {
        // ...
    }
}

class Employee extends Person {
    constructor(first, last, position) {
        super(first, last);
        this.position = position;
    }

    employeeMethod() {
        // ...
    }
}

class Manager extends Employee {
    constructor(first, last, position, department) {
        super(first, last, position);
        this.department = department;
    }

    personMethod() {
        const result = super.personMethod();
        // ...use `result` for something...
        return result;
    }

    managerMethod() {
        // ...
    }
}

例子:

对比

// **ES5**
var Person = function(first, last) {
    if (!(this instanceof Person)) {
        throw new Error("Person is a constructor function, use new with it");
    }
    this.first = first;
    this.last = last;
};

Person.prototype.personMethod = function() {
    // ...
};

var Employee = function(first, last, position) {
    if (!(this instanceof Employee)) {
        throw new Error("Employee is a constructor function, use new with it");
    }
    Person.call(this, first, last);
    this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.employeeMethod = function() {
    // ...
};

var Manager = function(first, last, position, department) {
    if (!(this instanceof Manager)) {
        throw new Error("Manager is a constructor function, use new with it");
    }
    Employee.call(this, first, last, position);
    this.department = department;
};
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.personMethod = function() {
    var result = Employee.prototype.personMethod.call(this);
    // ...use `result` for something...
    return result;
};
Manager.prototype.managerMethod = function() {
    // ...
};

现场示例:

正如你所看到的,那里有很多重复和冗长的东西,很容易出错并且重新输入很无聊(这就是为什么我写了一个脚本来做到这一点,回到过去)。


¹“在 ES2015-ES2018 中,class没有什么是你不能用构造函数和Reflect.construct(包括子类化ErrorArray)做的”

例子:

嗯。我不买这个的需要。我离开了 c# 以摆脱 oop,现在 oop 正在 javascript 上蔓延。我不喜欢类型。一切都应该是一个var/object/function。而已。没有多余的关键字。和继承是一个骗局。如果你想看到类型和非类型之间的良好斗争。看看肥皂与休息。我们都知道谁赢了。
2021-03-16 14:11:41
@JasonCust:是的,这是一种不同的方式。我不会称之为“更本土化”(我知道凯尔会,但那是凯尔)。构造函数是 JavaScript 的基础,看看所有的内置类型(除了反构造函数以某种方式Symbol搞砸了)。但同样重要的是,如果您不想使用它们,则不必使用。我在我的工作中发现构造函数在很多地方都有意义,并且语义new提高了清晰度;并且Object.create在许多其他地方都有意义,尤其是。立面情况。它们是互补的。:-)
2021-03-28 14:11:41
@foreyez:class语法并没有让 JavaScript 变得比以前更加类型化。正如我在回答中所说的那样,对于已经存在的内容,它主要是(非常)简单的语法。绝对有必要这样做,人们总是把旧的语法弄错。这也不意味着您必须比以前使用继承。(当然,如果您从不使用旧语法设置“类”,则也无需使用新语法这样做。)
2021-04-03 14:11:41
@JasonCust:是的,这是一个公平的比较,因为我展示了 ES5 的方式来做 ES6 对class. JavaScript是足够灵活,你就不必使用构造函数,如果你不想,这是你在做什么,但这是不同class-整点class是在JavaScript简化伪古典继承。
2021-04-08 14:11:41
我不知道这样的比较是否公平。您可以实现类似的目标,而无需任何繁琐。它使用 JS 通过原型链链接对象的方式,而不是近似经典继承。我做了一个要点,展示了一个基于你的简单例子来展示如何做到这一点。
2021-04-10 14:11:41

ES6 类是我们今天使用的原型类系统的语法糖。它们使您的代码更加简洁和自我记录,这是使用它们的充分理由(在我看来)。

使用 Babel 转译这个 ES6 类:

class Foo {
  constructor(bar) {
    this._bar = bar;
  }

  getBar() {
    return this._bar;
  }
}

会给你类似的东西:

var Foo = (function () {
  function Foo(bar) {    
    this._bar = bar;
  }

  Foo.prototype.getBar = function () {
    return this._bar;
  }

  return Foo;
})();

第二个版本并不复杂,需要维护的代码更多。当涉及到继承时,这些模式会变得更加复杂。

因为这些类编译成我们一直在使用的相同原型模式,所以您可以对它们进行相同的原型操作。这包括在运行时添加方法等,访问方法Foo.prototype.getBar等。

今天在 ES6 中有一些对隐私的基本支持,尽管它基于不导出您不想访问的对象。例如,您可以:

const BAR_NAME = 'bar';

export default class Foo {
  static get name() {
    return BAR_NAME;
  }
}

并且BAR_NAME不会被其他module直接引用。

许多库都试图支持或解决这个问题,比如 Backbone 和他们的extends助手,它采用未经验证的方法类函数和属性的散列,但没有用于暴露原型继承的一致系统,不涉及与原型混淆。

随着 JS 代码变得越来越复杂,代码库越来越大,我们已经开始进化出很多模式来处理继承和module等问题。用于为module创建私有作用域的 IIFE 有很多大括号和括号;缺少其中一个可能会导致有效脚本执行完全不同的操作(在module可以将下一个module作为参数传递给它之后跳过分号,这很少有好处)。

tl; dr:这是我们已经做的事情的糖,并在代码中明确您的意图。