为什么JavaScript ES6不支持多构造函数类?

Man*_*lua 48 javascript constructor class ecmascript-6

我想写下我的Javascript类.

class Option {
    constructor() {
        this.autoLoad = false;
    }

    constructor(key, value) {
        this[key] = value;
    }

    constructor(key, value, autoLoad) {
        this[key] = value;
        this.autoLoad = autoLoad || false;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想如果我们能用这种方式写出课程会很好.期待发生:

var option1 = new Option(); // option1 = {autoLoad: false}
var option2 = new Option('foo', 'bar',); // option2 = {foo: 'bar'}
var option3 = new Option('foo', 'bar', false); // option3 = {foo: 'bar', autoLoad: false}
Run Code Online (Sandbox Code Playgroud)

Cod*_*gue 30

我想写下我的Javascript类

你不能以同样的方式超载这样的标准功能.你可以做的是使用arguments对象来查询传递的参数数量:

class Option {
    constructor(key, value, autoLoad) {
        // new Option()
        if(!arguments.length) {
            this.autoLoad = false;
        }
        // new Option(a, [b, [c]])
        else {
            this[key] = value;
            this.autoLoad = autoLoad || false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Babel REPL示例

当然(使用您更新的示例),您可以采用不关心参数数量的方法,而不是每个单独的值是否通过,在这种情况下,您可以这样:

class Option {
    constructor(key, value, autoLoad) {
        if(!key) { // Could change this to a strict undefined check
            this.autoLoad = false;
            return;
        }
        this[key] = value;
        this.autoLoad = autoLoad || false;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么我们如此坚决拒绝使用参数默认值? (6认同)
  • @torazaburo*我们*不是.*我正在回答如何模拟重载的问题.我可能已经错过了原始代码的其他方面的一些优化,param默认是我认为可能是多个的一个. (3认同)

Bar*_*row 18

你想要的是构造函数重载.ECMAScript不支持这个以及函数重载的更一般情况.

ECMAScript不像更严格的语言那样处理缺少的参数.保留缺失参数的值undefined而不是引发错误.在这个范例中,很难/不可能检测出您的目标是哪个超载功能.

惯用的解决方案是拥有一个函数,并让它处理您需要的所有参数组合.对于原始示例,您可以测试是否存在key并且value像这样:

class Option {
  constructor(key, value, autoLoad = false) {
    if (typeof key !== 'undefined') {
      this[key] = value;
    }
    this.autoLoad = autoLoad;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果他正在使用`class`,他正在使用ES6.ES6具有参数默认值.使用它们. (3认同)
  • @torazaburo:默认值不适用于可变参数函数 (3认同)

GFo*_*y83 8

另一个选择是允许您的构造函数采用绑定到您的类属性的对象:

class Option {
  // Assign default values in the constructor object 
  constructor({key = 'foo', value, autoLoad = true} = {}) {
      this.key = key;
      // Or on the property with default (not recommended)
      this.value = value || 'bar';
      this.autoLoad = autoLoad;
      
      console.log('Result:', this);
  }
}

var option1 = new Option();
// Logs: {key: "foo", value: "bar", autoLoad: true}

var option2 = new Option({value: 'hello'});
// Logs: {key: "foo", value: "hello", autoLoad: true}
Run Code Online (Sandbox Code Playgroud)

这对于Typescript更加有用,因为您可以使用传入的值(即key只能是字符串,autoLoad布尔值等)确保类型安全。


小智 6

这是基于arity(参数数量)的重载的黑客攻击.我们的想法是从具有不同arities的许多函数创建函数(通过查看确定fn.length).

function overloaded(...inputs) {
  var fns = [];

  inputs.forEach(f => fns[f.length] = f);

  return function() {
    return fns[arguments.length].apply(this, arguments);
  };
}

var F = overloaded(
  function(a)    { console.log("function with one argument"); },
  function(a, b) { console.log("function with two arguments"); }
);

F(1);
F(2, 3);
Run Code Online (Sandbox Code Playgroud)

当然,这需要大量的防弹和清理,但你明白了.但是,我不认为你将这个应用于ES6类构造函数会有太多运气,因为它们是一种不同颜色的马.

  • 这是一个缓慢的黑客,它不是非常惯用. (3认同)
  • 在完全解释的环境中,可能没有显着差异,但对于像V8这样的JIT编译器,可优化问题和不可优化问题之间的区别是显而易见的.我不能在没有测试的情况下肯定地说,但是这样的动态分支闭包传递通常很慢.慢,虽然是主观的(我们在这里谈论ms),如果它不是一个非常热门的功能,我不会担心.惯用语也是一个主观术语,只是意味着某些东西被认为是该语言的"最佳实践".例如,同步IO在JS中不是惯用的. (3认同)
  • @BardiHarborow感谢您的回复.我修改了我的答案,说"黑客"而不是"一般解决方案". (2认同)

Dio*_*ert 6

从您的示例代码中猜测,您只需要为参数使用默认值:

class Option {
    constructor(key = 'foo', value = 'bar', autoLoad = false) {
        this[key] = value;
        this.autoLoad = autoLoad;
    }
}
Run Code Online (Sandbox Code Playgroud)

话虽如此,构造函数重载的另一种替代方法是使用静态工厂。假设您希望能够从普通参数、包含这些相同参数的散列或什至从 JSON 字符串实例化一个对象:

class Thing {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }

    static fromHash(hash) {
        return new this(hash.a, hash.b);
    }

    static fromJson(string) {
        return this.fromHash(JSON.parse(string));
    }
}

let thing = new Thing(1, 2);
// ...
thing = Thing.fromHash({a: 1, b: 2});
// ...
thing = Thing.fromJson('{"a": 1, "b": 2}');
Run Code Online (Sandbox Code Playgroud)