Правила, чтобы заменить «новый» - PullRequest
3 голосов
/ 05 марта 2012

В своих «Хороших частях» Крокфорд предлагает, чтобы «новое» никогда не использовалось. Чтобы следовать этому правилу, как бы вы реорганизовали следующий код?

function Range(from, to) { 
    this.from = from; 
    this.to = to; 
} 

Range.prototype = { 
    includes: function(x) {
        return this.from <= x && x <= this.to;
    }, 

    foreach: function(f) { 
        for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);
    },

    toString: function() { 
        return "(" + this.from + "..." + this.to + ")"; 
    } 
};

// Here are example uses of a range object 
var r = new Range(1,3); // Create a range object 
r.includes(2); // => true: 2 is in the range 
r.foreach(console.log); // Prints 1 2 3

Я заметил его дополнительный совет , но не было ясно, как его применять в этом (предположительно, очень распространенном) случае. Предложит ли он создать фабричную функцию, содержащую гигантский объектный литерал? Если да, разве это не неэффективно? ISTM, что такая фабричная функция при каждом вызове создает дублирующие функции. Другими словами, нет ни одного прототипа, содержащего общие пользовательские методы.

Кажется, что-то осталось недосказанным в его совете, я надеюсь, что кто-то может это прояснить.

Ответы [ 3 ]

2 голосов
/ 05 марта 2012

Здесь я показываю, как этого добиться, не используя new

Range = function(from, to) {

    function includes(x) {
        return this.from <= x && x <= this.to;
    }

    function foreach(f) {
        for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);
    }

    function toString(){
        return "(" + this.from + "..." + this.to + ")";
    }

    return {
        from: from,
        to: to,
        includes: includes,
        foreach:  foreach,
        toString: toString
    };
};

var r = Range(1, 3);
console.log(r.includes(2)); // => true: 2 is in the range
r.foreach(console.log); // Prints 1 2 3

Это всего лишь пример, но я бы следовал тому, что говорит @nnnnnn - «используйте его только тогда, когда это уместно. Насколько я понимаю, код в вашем вопросе идеально подходит для использования new и не делает нуждается в рефакторинге. "

EDIT:

Приведенный ниже код позволит избежать дублирования функций

Range = function(from, to) {
    return {
        from: from,
        to: to,
        includes: Range.includes,
        foreach:  Range.foreach,
        toString: Range.toString
    };
};

Range.includes = function(x) {
    return this.from <= x && x <= this.to;
}

Range.foreach = function (f) {
    for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);
}

Range.toString = function() {
    return "(" + this.from + "..." + this.to + ")";
}
1 голос
/ 05 марта 2012

Я думаю, он предложил бы сделать что-то вроде этого:

function Range(from, to) {
  if (!(this instanceof Range)) {
    return new Range(from, to);  // Yes, the new operator is used here, but...
  }

  this.from = from;
  this.to = to;
}

// ... now, the rest of the world can create ranges WITHOUT the new operator:
var my_range = Range(0, 1);
0 голосов
/ 05 марта 2012

Мне нравится эта техника, я называю ее гарантированными экземплярами:

var Range = function fn(from, to) {
    if (!(this instanceof fn)) {
        return new fn(from, to);
    };
    this.from = from; 
    this.to = to; 
} 

Range.prototype = { 
    includes: function(x) {
        return this.from <= x && x <= this.to;
    }, 

    foreach: function(f) { 
        for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);
    },

    toString: function() { 
        return "(" + this.from + "..." + this.to + ")"; 
    } 
};

// Here are example uses of a range object 
var r = new Range(1,3); // Create a range object 
r.includes(2); // => true: 2 is in the range 
r.foreach(console.log); // Prints 1 2 3
...