Подтвердить что ты не робот

Почему JavaScript ES6 не поддерживает классы с несколькими конструкторами?

Я хочу написать свой класс Javascript, как показано ниже.

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

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

    constructor(key, value, autoLoad) {
        this[key] = value;
        this.autoLoad = autoLoad || false;
    }
}

Я думаю, было бы неплохо, если бы мы могли записать класс таким образом. Ожидайте:

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}
4b9b3361

Ответ 1

Я хочу написать свой класс 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;
        }
    }
}

Пример 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;
    }
}

Ответ 2

То, что вы хотите, называется перегрузкой конструктора. Этот и более общий случай перегрузки функций не поддерживается в ECMAScript.

ECMAScript не обрабатывает отсутствующие аргументы так же, как более строгие языки. Значение отсутствующих аргументов остается как undefined вместо повышения ошибки. В этой парадигме сложно/невозможно определить, к какой перегруженной функции вы стремитесь.

Идиоматическое решение состоит в том, чтобы иметь одну функцию и обрабатывать все комбинации аргументов, которые вам нужны. Для исходного примера вы можете просто проверить наличие key и value следующим образом:

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

Ответ 3

Здесь взломан для перегрузки на основе arity (количество аргументов). Идея состоит в том, чтобы создать функцию из ряда функций с разными уровнями (определяется путем поиска 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);

Конечно, это требует много пуленепробиваемости и очистки, но вы получаете эту идею. Тем не менее, я не думаю, что вам удастся применить это к конструкторам класса ES6, потому что они лошади другого цвета.

Ответ 4

Другой вариант - позволить вашему конструктору взять объект, привязанный к вашим свойствам класса:

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}

Ответ 5

Угадываясь из вашего образца кода, все, что вам нужно, это использовать значения по умолчанию для ваших параметров:

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

Сказав это, другой альтернативой перегрузке конструктора является использование статических фабрик. Предположим, вы хотели бы создать экземпляр объекта из простых параметров, из хэша, содержащего те же параметры или даже из строки 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}');