草庐IT

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

coder 2023-07-03 原文

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

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

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

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

最佳答案

新的class语法主要是(虽然不完全)语法糖(但是,你知道的,是一种很好的糖)。它显着简化了构造函数的编写以及它们作为原型(prototype)分配给它们创建的对象的对象,尤其是在设置继承层次结构时,这在 ES5 语法中容易出错。但与旧方法不同的是,class语法还为 super 调用启用了super.example()(众所周知,旧方法很难做到)以及property declarationsprivate fieldsprivate methods(包括“10546”)。
(有时人们会说如果你想对classError进行子类化,你必须使用Array语法[在 ES5 中不能正确地子类化]。那不是真的,你可以使用不同的 ES2015 特性,Reflect.construct ” [ static ones, spec],如果您不想使用class语法。¹)

Moreover, is class a different kind of OOP or it still JavaScript's prototypical inheritance?


如果您喜欢使用构造函数(new Foo等),它与我们一直拥有的原型(prototype)继承相同,只是具有更清晰、更方便且不易出错的语法,以及一些附加功能。

Can I modify it using .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);
};

Are there speed benefits?


通过为此提供特定的习惯用法,我认为引擎可能能够更好地优化工作。但他们已经非常擅长优化,我不希望有显着差异。关于class语法的一件事是,如果您使用MDN,您可以最大限度地减少对象在构造时经历的形状更改次数,这可以使解释和稍后编译代码的速度更快一些。但同样,它不会很大。

What benefits does ES2015 (ES6) class syntax provide?


简而言之:如果您一开始不使用构造函数,更喜欢Object.create或类似的,class对您没有用。
如果您确实使用构造函数,class有一些好处:
  • 语法更简单,不易出错。
  • 更容易(同样,更不容易出错)使用新语法设置继承层次结构比使用旧语法。
  • class 可以保护您免受未将new与构造函数一起使用(通过让构造函数抛出异常)的常见错误。
  • 使用新语法调用方法的父原型(prototype)版本比旧语法简单得多(super.method()而不是ParentConstructor.prototype.method.call(this)Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this))。
  • 属性声明可以使正在创建的实例的形状更清晰,将其与构造函数逻辑分开。
  • 您可以使用class语法而不是 ES5 语法使用私有(private)字段和方法(实例和静态)。

  • 这是层次结构的语法比较(没有私有(private)成员):
    // ***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() {
            // ...
        }
    }
    
    例子:

    // ***ES2015+**
    class Person {
        constructor(first, last) {
            this.first = first;
            this.last = last;
        }
    
        personMethod() {
            return `Result from personMethod: this.first = ${this.first}, this.last = ${this.last}`;
        }
    }
    
    class Employee extends Person {
        constructor(first, last, position) {
            super(first, last);
            this.position = position;
        }
    
        personMethod() {
            const result = super.personMethod();
            return result + `, this.position = ${this.position}`;
        }
    
        employeeMethod() {
            // ...
        }
    }
    
    class Manager extends Employee {
        constructor(first, last, position, department) {
            super(first, last, position);
            this.department = department;
        }
    
        personMethod() {
            const result = super.personMethod();
            return result + `, this.department = ${this.department}`;
        }
    
        managerMethod() {
            // ...
        }
    }
    
    const m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops");
    console.log(m.personMethod());

    对比
    // **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() {
        // ...
    };
    
    现场示例:

    // **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() {
        return "Result from personMethod: this.first = " + this.first + ", this.last = " + this.last;
    };
    
    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.personMethod = function() {
        var result = Person.prototype.personMethod.call(this);
        return result + ", this.position = " + this.position;
    };
    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);
        return result + ", this.department = " + this.department;
    };
    Manager.prototype.managerMethod = function() {
        // ...
    };        
    
    var m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops");
    console.log(m.personMethod());

    正如你所看到的,那里有很多重复和冗长的东西,很容易出错,重新输入很无聊(我曾经使用脚本,在class出现之前的一天)。

    ¹ 如果您不想使用Reflect.construct语法,以下是您使用Error子类化class的方法(例如):

    // Creating an Error subclass:
    function MyError(...args) {
      return Reflect.construct(Error, args, this.constructor);
    }
    MyError.prototype = Object.create(Error.prototype);
    MyError.prototype.constructor = MyError;
    MyError.prototype.myMethod = function() {
      console.log(this.message);
    };
    
    // Example use:
    function outer() {
      function inner() {
        const e = new MyError("foo");
        console.log("Callng e.myMethod():");
        e.myMethod();
        console.log(`e instanceof MyError? ${e instanceof MyError}`);
        console.log(`e instanceof Error? ${e instanceof Error}`);
        throw e;
      }
      inner();
    }
    outer();
    .as-console-wrapper {
      max-height: 100% !important;
    }

    关于javascript - ES2015 (ES6) `class` 语法有什么好处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30783217/

    有关javascript - ES2015 (ES6) `class` 语法有什么好处?的更多相关文章

    1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    3. ruby - 树顶语法无限循环 - 2

      我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

    4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

      为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

    7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

      它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

    8. ruby - Infinity 和 NaN 的类型是什么? - 2

      我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

    9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

      如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

    10. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

      所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

    随机推荐