2012-03-10 12 views
5

From Secrets of the Javascript Ninja (gran recorrido por cierto):¿Es una buena práctica proteger a los constructores por la falta de 'nuevo'?

// 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."); 

¿Algún guías de estilo de código del mundo real hacen todos los constructores hacen este tipo de salvaguarda (las primeras dos líneas)? Creo que los linters recibirán llamadas como User("John", "Resig") y advertirán sobre la falta de new, ¿no es así?

+3

Creo que solo tienes que molestarte con eso si estás desarrollando una biblioteca pública, donde las personas podrían llamar erróneamente 'User()' en lugar de 'new User()'. –

Respuesta

7

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

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

Esto lanzará en modo estricto si se descuida la new. Por ejemplo,

User("a", "b") 

arrojará un TypeError en Firefox. Esto le permite detectar errores sin tener que pagar la penalización de un cheque instanceof. Lanza un error de tipo incluso si lo llama fuera del modo estricto. Esto se debe a que los funcitons de modo estricto invocados directamente tienen this vinculado a undefined en lugar de global. Establecer una propiedad en undefined da como resultado una excepción TypeError.

+0

Parece un poco como un truco. Me pregunto qué tan portátil es este código? –

+0

En realidad, es al revés. Lo que Chuckj está proponiendo es la forma "correcta" de hacerlo (ES5) y la forma en que OP sugiere es el "truco". Eche un vistazo aquí: https://developer.mozilla.org/en/JavaScript/Strict_mode – Saebekassebil

+0

@Saebekassebil, la solución de chuckj podría ser la forma correcta, pero, de acuerdo con el artículo vinculado, el modo estricto no es compatible de manera consistente en todos los navegadores. Lo dejan muy claro: "Los navegadores aún no implementan de manera fiable el modo estricto, así que no dependas ciegamente de él"; "Los navegadores que no soportan el modo estricto ejecutarán código de modo estricto con un comportamiento diferente al de los navegadores que lo hagan", etc. Si el OP realmente desea implementar esta característica, creo que debería usar la solución de John Resig, que debería funcionar en todas las principales implementaciones de JavaScript. . –

2

Mientras comprueba el código en JSHint.com, que no me dijo que me estaba perdiendo 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 borra de ISN' el punto aquí.

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

No hay nada malo en poner un simple; Podría decir condición adicional que le ayuda a evitar el problema.

Hay una convención que primera letra de una función constructora debería ser de capital; que se introdujo por la misma razón para que un desarrollador JS nunca olvide la palabra clave new cuando dice que una función tiene primera letra mayúscula, pero no todos los desarrolladores pueden saberlo (no sabía hasta hace poco cuando empecé a explorar JS en serio), ahí es donde salvaguardas tu código colocándolo tú mismo si no está especificado por world world.

+0

El código emite una llamada a 'new' por sí mismo cuando no se ha llamado como constructor, por lo que funciona igual si se usa' new User (x, y) 'o' User (x, y) '. – Lucero

+0

Estaba bastante seguro de que vi en algún lugar un linter que consideraba la regla del capital de la primera letra para deducir el uso del constructor y comprobar si faltaba 'new'. Tal vez estoy alucinando. – ripper234

+1

@ ripper234: si tienes linter disponible siempre o lo usas siempre y esperas que otros que usen tu código también tengan linters, entonces no hay ningún problema, todo estará siempre bien :) – Sarfraz

1

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

Dicho esto, no me gusta este tipo de soluciones transparentes. La codificación defensiva está bien (por ejemplo, lanzar un error), pero no me gusta el código haciendo 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) utilizan la misma "función" de JavaScript aunque en la combinación de un sistema de componentes y plantillas que utilizamos internamente.

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

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

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

Esto es solo un ejemplo de cómo puede aprovechar dichos constructos de lenguaje.

1

Sólo por diversión: esto también podría ser una manera de proteger contra el olvido la palabra clave new:

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' 

En cuanto a su pregunta: yo consideraría 'Buena Práctica' bastante normativo/crítico. ¿Es una "buena práctica" comer un ojo de vaca todos los días? Si tuviera que responder a eso, diría que no, pero estoy seguro de que no todos en el mundo estarían de acuerdo con eso. Usar el patrón que demostró para eludir una palabra clave new olvidada no es bueno o malo per se. Depende de los criterios que establezca: ¿desea imponer un estricto rigor o desea salvaguardar su descuido propio o el de sus colegas? ¿El patrón será menos eficiente? ¿La rigurosidad de la respuesta de chukj funcionará en todas las situaciones y, de no ser así, será suficiente para usted?

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

usando JSLint se hacer daño

Pero por otro lado, jslint ofrece muchas posibilidades para facilitar esa fuerza usando opciones como 'Tolerar definiciones mal ordenadas', 'Tolerar constructores no capitalizados', etc. En otras palabras, incluso usando jslint, 'buena práctica' no es inequívocamente definido.

A veces la comunidad javascript se comporta como 'la denominación de la iglesia de javascript'. En él encontramos creyentes muy estrictos, para quienes la ley es la ley, y toda aberración a ella es un pecado capital (para quienes puede ser "uso estricto" fue especialmente inventado;). Otros siguen un camino más pragmático.Hay feligreses regulares y personas que nunca se presentan. Cismas emergen. Algunos planean abandonar esta iglesia y encontraron una nueva, p. donde classes están permitidos y se consideran buenos y sagrados.

Como puede ser claro ahora: creo que no existe la "buena práctica". O para expresarlo en términos más pseudoreligiosos: la buena práctica está en el ojo del espectador.

+0

Sin embargo, en realidad no es una respuesta ... Aún así, no voy a menospreciar. – ripper234

+0

Como dije, solo por diversión. Pero edité mi respuesta, para responder su pregunta también. – KooiInc

Cuestiones relacionadas