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

时间:2015-06-11 14:08:50

标签: javascript ecmascript-6

我对ES6课程有很多疑问。

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

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

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

2 个答案:

答案 0 :(得分:110)

(几乎)完全由你决定。新的class东西大多只是语法糖。 (但是,你知道,那种好糖。)ES2015-ES2018中没有任何内容class可以做到你无法用构造函数和Reflect.construct做的事情(包括子类化ErrorArray¹)。 ( 可能是ES2019或ES2020中的某些内容,您可以使用class执行此操作,否则您无法做到:private fieldsprivate methods。)< / p>

  

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

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

    < / LI>

以下是层次结构的语法比较:

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

VS

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

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

¹“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;
}

答案 1 :(得分:19)

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:这是我们已经做过的事情,并且在代码中明确了你的意图。