¿Características ocultas de JavaScript? [cerrado]


312

¿Qué "características ocultas" de JavaScript crees que todo programador debería saber?

Después de haber visto la excelente calidad de las respuestas a las siguientes preguntas, pensé que era hora de pedirle JavaScript.

A pesar de que JavaScript es posiblemente el lenguaje más importante del lado del cliente en este momento (solo pregúntele a Google) es sorprendente lo poco que la mayoría de los desarrolladores web aprecian lo poderoso que realmente es.


1
¿No quiso decir "Habiendo visto los puntos de representación y puntos de vista que atrajo esta otra pregunta, pensé que haría casi exactamente la misma pregunta para impulsar la mía"? ;-)
Bobby Jack

1
Claro, pesimista. :) Había considerado hacer de esto una pregunta de la comunidad. Además, después de obtener un cierto número de puntos, todo disminuye la rentabilidad.
Allain Lalonde

1
Es justo: ¡no parece que 'necesites' al representante! Supongo que solo tengo un gran problema con C # one: no me parece exactamente el tipo de pregunta para la que está destinado este sitio.
Bobby Jack

3
Sí, tal vez no, pero el conocimiento en las respuestas me pareció genial. Creo que sería difícil exponer a un programador promedio de C # a todo en un solo lugar si no fuera por SO. Llevaría años jugar con él para llegar a la misma lista duramente ganada.
Allain Lalonde

77
Llevo 10 años escribiendo JavaScript profesionalmente y aprendí una o tres cosas de este hilo. Gracias Alan!
Andrew Hedges

Respuestas:


373

No necesita definir ningún parámetro para una función. Simplemente puede usar el argumentsobjeto tipo matriz de la función .

function sum() {
    var retval = 0;
    for (var i = 0, len = arguments.length; i < len; ++i) {
        retval += arguments[i];
    }
    return retval;
}

sum(1, 2, 3) // returns 6

117
Sin embargo, vale la pena señalar que aunque los argumentos actúan como una matriz, no es una matriz de JavaScript real, es solo un objeto. Por lo tanto, no puede hacer join (), pop (), push (), slice (), etc. (Puede convertirlo en una matriz real si lo desea: "var argArray = Array.prototype.slice.call (argumentos);")
Jacob Mattison el

51
También vale la pena señalar que acceder al objeto Arguments es relativamente costoso; los mejores ejemplos se encuentran en Safari, Firefox y Chrome nightlies, donde simplemente hacer referencia al argumentsobjeto hace que llamar a una función sea mucho más lento, por ejemplo. argumentos if (falsos); lastimará perf.
olliej

48
En la misma línea, los argumentos tienen una propiedad "llamada", que es la función actual en sí misma. Esto permite hacer recursiones con funciones anónimas, ¡genial!
Vincent Robert el

44
@Nathan "f (x, y, z)" se ve mejor que "f ([x, y, z])".
Mark Cidade

16
@ Vincent Robert: tenga en cuenta que arguments.calleese está desaprobando.
Ken

204

Podría citar la mayor parte del excelente libro de Douglas Crockford JavaScript: The Good Parts .

Pero tomaré solo uno para usted, siempre use ===y en !==lugar de ==y!=

alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true

==No es transitivo. Si lo usa ===, daría falso para todas estas declaraciones como se esperaba.


29
Es una pena que tanta gente piense que Crockford lo sabe todo. De acuerdo, el tipo está en lo cierto con la mayoría de sus críticas, pero me detengo antes de darle a sus cosas un respaldo general como hacen muchos desarrolladores ...
Jason Bunting

21
Secundo la advertencia de Jason. El libro en sí es muy interesante, y lo hace dar un montón de buenos consejos, pero DC es ahora demasiado convencido de que su forma de hacer las cosas es la única manera correcta, todo lo demás es "defectuoso". Si desea algunos ejemplos, mire sus respuestas en el Grupo JSLint Yahoo.
Zilk

30
Use === en lugar de == es un buen consejo si está confundido con la escritura dinámica y solo quiere que sea "realmente" igual. Aquellos de nosotros que entendemos la escritura dinámica podemos continuar usando == para situaciones en las que sabemos que queremos lanzar, como en 0 == '' o 0 == '0'.
thomasrutter 01 de

20
Bueno, == y === no son sobre tipeo dinámico. == escribe coersion, que es una bestia diferente. Si sabe que desea convertir a cadena / número / etc., entonces debe hacerlo explícitamente.
Rene Saarsoo 05 de

15
Creo que la parte más aterradora de ==es '\n\t\r ' == 0=> true...: D
Shrikant Sharat

189

Las funciones son ciudadanos de primera clase en JavaScript:

var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };

var sum = function(x,y,z) {
  return x+y+z;
};

alert( passFunAndApply(sum,3,4,5) ); // 12

Las técnicas de programación funcional se pueden usar para escribir javascript elegante .

En particular, las funciones se pueden pasar como parámetros, por ejemplo, Array.filter () acepta una devolución de llamada:

[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]

También puede declarar una función "privada" que solo existe dentro del alcance de una función específica:

function PrintName() {
    var privateFunction = function() { return "Steve"; };
    return privateFunction();
}

3
Hay tres formas de hacer funciones en javascript: function sum (x, y, z) {return (x + y + z); } y var sum = nueva función ("x", "y", "z", "return (x + y + z);"); son las otras formas
Marius

66
El concepto de funciones como datos definitivamente gana grandes puntos en mi libro.
Jason Bunting

Acabo de actualizar el ejemplo para mostrar cómo usar una función "privada" que existe solo dentro del alcance de una función específica.
Chris Pietschmann

new Function()es tan malo como eval. No utilice.
Nicolás

11
No estoy seguro de si se trata de una función oculta ... más como una función principal
Claudiu

162

Puede usar el operador in para verificar si existe una clave en un objeto:

var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true

Si encuentra los literales de los objetos demasiado feos, puede combinarlos con la sugerencia de función sin parámetros:

function list()
 { var x = {};
   for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
   return x
 }

 5 in list(1,2,3,4,5) //true

22
No es tan inteligente, eso comprueba si una clave está presente, no si hay un valor. x en la lista; solo funciona porque x [1]! = nulo, no porque el valor 1 esté allí.
Armin Ronacher

1
No he usado la técnica en un tiempo, así que olvidé que realmente usé literales de objetos antes. Gracias por la corrección.
Mark Cidade

34
Además, tenga cuidado: ¡el operador in también prueba la cadena prototipo! Si alguien ha puesto una propiedad llamada '5' en el prototipo Object, el segundo ejemplo devolverá verdadero incluso si llamó '5 en la lista (1, 2, 3, 4)' ... Será mejor que use hasOwnProperty Método: list (1, 2, 3, 4) .hasOwnProperty (5) devolverá falso, incluso si Object.prototype tiene una propiedad '5'.
Martijn

3
Para la solución más general, una que pueda probar si un objeto tiene su propia propiedad, incluso si se llama "hasOwnProperty", debe ir a: Object.prototype.hasOwnProperty.call (objeto, nombre) ;
Kris Kowal

1
@Kris, no a menos que alguien sobrescriba Object.prototype.hasOwnProperty;)
Nick

153

Asignación de valores predeterminados a variables

Puede usar el operador lógico o ||en una expresión de asignación para proporcionar un valor predeterminado:

var a = b || c;

La avariable obtener el valor de csólo si bes Falsy (si es null, false, undefined, 0, empty string, o NaN), de lo contrario aobtendrá el valor de b.

Esto suele ser útil en funciones, cuando desea dar un valor predeterminado a un argumento en caso de que no se proporcione:

function example(arg1) {
  arg1 || (arg1 = 'default value');
}

Ejemplo de reserva de IE en controladores de eventos:

function onClick(e) {
    e || (e = window.event);
}

Las siguientes características del lenguaje han estado con nosotros durante mucho tiempo, todas las implementaciones de JavaScript las admiten, pero no formaron parte de la especificación hasta ECMAScript 5th Edition :

La debuggerdeclaración

Descrito en: § 12.15 La declaración del depurador

Esta declaración le permite poner puntos de interrupción mediante programación en su código simplemente por:

// ...
debugger;
// ...

Si un depurador está presente o activo, hará que se rompa inmediatamente, justo en esa línea.

De lo contrario, si el depurador no está presente o activo, esta declaración no tiene ningún efecto observable.

Literales de cadena multilínea

Descrito en: § 7.8.4 Literales de cadena

var str = "This is a \
really, really \
long line!";

\ Debe tener cuidado porque el carácter al lado del debe ser un terminador de línea, si tiene un espacio después del, \por ejemplo, el código se verá exactamente igual, pero generará un SyntaxError.


28
No si es nulo, si se considera falso. a = 0 || 42; te dará 42. ¿Esto es comparable con Python o no con C #? operador. Si desea el comportamiento de C #, ¿a = (b === nulo)? c: b;
Armin Ronacher

También funciona en Visual Studio, si desarrollas en ASP.NET :)
chakrit

2
Desearía que hubiera apropiado || solo para indefinidos. Esto me mordió hoy por 0, ya que quería crear una emulación del método sobrecargado, de modo que el último argumento fuera opcional y se usaría un valor predeterminado.
egaga

+1 este truco lo utiliza el fragmento predeterminado de Google Analytics. `var _gaq = _gaq || []; `; evita que los usuarios entusiastas sobrescriban su propio trabajo.
Yahel

2
No sabía acerca de la técnica literal de cadena multilínea. Eso es fantástico, gracias.
Charlie Flowers

145

JavaScript no tiene alcance de bloque (pero tiene cierre, así que vamos a llamarlo incluso?).

var x = 1;
{
   var x = 2;
}
alert(x); // outputs 2

3
Esa es buena. Es una diferencia realmente importante de la mayoría de los lenguajes similares a C.
Martin Clarke

99
Siempre puede hacer "var tmp = function () {/ * block scope * /} ();". La sintaxis es fea, pero funciona.
Joeri Sebrechts

3
O puede usar "let" si solo es Firefox: stackoverflow.com/questions/61088/…
Eugene Yokota

10
o simplemente: (function () {var x = 2;}) (); alerta (tipo de x); // undefined
Pim Jager

@Pim: JSLint dice: "Mueva la invocación a los parens que contienen la función". Junto con "Se esperaba exactamente un espacio entre 'function' y '('.".
Hello71

144

Puede acceder a las propiedades del objeto en []lugar de.

Esto le permite buscar una propiedad que coincida con una variable.

obj = {a:"test"};
var propname = "a";
var b = obj[propname];  // "test"

También puede usar esto para obtener / establecer propiedades de objeto cuyo nombre no sea un identificador legal.

obj["class"] = "test";  // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.

Algunas personas no saben esto y terminan usando eval () como este, lo cual es una muy mala idea :

var propname = "a";
var a = eval("obj." + propname);

Esto es más difícil de leer, más difícil de encontrar errores (no puede usar jslint), más lento de ejecutar y puede conducir a exploits XSS.


eval es malvado, aunque rara vez es necesario
Doug Domeny

Nunca uso eval y recuerdo cuando descubrí esto. Me hace muy feliz.

En resumen, se puede acceder a las propiedades de los objetos a través de la notación de punto y subíndice
Russ Cam

99
Es interesante notar que la referencia de puntos es en realidad azúcar de sintaxis para la ref. foo.bar, De acuerdo con la especificación de todos modos, se comporta exactamente igual foo["bar"]. También tenga en cuenta que todo es una propiedad de cadena. incluso cuando hace acceso a la matriz array[4], el 4 se convierte en una cadena (de nuevo, al menos según la especificación ECMAScript v3)
Claudiu

Supongo que todo programador de JS debería saber esto.
Cem Kalyoncu

144

Si buscas en Google una referencia decente de JavaScript sobre un tema determinado, incluye la palabra clave "mdc" en tu consulta y tus primeros resultados serán del Centro de desarrolladores de Mozilla. No llevo ninguna referencia fuera de línea o libros conmigo. Siempre uso el truco de palabras clave "mdc" para llegar directamente a lo que estoy buscando. Por ejemplo:

Google: javascript array sort mdc
(en la mayoría de los casos, puede omitir "javascript")

Actualización: Mozilla Developer Center ha cambiado su nombre a Mozilla Developer Network . El truco de la palabra clave "mdc" aún funciona, pero pronto tendremos que comenzar a usar "mdn" en su lugar .


50
Guau, gran recurso. Al instante mejor que w3schools cutres ...
DisgruntledGoat

11
Ni siquiera necesitas buscarlo en Google, si estás en Firefox: simplemente escribe "array mdc" en la barra de direcciones y presiona Enter.
Sasha Chedygov

2
la mejor parte es cómo esta pregunta de desbordamiento de pila está en la primera página de resultados :)
Jiaaro

55
Una propuesta de esto: promotejs.com , una iniciativa de SEO de base para impulsar los resultados de MDC más arriba en los resultados de búsqueda de Google.
Yahel

3
Ahora es el centro de documentación de MDN, por lo que la palabra clave 'mdc' sigue siendo válida :)
Aleadam

143

Tal vez un poco obvio para algunos ...

Instale Firebug y use console.log ("hola"). Mucho mejor que usar la alerta aleatoria (); que recuerdo haber hecho mucho hace unos años.


12
Simplemente no olvide eliminar las declaraciones de la consola antes de liberar su código a otras personas que no tengan instalado Firebug.
Chris Noe

161
función de registro (msg) {if (consola) console.log (msg) else alert (msg)}
Josh

44
Aún mejor, preceda las declaraciones de registro con ';;;' y luego minify se encarga de ti. (Al menos, el módulo Perl que uso tiene esa característica, y afirma que es un lugar común.)
Kev

10
Josh: Eso no funcionará ya que la consola no está definida. Puede verificar typeof console! == "undefined" o window.console.
Eli Gray

23
Incluya siempre: if (typeof ('console') == 'undefined') {console = {log: function () {}}; } entonces puedes continuar usando console.log, y simplemente no hace nada.
gregmac

120

Métodos privados

Un objeto puede tener métodos privados.

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    // A private method only visible from within this constructor
    function calcFullName() {
       return firstName + " " + lastName;    
    }

    // A public method available to everyone
    this.sayHello = function () {
        alert(calcFullName());
    }
}

//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();

// This fails since the method is not visible from this scope
alert(person1.calcFullName());

16
Esa no es realmente una función privada, es más una variable de función en un ámbito local.
Keith

66
Es cierto, pero según todas las definiciones operativas, puedo pensar que es un método. Es un bloque de código con un nombre que tiene acceso al estado de instancia y solo puede ser visto por esa instancia. ¿Cuál es su definición de un método privado?
Allain Lalonde

14
@Zach, exactamente! Es fácil, después de pasar años trabajando con lenguajes OO basados ​​en clases, olvidar que son simplemente una implementación de los conceptos OO. Por supuesto, las diversas bibliotecas que intentan meter OO cuasi-class en JS tampoco ayudan ...
Shog9

55
Me pregunto, ¿person1 tiene un blog de derecho? ;-)
travis

44
+1 para la referencia de desarrollo arrestado
Domenic

99

También mencionado en Crockford "Javascript: The Good Parts":

parseInt()es peligroso. Si le pasa una cadena sin informarle de la base adecuada, puede devolver números inesperados. Por ejemplo, parseInt('010')devuelve 8, no 10. Pasar una base a parseInt hace que funcione correctamente:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.

13
Al hacer revisiones de código, siempre busque esta. Dejar el ", 10" es un error común que pasa desapercibido en la mayoría de las pruebas.
Doug Domeny

Me quemé por el problema de la raíz hace años y nunca he olvidado algo tan contraintuitivo como tal. Una gran cosa para señalar, ya que te hará pensar por un tiempo.
JamesEggers

44
¿Por qué no usar Math.flooro Number? 10 === Math.floor("010"); 10 === Number("010");flotadores:42 === Math.floor("42.69"); 42.69 === Number("42.69");
sólo alguien

1
@Infinity Si aún no es una respuesta publicada, debería hacerlo. No tenía idea de que tan simple como esto anular el comportamiento de la función incorporada. Por supuesto, debería hacer que uno mire un poco más de cerca cualquier paquete de código que tome prestado de otros sitios. Esa parseIntfunción inofensiva podría hacerse fácilmente para hacer algo no tan inofensivo.
bob-the-destroyer

66
@Infinity: ¿qué hay de redefinir el fn para resaltar el 'error de codificación'? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
JBRWilkinson

97

Las funciones son objetos y, por lo tanto, pueden tener propiedades.

fn = función (x) {
   // ...
}

fn.foo = 1;

fn.next = function (y) {
  //
}

13
Este es un consejo muy útil. Por ejemplo, puede establecer valores predeterminados como una propiedad de la función. Por ejemplo: myfunc.delay = 100; Luego, los usuarios pueden cambiar el valor predeterminado y todas las llamadas a funciones utilizarán el nuevo valor predeterminado. Por ejemplo: myfunc.delay = 200; myfunc ();
BarelyFitz 01 de

Útil ... y peligroso!
palswim

Parece descuidado, ¿por qué usar esto en lugar de una variable?
instantsetsuna

1
@instantsetsuna: ¿Por qué tener otra variable separada ? Como de costumbre, esto se reduce a "usarlo cuando sea apropiado / útil" ;-)
VolkerK

91

Tendría que decir funciones autoejecutables.

(function() { alert("hi there");})();

Debido a que Javascript no tiene un alcance de bloque , puede usar una función de ejecución automática si desea definir variables locales:

(function() {
  var myvar = 2;
  alert(myvar);
})();

Aquí, myvarno interfiere con el alcance global ni lo contamina, y desaparece cuando finaliza la función.


2
¿Para qué sirve esto? Obtiene los mismos resultados al colocar la alerta fuera de la función.
PotatoEngineer

77
No se trata de la alerta, se trata de definir y ejecutar una función de una vez. Puede hacer que esa función de ejecución automática devuelva un valor y pase la función como parámetro a otra función.
ScottKoon

55
@Paul es bueno para la encapsulación.
Mike Robinson

22
También es bueno para el alcance de bloque.
Jim Hunziker

24
Sí, adjunto todos mis .jsarchivos en una función de ejecución automática anónima y adjunto todo lo que quiera accesible globalmente dentro del windowobjeto. Previene la contaminación global del espacio de nombres.
cdmckay el

83

Sepa cuántos parámetros espera una función

function add_nums(num1, num2, num3 ){
    return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.

Sepa cuántos parámetros recibe la función

function add_many_nums(){
    return arguments.length;
}    
add_many_nums(2,1,122,12,21,89); //returns 6

23
Nunca supe de la primera parte. ¡Agradable!
mcjabberz

1
Del mismo modo, puede averiguar con cuántos argumentos espera una función function.length.
Xavi

66
@Xavi, que es la primera parte de la respuesta
pramodc84

79

Aquí hay algunas cosas interesantes:

  • Comparar NaNcon cualquier cosa (incluso NaN) siempre es falso, eso incluye ==, <y >.
  • NaN Significa No es un número, pero si pregunta por el tipo, en realidad devuelve un número.
  • Array.sort puede tomar una función de comparación y es llamado por un controlador de tipo rápido (depende de la implementación).
  • La expresión regular "constantes" puede mantener el estado, como lo último que hicieron coincidir.
  • Algunas versiones de JavaScript que permiten el acceso a $0, $1, $2los miembros de una expresión regular.
  • nullEs diferente a todo lo demás. No es un objeto, un booleano, un número, una cadena, ni undefined. Es un poco como un "alternativo" undefined. (Nota: typeof null == "object")
  • En el contexto más externo, thisproduce el objeto [Global] que de otro modo no se podría nombrar.
  • Declarar una variable con var, en lugar de depender simplemente de la declaración automática de la variable, le da al tiempo de ejecución una oportunidad real de optimizar el acceso a esa variable
  • La withconstrucción destruirá tales optimizaciones
  • Los nombres de las variables pueden contener caracteres Unicode.
  • Las expresiones regulares de JavaScript no son realmente regulares. Se basan en expresiones regulares de Perl, y es posible construir expresiones con lookaheads que tardan mucho, mucho tiempo en evaluarse.
  • Los bloques se pueden etiquetar y usar como objetivos de break. Los bucles se pueden etiquetar y usar como objetivo de continue.
  • Las matrices no son escasas. Establecer el elemento 1000 de una matriz que de otra manera estaría vacía debería llenarlo undefined. (depende de la implementación)
  • if (new Boolean(false)) {...} ejecutará el {...}bloque
  • Los motores de expresión regular de Javascript son específicos de la implementación: por ejemplo, es posible escribir expresiones regulares "no portátiles".

[actualizado un poco en respuesta a buenos comentarios; por favor vea los comentarios]


55
nulo es en realidad un objeto (especial). typeof nulldevuelve "objeto".
Ates Goral el

44
También puede obtener el objeto [Global] desde cualquier lugar como este: var glb = function () {return this; } ();
Zilk

2
El objeto global en javascript en un navegador es el objeto de ventana. Cuando en el ámbito global haciendo: window.a == a;
Pim Jager

8
"Las matrices no son escasas" depende de la implementación. Si establece el valor de un [1000] y mira un [999], entonces sí undefined, pero ese es solo el valor predeterminado que obtiene cuando busca un índice que no existe. Si marcó un [2000], eso también sería undefined, pero eso no significa que todavía le haya asignado memoria. En IE8, algunas matrices son densas y otras son escasas, dependiendo de cómo se sintió el motor JScript en ese momento. Lea más aquí: blogs.msdn.com/jscript/archive/2008/04/08/…
Chris Nielsen

2
@Ates y @SF: typeof devuelve "objeto" para un rango de diferentes tipos. Pero una vez que sabe cómo funciona y qué tipos se identifican como "objeto", es al menos confiable y consistente en su implementación.
thomasrutter

77

Sé que llego tarde a la fiesta, pero no puedo creer +que la utilidad del operador no se haya mencionado más allá de "convertir algo a un número". ¿Tal vez así de escondida está una característica?

// Quick hex to dec conversion:
+"0xFF";              // -> 255

// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();

// Safer parsing than parseFloat()/parseInt()
parseInt("1,000");    // -> 1, not 1000
+"1,000";             // -> NaN, much better for testing user input
parseInt("010");      // -> 8, because of the octal literal prefix
+"010";               // -> 10, `Number()` doesn't parse octal literals 

// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null;                // -> 0;

// Boolean to integer
+true;                // -> 1;
+false;               // -> 0;

// Other useful tidbits:
+"1e10";              // -> 10000000000
+"1e-4";              // -> 0.0001
+"-12";               // -> -12

Por supuesto, puede hacer todo esto usando en su Number()lugar, ¡pero el +operador es mucho más bonito!

También puede definir un valor numérico de retorno para un objeto anulando el valueOf()método del prototipo . Cualquier conversión de número realizada en ese objeto no dará como resultado NaN, pero el valor de retorno del valueOf()método:

var rnd = {
    "valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd;               // -> 442;
+rnd;               // -> 727;
+rnd;               // -> 718;

Puedes hacerlo simplemente 0xFF, etc., no es necesario +"0xFF".
nyuszika7h

99
@ Nyuszika7H: te estás perdiendo el punto, que está obligando a otros primitivos y objetos a los números. Por supuesto, puedes escribir 0xFF, de la misma manera que puedes escribir en 1lugar de +true. Estoy sugiriendo que puede usarlo +("0x"+somevar)como una alternativa parseInt(somevar, 16), si lo desea.
Andy E

75

" Métodos de extensión en JavaScript " a través de la propiedad prototipo.

Array.prototype.contains = function(value) {  
    for (var i = 0; i < this.length; i++) {  
        if (this[i] == value) return true;  
    }  
    return false;  
}

Esto agregará un containsmétodo a todos los Arrayobjetos. Puedes llamar a este método usando esta sintaxis

var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");

18
Esto generalmente se considera una mala idea, porque otro código (no el suyo) puede hacer suposiciones sobre el objeto Array.
Chris Noe

39
En general, también se considera una mala idea hacer suposiciones sobre el objeto Array. :(
párpado

Uhmmmm .. javascript 1.6 array extras? ¿índice de? sonando alguna campana?
Breton

2
@Breton: No es algo específico de la clase Array, es solo un ejemplo. Lo uso para extender el nuevo Date (). ToString (); método, lo que permite utilizar una cadena de máscara. Cualquier objeto puede extenderse, y todas sus instancias obtienen el nuevo método.
Esteban Küber

1
@Mathias: no se trata del DOM.
dolmen

60

Para eliminar correctamente una propiedad de un objeto, debe eliminarla en lugar de establecerla como indefinida :

var obj = { prop1: 42, prop2: 43 };

obj.prop2 = undefined;

for (var key in obj) {
    ...

La propiedad prop2 seguirá siendo parte de la iteración. Si desea deshacerse por completo de prop2 , debe hacer lo siguiente:

delete obj.prop2;

La propiedad prop2 ya no aparecerá cuando esté recorriendo las propiedades.


3
Tenga en cuenta que la declaración de eliminación no está exenta de peculiaridades específicas del navegador. Por ejemplo, esto fallará con un gran error si lo prueba en IE y el objeto no es un objeto JS nativo (incluso cuando elimina una propiedad que agregó). Tampoco está destinado a eliminar una variable, como en delete myvar; pero creo que eso funciona en algunos navegadores. Sin embargo, el código en la respuesta anterior parece bastante seguro.
thomasrutter

Por cierto, ¡indefinido también puede ser una variable! Prueba var undefined = "something"
Johann Philipp Strathausen

57

with.

Raramente se usa, y francamente, rara vez es útil ... Pero, en circunstancias limitadas, tiene sus usos.

Por ejemplo: los literales de objetos son bastante útiles para configurar rápidamente propiedades en un nuevo objeto. Pero, ¿qué sucede si necesita cambiar la mitad de las propiedades de un objeto existente?

var user = 
{
   fname: 'Rocket', 
   mname: 'Aloysus',
   lname: 'Squirrel', 
   city: 'Fresno', 
   state: 'California'
};

// ...

with (user)
{
   mname = 'J';
   city = 'Frostbite Falls';
   state = 'Minnesota';
}

Alan Storm señala que esto puede ser algo peligroso: si el objeto utilizado como contexto no tiene una de las propiedades asignadas, se resolverá en el ámbito externo, posiblemente creando o sobrescribiendo una variable global. Esto es especialmente peligroso si está acostumbrado a escribir código para trabajar con objetos donde las propiedades con valores predeterminados o vacíos se dejan sin definir:

var user = 
{
   fname: "John",
// mname definition skipped - no middle name
   lname: "Doe"
};

with (user)
{
   mname = "Q"; // creates / modifies global variable "mname"
}

Por lo tanto, probablemente sea una buena idea evitar el uso de la withdeclaración para dicha asignación.

Ver también: ¿Existen usos legítimos para la declaración "con" de JavaScript?


29
La sabiduría convencional debe ser evitada. Si el objeto de usuario no tuviera una de las propiedades que mencionó, se modificaría la variable fuera del pseudo-alcance del bloque with. De esa manera se encuentran los errores. Más información en yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Alan Storm

1
Shog, las objeciones no son sobre variables mal escritas, sino sobre mirar un bloque de código y poder decir con certeza qué hace cualquier línea en particular en ese bloque. Debido a que los objetos Javascript son tan dinámicos, no puedes decir con certeza qué propiedades / miembros tiene en ningún momento.
Alan Storm

2
Amén: si vi la declaración "con" en cualquier JS que encontré, la eliminaría y le preguntaría al desarrollador que la escribió para asegurarse de que sabe por qué no es bueno usarla ... "¿característica oculta?" Más como "característica aborrecible".
Jason Bunting

1
considere una cadena más compleja abcd "con (abc) {d.foo = bar;} es potente y no es inherentemente propensa a errores. La clave es reducir la raíz un nivel más arriba. ¿Y escribir mal un nombre de variable? ¿Está introduciendo un error? si haces eso donde sea que lo hagas, independientemente de "con".
annakata

44
Douglas Crockford dijo recientemente que "con" es una de las peores partes de JavaScript en un .NET Rocks. podcast
núcleo

51

Se pueden invocar métodos (o funciones) en objetos que no son del tipo con el que fueron diseñados para trabajar. Es genial llamar a métodos nativos (rápidos) en objetos personalizados.

var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });

Este código falla porque listNodesno es unArray

Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);

Este código funciona porque listNodesdefine suficientes propiedades de tipo matriz (longitud, operador []) para ser utilizadas por sort().


43

La herencia de prototipos (popularizada por Douglas Crockford) revoluciona por completo su forma de pensar acerca de muchas cosas en Javascript.

Object.beget = (function(Function){
    return function(Object){
        Function.prototype = Object;
        return new Function;
    }
})(function(){});

Es un asesino! Lástima cómo casi nadie lo usa.

Le permite "engendrar" nuevas instancias de cualquier objeto, extenderlas, mientras mantiene un enlace de herencia (en vivo) prototípico a sus otras propiedades. Ejemplo:

var A = {
  foo : 'greetings'
};  
var B = Object.beget(A);

alert(B.foo);     // 'greetings'

// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo);     // 'hello'

A.bar = 'world';
alert(B.bar);     // 'world'


// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo);     // 'hello'

B.bar = 'universe';
alert(A.bar);     // 'world'

42

Algunos llamarían a esto una cuestión de gustos, pero:

aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }

El operador trinario se puede encadenar para actuar como el esquema (cond ...):

(cond (predicate  (action  ...))
      (predicate2 (action2 ...))
      (#t         default ))

Se puede escribir como...

predicate  ? action( ... ) :
predicate2 ? action2( ... ) :
             default;

Esto es muy "funcional", ya que ramifica su código sin efectos secundarios. Entonces en lugar de:

if (predicate) {
  foo = "one";
} else if (predicate2) {
  foo = "two";
} else {
  foo = "default";
}

Puedes escribir:

foo = predicate  ? "one" :
      predicate2 ? "two" :
                   "default";

Funciona bien con la recursividad, también :)


Me gusta la sintaxis del predicado que das. Nunca pensé en encadenar así. ordenado.
Allain Lalonde

2
Uh ... JavaScript tiene una declaración switch (). :-)
staticsan

No soy un gran admirador de las declaraciones de cambio: son un artefacto de C, no una programación funcional. En mi ejemplo, una declaración de cambio aún necesitaría tres declaraciones separadas, todas comenzando con "foo =" - repetición obvia innecesaria.
Andrey Fedorov

14
Yo, por mi parte, doy la bienvenida al operador ternario.
thomasrutter

8
En la relectura, me gustaría señalar que esto no es "hacer que el código se vea como otro idioma", sino realmente simplificar el significado semántico del código: cuando intentas decir "establece foo en uno de tres cosas ", esa es una declaración que debería comenzar con" foo = ... ", no" if ".
Andrey Fedorov

41

Los números también son objetos. Entonces puedes hacer cosas geniales como:

// convert to base 2
(5).toString(2) // returns "101"

// provide built in iteration
Number.prototype.times = function(funct){
  if(typeof funct === 'function') {
    for(var i = 0;i < Math.floor(this);i++) {
      funct(i);
    }
  }
  return this;
}


(5).times(function(i){
  string += i+" ";
});
// string now equals "0 1 2 3 4 "

var x = 1000;

x.times(function(i){
  document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document

¡DIOS MIO! No sabía sobre toString (radix) ...
Ates Goral

1
Esa implementación de timesno es eficiente: Math.floorse llama cada vez en lugar de solo una vez.
dolmen

33

¿Qué hay de los cierres en JavaScript (similar a los métodos anónimos en C # v2.0 +)? Puede crear una función que cree una función o "expresión".

Ejemplo de cierres :

//Takes a function that filters numbers and calls the function on 
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
  var filteredNumbers = [];

  for (var index = 0; index < numbers.length; index++)
  {
    if (filterFunction(numbers[index]) == true)
    {
      filteredNumbers.push(numbers[index]);
    }
  }
  return filteredNumbers;
}

//Creates a function (closure) that will remember the value "lowerBound" 
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
  return function (numberToCheck) {
    return (numberToCheck > lowerBound) ? true : false;
  };
}

var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];

var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);

numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);

numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);

1
¿No estoy seguro, pero puedo devolver (numberToCheck> lowerBound)? verdadero Falso; simplemente conviértase en return (numberToCheck> lowerBound); tratando de aumentar mi conocimiento ...
davidsleeps

44
Yo diría que las funciones anónimas en C # son equivalentes a los cierres, no al revés :)
vava

11
Los cierres y las funciones anónimas son conceptos separados y distintos. Que las funciones se pueden crear sin ser nombrado es tener funciones anónimas. Que una variable en el ámbito de 'creación' esté vinculada con la función creada es un cierre. En resumen, un cierre es más como una variable global oculta.
slebetman

1
Es verdad. Solo cuando los métodos anónimos hacen uso de una variable del ámbito de creación es similar a un cierre. He actualizado el inglés en la respuesta. Todavía deja algo que desear, pero estoy perdido por el inglés correcto.
Tyler

2
No creo que este sea el mejor o más fácil de entender ejemplo de lo que es un cierre. Solo digo. El objetivo de un cierre es que, incluso cuando un grupo de variables parece estar 'fuera de alcance', pueden permanecer disponibles para una función que se definió originalmente dentro de ese alcance. En el ejemplo anterior, eso significa que la función lowerBound aún es accesible por esa función anónima interna, incluso cuando la función externa, buildGreaterThanFunction, finaliza.
thomasrutter

32

También puede extender (heredar) clases y anular propiedades / métodos utilizando el prototipo de cadena cuchara aludida.

En el siguiente ejemplo creamos una clase Pet y definimos algunas propiedades. También anulamos el método .toString () heredado de Object.

Después de esto, creamos una clase Dog que extiende Pet y anula el método .toString () cambiando nuevamente su comportamiento (polimorfismo). Además, agregamos algunas otras propiedades a la clase secundaria.

Después de esto, verificamos la cadena de herencia para mostrar que Dog todavía es de tipo Dog, de tipo Pet y de tipo Object.

// Defines a Pet class constructor 
function Pet(name) 
{
    this.getName = function() { return name; };
    this.setName = function(newName) { name = newName; };
}

// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function() 
{
    return 'This pets name is: ' + this.getName();
};
// end of class Pet

// Define Dog class constructor (Dog : Pet) 
function Dog(name, breed) 
{
    // think Dog : base(name) 
    Pet.call(this, name);
    this.getBreed = function() { return breed; };
}

// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();

// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;

// Now we override Pet.prototype.toString
Dog.prototype.toString = function() 
{
    return 'This dogs name is: ' + this.getName() + 
        ', and its breed is: ' + this.getBreed();
};
// end of class Dog

var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);

// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true

Ambas respuestas a esta pregunta fueron códigos modificados de un gran artículo de MSDN de Ray Djajadinata.


31

Puede detectar excepciones según su tipo. Citado de MDC :

try {
   myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
   // statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
   // statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
   // statements to handle EvalError exceptions
} catch (e) {
   // statements to handle any unspecified exceptions
   logMyErrors(e); // pass exception object to error handler
}

NOTA: Las cláusulas de captura condicional son una extensión de Netscape (y, por lo tanto, Mozilla / Firefox) que no forma parte de la especificación ECMAScript y, por lo tanto, no se puede confiar, excepto en navegadores particulares.


29
No pude evitarlo: Atrápame (a mí si puedes)
Ates Goral

66
Lea la nota de la página de MDC que citó: las cláusulas de captura condicional son una extensión de Netscape (y, por lo tanto, Mozilla / Firefox) que no forma parte de la especificación ECMAScript y, por lo tanto, no se puede confiar excepto en navegadores particulares.
Jason S

31

La parte superior de mi cabeza...

Las funciones

argumentos.callee se refiere a la función que aloja la variable "argumentos", por lo que puede usarse para recurrir funciones anónimas:

var recurse = function() {
  if (condition) arguments.callee(); //calls recurse() again
}

Eso es útil si quieres hacer algo como esto:

//do something to all array items within an array recursively
myArray.forEach(function(item) {
  if (item instanceof Array) item.forEach(arguments.callee)
  else {/*...*/}
})

Objetos

Una cosa interesante sobre los miembros del objeto: pueden tener cualquier cadena como sus nombres:

//these are normal object members
var obj = {
  a : function() {},
  b : function() {}
}
//but we can do this too
var rules = {
  ".layout .widget" : function(element) {},
  "a[href]" : function(element) {}
}
/* 
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
  var elements = document.querySelectorAll(rules[item]);
  for (var e, i = 0; e = elements[i++];) rules[item](e);
}

Instrumentos de cuerda

String.split puede tomar expresiones regulares como parámetros:

"hello world   with  spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]

String.replace puede tomar una expresión regular como parámetro de búsqueda y una función como parámetro de reemplazo:

var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"

Las cosas que mencionas ... ¿Se implementan en todos los navegadores?
cllpse

44
No. Estoy bastante seguro de que Mosaic carece de la mayoría de ellos.
jsight 01 de

2
Las características de JavaScript, sí, se implementan en todos los principales navegadores (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 y Chrome). document.querySelectorAll aún no es compatible con todos los navegadores (es la versión W3C de JQuery's $ () y Prototype's $$ ())
Leo

66
arguments.calleeestá en desuso y arrojará una excepción en ECMAScript 5.
Hola71

No del todo cierto. Una clave de objeto no puede (o más bien, no debe) usar la cadena "hasOwnProperty" como nombre, ya que eso anularía el método de objeto incorporado.
Breton

29

Puede usar objetos en lugar de interruptores la mayor parte del tiempo.

function getInnerText(o){
    return o === null? null : {
        string: o,
        array: o.map(getInnerText).join(""),
        object:getInnerText(o["childNodes"])
    }[typeis(o)];
}

Actualización: si le preocupa que los casos que evalúan por adelantado sean ineficientes (¿por qué le preocupa la eficiencia desde el principio en el diseño del programa?), Puede hacer algo como esto:

function getInnerText(o){
    return o === null? null : {
        string: function() { return o;},
        array: function() { return o.map(getInnerText).join(""); },
        object: function () { return getInnerText(o["childNodes"]; ) }
    }[typeis(o)]();
}

Esto es más oneroso de escribir (o leer) que un interruptor o un objeto, pero conserva los beneficios de usar un objeto en lugar de un interruptor, detallado en la sección de comentarios a continuación. Este estilo también hace que sea más sencillo convertir esto en una "clase" adecuada una vez que crece lo suficiente.

update2: con extensiones de sintaxis propuestas para ES.next, esto se convierte en

let getInnerText = o -> ({
    string: o -> o,
    array: o -> o.map(getInnerText).join(""),
    object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);

3
Así es como Python pasa sin una declaración de cambio.
outis

2
El problema es que siempre evalúa todos los casos.
Kornel

@porneL esto es cierto, pero confiere algunos beneficios: es lógicamente más limpio: los casos son cadenas que se buscan en una tabla hash, no expresiones en las que cada uno debe ser evaluado para la igualdad hasta que uno vuelva a ser verdadero. Entonces, mientras se evalúan más "valores", se evalúan menos "claves". Los objetos pueden generarse dinámicamente y modificarse para una escalabilidad posterior, reflejarse para imprimir la IU o generar documentos, e incluso reemplazarse por una función dinámica de "búsqueda", que es mejor que tener casos copiados / pegados. No hay confusión sobre interrupciones, fallos o valores predeterminados. Puede ser serializado JSON ...
Breton

@porneL oh sí, y de nuevo en lo que respecta a la escalabilidad, un objeto puede incluso convertirse fácilmente en una configuración externa o archivo de datos, un cambio algo más directo que con una declaración de cambio, pero trivial si está diseñado con un objeto en mente para comenzar con.
Breton

Sé que esta es una entrada tardía, pero a menos que tenga alguna lógica de verificación de tipo personalizada, ¿cuándo va a funcionar una matriz con su ejemplo? var arr = []; typeof arr; // object
keeganwatkins

25

Asegúrese de utilizar el método hasOwnProperty al recorrer las propiedades de un objeto:

for (p in anObject) {
    if (anObject.hasOwnProperty(p)) {
        //Do stuff with p here
    }
}

Esto se hace para que solo acceda a las propiedades directas de unObjeto y no use las propiedades que se encuentran en la cadena del prototipo.


23

Variables privadas con una interfaz pública

Utiliza un pequeño truco con una definición de función de llamada automática. Todo lo que se devuelve dentro del objeto está disponible en la interfaz pública, mientras que todo lo demás es privado.

var test = function () {
    //private members
    var x = 1;
    var y = function () {
        return x * 2;
    };
    //public interface
    return {
        setx : function (newx) {
            x = newx;
        },
        gety : function () {
            return y();
        }
    }
}();

assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());

1
esto se llama patrón de módulo, como Eric Miraglia lo denominó en yuiblog.com/blog/2007/06/12/module-pattern. Creo que el nombre es engañoso, debería llamarse Patrón Singleton o algo así. También podría agregar que los métodos públicos también pueden llamar a otros métodos públicos mediante el uso de 'este' objeto. Uso este patrón todo el tiempo en mi código para mantener las cosas organizadas y limpias.
mikeycgto
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.