2011-03-07 9 views
56

libro de Crockford, JavaScript: las partes buenas, dice (en la página 114) que las funciones constructoras deben nombres siempre darse con una letra mayúscula inicial (es decir, punto), y los nombres de funciones con letras mayúsculas iniciales deben solo ser utilizados con funciones de constructor (todo lo demás debe ser de menor instancia).JavaScript: las partes buenas - ¿Cómo no usar `new` en absoluto

Esta convención nos ayuda a evitar el uso del operador new con funciones de constructor.

Él continúa diciendo que "[a] n aún mejor estrategia de afrontamiento es no usar new en absoluto."

Mi pregunta es, ¿cómo programamos JavaScript sin usar new en absoluto?

  • Podemos evitar new Object() y new Array() con el literal {} y [].
  • Podemos evitar new Number(), new Boolean() y new String() con 0, true y ''.
  • Podemos evitar new RegExp() con algo así como /pattern/.

¿Cómo evitamos new Date()?

Y, lo más importante, ¿cómo evitamos usar new con nuestros propios Objetos personalizados?

+1

de prototipos, prototipos, prototipos. – Oded

+0

¿Qué argumento tiene? Tengo un proyecto completo que depende de 'nuevo' para obtener una gran oferta y me ha ayudado muchísimo organizarlo. – pimvdb

+2

eche un vistazo a [¿Puedo construir un objeto JavaScript sin utilizar la nueva palabra clave] (http://stackoverflow.com/questions/1889014/can-i-construct-a-javascript-object-without-using-the-new -keyword) – Maxym

Respuesta

6

Usted puede evitar new mediante la creación de funciones de fábrica:

var today = Date.getToday(); 

(En caso de que se preguntan, que no puede evitar que la función propia fábrica :)

Date.getToday = function() { return new Date(); }; 

Aunque solo creo que deberías crear tales funciones si agrega valor semántico (como en el caso anterior) o si puedes predeterminar algunos de los parámetros del constructor. En otras palabras, no lo haga solo para evite usando new.

+1

Creo que quiere decir que uno haría: 'Date.prototype.getToday = function() {...}' – Pointy

+1

Si bien eso es cierto, no es lo que Douglas Crockford quiso decir, y esta pregunta es solo eso. Ver http://javascript.crockford.com/prototypal.html –

+0

@Pointy: No, quiso decir lo que escribió. El punto es poder escribir Date.getToday() en lugar de New Date(). Si él declarara la función en el prototipo, tendría que escribir una nueva Fecha(). GetToday(), derrotando así el punto del ejercicio. – KaptajnKold

41

Crockford da un ejemplo de una función de creación de objetos que deberían haber sido proporcionados por sí mismo JS en una de sus conversaciones Javascript disponibles en http://developer.yahoo.com/yui/theater/

Sin embargo, el (3) propio equipo de YUI utiliza "nuevo", y SÍ siga sus recomendaciones (ya que él es el arquitecto JS jefe de Yahoo (ACTUALIZACIÓN: siguió adelante, pero la declaración era cierta cuando esta respuesta se escribió originalmente). Entiendo que esta afirmación particular sea más en un nivel "académico", lo que DEBERÍA tener TENGO el lenguaje diseñado "correctamente" y no con algunos restos de la herencia hereditaria. Él (en mi humilde opinión) dice que la forma en que resultó JS es conflictiva, basada en prototipos pero con esto de la "clase clásica" lenguajes de herencia.

Sin embargo, JS es como es, así que ve y usa "nuevo".

Usted puede encontrar que su función de creación de objetos aquí: http://javascript.crockford.com/prototypal.html

if (typeof Object.create !== 'function') { 
    Object.create = function (o) { 
     function F() {} 
     F.prototype = o; 
     return new F(); 
    }; 
} 
newObject = Object.create(oldObject); 


EDIT: Se ha actualizado para utilizar la última versión de Crockford de esa función - hay tres.


ACTUALIZACIÓN de junio de 2015: Hemos tenido Object.create(...) durante bastante tiempo ahora, que todas las ayudas actuales navegadores (incluido IE 9 y superior.), Así que no había necesidad de utilizar la función de Crockford.

Sin embargo, resulta que si se utiliza Object.create usted debe asegurarse de que usted no hace que una gran cantidad: Esa función es mucho más lento que usando new Constructor()!

Consulte http://mrale.ph/blog/2014/07/30/constructor-vs-objectcreate.html para obtener una explicación (para el motor V8), y vea http://jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/62 para obtener una demostración de rendimiento.

Otra razón para no dar la espalda a new Constructor(...) es que ES6 classes seguramente tendrá una amplia adopción aunque solo sea por la sencilla razón de que la mayoría de los desarrolladores de Javascript provienen de idiomas basados ​​en clases.

También puedes ver este artículo, que argumenta paraObject.create: http://davidwalsh.name/javascript-objects-deconstruction

guste o no, especialmente en los proyectos que desea compartir con una amplia gama de personas (en el espacio y el tiempo - lo que significa derecha ni o con el tiempo, otras personas que se hagan cargo de usted) hay más razones para usar new.

ACTUALIZACIÓN Septiembre de 2015: Para mí, he comenzado a utilizar ES 2015 Javascript para todo, usando ya sea io.js y/o Babel. Tampoco utilizo cualquieranew en mis proyectos a excepción de los complementos de Javascript como new Error(...). Prefiero usar el enfoque funcional mucho más poderoso, ignoro por completo el sistema de objetos. [my-object].prototype y this han desaparecido por completo de mis proyectos. Durante mucho tiempo, yo era MUY escéptico de estas ideas "porque los objetos funcionan bien". Pero después de probarlo a regañadientes al comienzo de un nuevo proyecto (io.js), hizo clic y no entiendo por qué perdí dos décadas. Bien, no del todo, hoy los motores y el hardware de JS son mucho más propicios para ese estilo. Especialmente con ES 2015, recomiendo dar un estilo funcional completamente libre de cualquier this y class (la nueva palabra clave ES 2015 o el concepto completo, basado en el uso de constructorFn.prototype). Puede tomarle algunas semanas, pero una vez que "haga clic" le prometo que no volverá jamás, no voluntariamente. Es mucho más conveniente y más poderoso.

ACTUALIZACIÓN Febrero de 2018: Mientras sigo haciendo lo que escribí en la actualización anterior, ahora quiero agregar que a veces las clases están bien. No hay absolutos :-)

+0

¿Hay un "estilo funcional" particular al que te refieres? ¿O simplemente aplicando la filosofía de programación funcional a Javascript para anular algunos de estos problemas? – djfdev

+1

@ Mörre Noseshine Con respecto a su actualización (septiembre de 2015), realmente me gustaría ver cómo lo logró (sin utilizar ningún 'nuevo' o 'esto'). ¿Podría darnos un ejemplo? ¿Utiliza una función de fábrica, así como Object.create para hacer esto? Gracias. – Magrangs

+2

@Magrangs No. Simplemente llamo a funciones. El estado que almacena en los objetos que almaceno en variables de funciones, utilizando las reglas de alcance (léxicas) del lenguaje. Si el exterior necesita acceso a mi estado interno, devuelvo un objeto con (solo) métodos (que están dentro de mi alcance léxico y, por lo tanto, tengo acceso), solo un contenedor literal de objetos. Es necesario porque una función solo puede tener un valor de retorno. El alcance léxico toma el lugar de los objetos. –

3

Creo que su consejo de no usar newen total es conceptual (académico) y no debe tomarse literalmente.La clase Date es una excepción perfecta a la regla, porque ¿de qué otro modo puede obtener un objeto de fecha actual (o arbitrario) utilizando ECMAScript estándar?

Sin embargo, con respecto a no usar new con sus propios objetos personalizados, puede usar algunas estrategias. Una es usar métodos similares a los de fábrica en lugar de constructores que podrían tomar como argumento una instancia de objeto para "bendecir" en su nuevo tipo, o usar un nuevo objeto literal por defecto. Considere lo siguiente:

var newCar = function(o) { 
    o = o || {}; 
    // Add methods and properties to "o"... 
    return o; 
} 
1
function F() { return { /* your fields and methods here */ } } 
12

No usar new y seguir ciegamente Crockford es tonta.

Comprender JavaScript y escribir un buen código. La palabra clave new es Cornerstone de JavaScript OO.

Vas a perder un buen código de JavaScript evitando new.

En lugar de cortar arbitrariamente trozos grandes de su conjunto de herramientas, apúntelo y úselo correctamente.

Crockford tiene la costumbre de decir que cualquier cosa en JavaScript que alguna vez le dio un error en su código es malo.

Yo personalmente diría que "[una] n mejor estrategia de afrontamiento es ser competente".

+21

No diría que 'new' es la piedra angular de JavaScript OO. –

+0

¿Cómo se hacen objetos sin la palabra clave 'new'? – Raynos

+0

@SeanMcMillan, ¿se da cuenta de que cualquier referencia a '{}' es una referencia a 'new Object()' right? Simplemente no puede crear objetos sin la palabra clave 'new'. – Raynos

5

Esta pregunta ya ha sido formulada y respondida: Is JavaScript's "new" keyword considered harmful?

Como dijo Raynos, siguiendo ciegamente Crockford (o cualquier otra persona para esa materia) sin entender por qué dicen las cosas que hacen, es tonta.

+0

Muy buen punto, porque en ese contexto Crockford habla de la historia de JS y en un contexto más amplio de dónde viene, y cómo debería haber sido. Como terminamos con JS tal como es, utilizamos "nuevo". –

1

Puede evitar "nuevo" devolviendo un objeto anónimo y utilizando un cierre en su constructor. Esto también te ayuda a ocultar datos privados.

considerar:

function SomeCounter(start) { 

    var counter = start; 

    return { 
     getCounter : function() { 
      return counter; 
     }, 

     increaseCounter : function() { 
      counter++; 
     } 

    }; 
} 

Ahora usar esto, todo lo que tiene que hacer es

var myCounter = SomeCounter(5); 
myCounter.increaseCounter(); 
console.log(myCounter.getCounter()); //should log 6 

La belleza de esto es que no es necesario recordar usar "nuevo", pero si lo haces, no te lastimará.

var myCounter = new SomeCounter(5); //still works 
+1

Si bien esto es cierto, estaría muy molesto si llama funciones con 'nuevo' que no lo necesitan. Y convertir todos sus métodos en cierres significa que necesita almacenamiento para cada método en cada instancia de su tipo. –

12

No sé cómo evitar new Date() o new XMLHttpRequest() tampoco. Pero sé cómo evitar el uso de nuevos para mis propios tipos.

Primero, empiezo con Object.create(). Este es un método ES5, por lo que no está disponible en todas partes. Lo agrego usando el es5-shim, anuncio y estoy listo para comenzar.

Me gusta el patrón del módulo, así que empiezo envolviendo mi tipo en una función anónima autoejecutable, var Xyz = (function() {...})(). Esto significa que tengo un espacio privado para trabajar sin hacer un lío en el espacio de nombres global. Devuelvo un objeto con una función create() y una propiedad prototype. la función create() es para usuarios de mi tipo.Cuando lo desean, llaman al Xyz.create() y recuperan un nuevo objeto inicializado de mi tipo. La propiedad prototype está disponible si las personas desean heredar.

He aquí un ejemplo:

var Vehicle = (function(){ 
     var exports = {}; 
     exports.prototype = {}; 
     exports.prototype.init = function() { 
       this.mph = 5; 
     }; 
     exports.prototype.go = function() { 
       console.log("Going " + this.mph.toString() + " mph."); 
     }; 

     exports.create = function() { 
       var ret = Object.create(exports.prototype); 
       ret.init(); 
       return ret; 
     }; 

     return exports; 
})(); 

y la herencia es así:

var Car = (function() { 
     var exports = {}; 
     exports.prototype = Object.create(Vehicle.prototype); 
     exports.prototype.init = function() { 
       Vehicle.prototype.init.apply(this, arguments); 
       this.wheels = 4; 
     }; 

     exports.create = function() { 
       var ret = Object.create(exports.prototype); 
       ret.init(); 
       return ret; 
     }; 

     return exports; 

})(); 
1

le pegan con el uso de 'nuevo' para crear instancias de objetos de otras personas. Pero para su propio código, puede evitar las trampas de "esto" y "nuevo".

(Por cierto, el problema no es con 'nuevo' sí mismo. Pero un objeto que está siendo instanciado con 'nuevo' probablemente esté usando 'esto' internamente. El uso de 'esto' conduce a errores frecuentes y sutiles, porque JavaScript de 'esto' requiere la persona que llama que hacer trabajo extra para unir el llamado método al objeto correcto, y los enlaces incorrectos son difíciles de pelusa o detectarlo de cualquier manera antes de tiempo de ejecución)

versión corta:.

Para evitar el uso de 'nuevo' para crear una instancia de objetos que escriba, simplemente devuelva un objeto desde cualquier función. Dentro de esa función, conecte cualquier método a ese objeto y realice cualquier inicialización. Ya terminaste: esa función es tanto tu definición de clase como tu constructor.

versión larga, con el ejemplo:

El siguiente patrón de 'yo' evita el uso de ambos 'nuevo' y 'esto'. Si bien el patrón almacena copias de métodos en cada objeto, esto no importará a menos que esté creando muchos objetos en tiempo de ejecución. Si ese es el caso, entonces siempre puedes usar el patrón 'flyweight' de http://justjs.com/posts/this-considered-harmful. (Si bien los ejemplos de esa página todavía utilizan 'esto' un poco, que es una ligera inclinación para adaptarse a los a la siguiente distribución también.)

// this function defines a "class" -- the whole function is the constructor 
 
function Foo(x) { 
 
    // create/init whatever object type you want here 
 
    var self = {x: x}; 
 
    // if subclassing, for instance, you can do: 
 
    // var self = Baz(x); 
 

 
    // public method 
 
    self.foo = function() { 
 
     return self.x; 
 
    }; 
 

 
    // public method 
 
    self.bar = function() { 
 
     console.log(self.x); 
 
     logger(); 
 
    }; 
 

 
    // private method 
 
    function logger() { 
 
     console.log(self.x); 
 
    } 
 

 
    // ...more constructor bits can go here 
 

 
    // don't forget to return self 
 
    return self; 
 
} 
 

 
var f = Foo(1); 
 
var g = Foo(2); 
 
setTimeout(f.bar, 1000); 
 
setTimeout(g.bar, 1000); 
 

 
console.log(g.foo()); // 2 
 
g.x = 5; 
 
console.log(f.foo()); // 1 
 
console.log(g.foo()); // 5 
 

 
// ...then, 1 second later: 
 
// 1 (from f.bar) 
 
// 1 (from f.logger) 
 
// 5 (from g.bar) 
 
// 5 (from g.logger) 
 

 
// blows up if uncommented 
 
// f.logger();

Cuestiones relacionadas