¿Es una buena práctica proteger a los constructores de perder "nuevos"?

De Secretos de Javascript Ninja (gran tutorial por cierto):

// We need to make sure that the new operator is always used
function User(first, last){ 
  if ( !(this instanceof User) ) 
    return new User(first, last); 

  this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

assert( user, "This was defined correctly, even if it was by mistake." ); 
assert( name == "Resig", "The right name was maintained." );

¿Alguna guía de estilo de código del mundo real hace que todos los constructores hagan este tipo de protección (las dos primeras líneas)? Creo que los linters captarán llamadas como User("John", "Resig") y avisar de los desaparecidos newellos no?

preguntado el 10 de marzo de 12 a las 08:03

Creo que solo necesita preocuparse por eso si está desarrollando una biblioteca pública, donde la gente podría llamar erróneamente User() en lugar de new User(). -

4 Respuestas

Le recomiendo que use el modo estricto y pruebe en un navegador que lo admita. Por ejemplo,

function User(first, last) {
    "use strict";
    this.name = first + " " + last;
}

esto arrojará en modo estricto si descuida el new. Por ejemplo,

User("a", "b")

lanzará un TypeError en Firefox. Esto le permite detectar errores sin tener que pagar la penalización de un instanceof cheque. Lanza un error de tipo incluso si lo llama fuera del modo estricto. Esto se debe a que las funciones de modo estricto invocadas directamente tienen this obligado a undefined en lugar de globales. Establecer una propiedad en undefined resulta en un TypeError excepción.

respondido 13 mar '12, 03:03

En realidad es al revés. Lo que chuckj propone es la forma "adecuada" de hacerlo (ES5) y la forma en que OP sugiere es el "truco". Echa un vistazo aquí: developer.mozilla.org/en/JavaScript/Modo_estricto - Saebekassebil

@Saebekassebil, la solución de chuckj podría ser la forma correcta pero, según el artículo vinculado, el modo estricto no se admite de manera consistente en todos los navegadores. Lo dejan bastante claro: "Los navegadores aún no implementan de manera confiable el modo estricto, así que no dependa ciegamente de él". "Los navegadores que no admiten el modo estricto ejecutarán un código de modo estricto con un comportamiento diferente al de los navegadores que lo hacen", etc. Si el OP realmente quiere implementar esta función, creo que debería usar la solución de John Resig, que debería funcionar en todas las principales implementaciones de JavaScript. . - laurent

@chuckj: "Le recomiendo que use el modo estricto y pruebe en un navegador que lo admita". - Tienes razón, pero si el código se prueba en un navegador compatible de "uso estricto" y no se lanzan excepciones, entonces es seguro asumir que tampoco se lanzarán excepciones (debido a la falta de la nueva palabra clave) en navegadores incompatibles. - Saebekassebil

@Saebekassebil es correcto, no necesita que todos los navegadores generen una excepción, solo necesita al menos uno con el que pruebe. - chuckj

Mientras revisa su código en JSHint.com, no me dijo que me faltaba new palabra clave:

function User(first, last){ 
  this.name = first + " " + last; 
} 

var name = "Resig"; 
var user = User("John", name); 

Resultado:

Line 2: this.name = first + " " + last;
Missing "use strict" statement.

Line 5: var name = "Resig";
Redefinition of 'name'.

Pero el linter no es el punto aquí.

En cuanto a si es una buena práctica o no, sí es una buena práctica porque los humanos no son linters, pueden olvidarse de poner new palabra clave. E incluso si un linter advierte al respecto, seguiría adelante y protegería mi código.

No hay nada de malo en poner un simple; tu puedes decir extra condición que le ayuda a evitar el problema.

Hay una convención que primera letra de una función constructora debe ser capital; que se introdujo por la misma razón para que un desarrollador de JS nunca olvide el new palabra clave cuando dice que una función tiene la primera letra mayúscula, pero es posible que no todos los desarrolladores lo sepan (no lo supe hasta hace poco, cuando comencé a explorar JS en serio), ahí es donde salvaguarda su código colocándolo usted mismo si no lo especifica exterior mundo.

respondido 10 mar '12, 09:03

El código emite una llamada a new solo cuando no ha sido llamado como constructor, para que funcione igual si lo haces new User(x, y) or User(x, y). - Lucero

Estaba bastante seguro de que vi en alguna parte un linter que consideró la regla de la primera letra mayúscula para inferir el uso del constructor y verificar si faltaba new. Tal vez estoy alucinando. - ripper234

@ripper234: si siempre tiene linter disponible o lo usa siempre y espera que otros que usan su código también tengan linter, entonces no hay ningún problema, todo siempre estará bien :) - Sarfraz

@Sarfraz: bueno, actualmente no lo tenemos integrado en nuestro proceso de compilación, pero definitivamente lo haremos pronto. - ripper234

El código no avisa, solo llama new si no se usó en primer lugar, por lo que llamar User(x, y) terminará haciendo un new User(x, y) internamente.

Dicho esto, no me gusta este tipo de arreglos transparentes. La codificación defensiva está bien (por ejemplo, arrojar un error), pero no me gusta que el código haga suposiciones sobre lo que quiero hacer (o lo que podría haber hecho, como usar una letra mayúscula por error cuando quería llamar a una función llamada user).


Nosotros (como en nuestra empresa) usamos la misma "característica" de JavaScript, aunque en la combinación de un componente y un sistema de plantillas que usamos internamente.

Nuestros "componentes" (objeto JS con comportamiento adjunto a un objeto DOM específico) se crean con este estilo: new MyComponent(domObject) - esto crea el componente y lo adjunta al elemento DOM (donde puede escuchar eventos, etc.).

También usamos un motor de plantillas que se compila en funciones JS, de modo que puede llamar a la plantilla como una función como esta: myTemplate(someData) - esto convierte la plantilla dada en una cadena HTML. Las plantillas también pueden llamar a parciales, que obviamente son solo funciones normales que también devuelven cadenas HTML.

Para generar directamente el HTML para los componentes, el constructor del componente utilizará la "detección" si se llamó a través de new o no actuar como constructor o como plantilla. Cuando funciona como plantilla, agregará un atributo especial a su elemento raíz (data-component en nuestro caso) para que luego se pueda hacer una corrección donde el HTML generado se convierta en un árbol DOM y luego todos los elementos DOM con este atributo obtengan automáticamente un componente generado para ellos (usando new esta vez).

Este es solo un ejemplo de cómo puede aprovechar tales construcciones de lenguaje.

respondido 10 mar '12, 09:03

Solo por diversión: esta también podría ser una forma de protegerse contra el olvido del new palabra clave:

function User(first,last,id){
  User.Instance = User.Instance|| function(firstname,lastname,id){
      this.firstname = firstname || 'nofirstname';
      this.lastname = lastname || 'nolastname';
      this.id = id || 0;
      if (!User.Instance.prototype.nameUpper){
       User.Instance.prototype.nameUpper = function(){
                   return this.name.toUpperCase();
       };
       User.Instance.prototype.resetId = function(){
                   this.id = 0; return this;
       };
       User.Instance.prototype.toString = function(){
           return [this.id,': ',
                   this.firstname[0].toUpperCase(),
                   this.firstname.slice(1),
                   ' ',
                   this.lastname[0].toUpperCase(),
                   this.lastname.slice(1)].join('');
       };
      }
  }
  return new User.Instance(first,last,id);
}
//usage
var pete = User('pete','johanssen',1),
    jean = User('jean','harlowe',2),
    mary = new User('mary','wsnovsky',3);
console.log(pete); //=> 1: Pete Johanssen'
console.log(mary); //=> 3: Mary Wsnovsky'

Con respecto a su pregunta: consideraría 'Bueno Práctica' bastante normativa/crítica. ¿Es una 'buena práctica' comer un ojo de vaca todos los días? Si tuviera que responder eso, diría que no, pero estoy seguro de que no todos en el mundo estarían de acuerdo con eso. Usando el patrón que demostró para eludir un olvidado new La palabra clave no es buena o mala per se. Depende de los criterios que establezca: ¿quiere hacer cumplir un cierto rigor, o quiere protegerse contra su propio descuido o el de sus colegas? ¿El patrón tendrá menos rendimiento? ¿Será el rigor de chukjLa respuesta de funciona en todas las situaciones, y si no, ¿será eso suficiente para usted?

Una de las cosas que amo y El odio en javascript es su flexibilidad. JSLint, por lo que entendí de las conferencias de Douglas Crockford, está diseñado para forzarlo a un estilo de programación uniforme, en sus propias palabras:

usando jslint herirte

Pero, por otro lado, jslint ofrece muchas posibilidades para aliviar esa fuerza usando opciones como 'Tolerar definiciones desordenadas', 'Tolerar constructores no capitalizados', etc. En otras palabras, incluso usando jslint, la 'buena práctica' no se define sin ambigüedades.

A veces, la comunidad javascript se comporta como 'la iglesia de la denominación javascript'. En ella encontramos creyentes muy estrictos, para quienes la letra de la ley es la ley, y toda aberración a ella es un pecado capital (para quien puede ser "uso estricto" fue inventado especialmente ;). Otros siguen un camino más pragmático. Hay feligreses regulares y personas que nunca se presentan. Surgen cismas. Algunos planean dejar esta iglesia y fundar una nueva, por ejemplo, donde classes son permitidas y consideradas buenas y sagradas.

Como puede quedar claro a estas alturas: creo que no existen las 'buenas prácticas'. O para expresarlo en términos más pseudo-religiosos: la buena práctica está en el ojo del espectador.

contestado el 23 de mayo de 17 a las 12:05

Como dije, solo por diversión. Pero edité mi respuesta para responder también a tu pregunta. - kooiinc

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.