ES2015(ES6)`class`语法提供了哪些好处? [关闭]

问题描述 投票:77回答:2

我对ES6课程有很多疑问。

使用class语法有什么好处?我读到公共/私有/静态将成为ES7的一部分,这是一个原因吗?

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

有速度的好处吗?如果你有一个像大应用程序这样的大应用程序,可能更容易维护/理解?

javascript ecmascript-6
2个回答
112
投票

无论你做什么,它(几乎)完全取决于你。新的class东西大多只是语法糖。 (但是,你知道,那种好糖。)ES2015-ES2018中没有任何东西可以用class来做你无法用构造函数和Reflect.construct(包括继承ErrorArray¹)。 (在ES2019或ES2020中你可以用class做一些你不能做的事情:private fieldsprivate methods。)

而且,class是一种不同的OOP还是它仍然是JavaScript的典型继承?

它与我们一直拥有的原型继承相同,如果您喜欢使用构造函数(new Foo等),只需使用更清晰,更方便的语法。 (特别是在从ArrayError派生的情况下,你在ES5及更早版本中无法做到。你现在可以使用Reflect.construct [specMDN],但不能使用旧的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)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() {
        // ...
    }
}

例:

// ***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());

正如你所看到的那样,很多重复和冗长的东西很容易出错并且无法重新打字(这就是为什么我在当天写的a script to do it)。


¹“ES2015-ES2018中没有任何东西可以用class来做你无法用构造函数和Reflect.construct(包括继承ErrorArray)”

例:

// 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;
}

20
投票

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将不能用于其他模块直接引用。

许多库试图支持或解决这个问题,比如Backbone和他们的extends帮助器,它使用类似方法的函数和属性的未经验证的散列,但是没有用于暴露原型继承的编组系统,不涉及原型的捣乱。

随着JS代码变得越来越复杂并且代码库越来越大,我们开始发展很多模式来处理继承和模块之类的事情。 IIFE用于创建模块的私有范围有很多括号和parens;缺少其中一个可能导致一个完全不同的有效脚本(在模块可以将下一个模块作为参数传递给它之后跳过分号,这很少有用)。

tl; dr:它是我们已经做过的糖,并且在代码中明确了你的意图。

© www.soinside.com 2019 - 2024. All rights reserved.