Caractéristiques cachées de JavaScript? [fermé]


312

Quelles "fonctionnalités cachées" de JavaScript pensez-vous que chaque programmeur devrait connaître?

Après avoir vu l'excellente qualité des réponses aux questions suivantes, j'ai pensé qu'il était temps de lui demander JavaScript.

Même si JavaScript est sans doute le langage côté client le plus important en ce moment (il suffit de demander à Google), il est surprenant de constater à quel point la plupart des développeurs Web apprécient sa puissance.


1
Vous ne vouliez pas dire "Après avoir vu les points et les points de vue de cette autre question attirés, j'ai pensé que je poserais presque exactement la même question pour renforcer la mienne"? ;-)
Bobby Jack

1
Bien sûr, pessimiste. :) J'avais envisagé d'en faire une question communautaire. De plus, après avoir obtenu un certain nombre de points, ce sont tous des rendements décroissants.
Allain Lalonde

1
Assez juste - il ne semble pas que vous ayez besoin du représentant! Je suppose que j'ai juste un gros problème avec le C # one - ne me semble pas exactement comme le type de question à laquelle ce site était destiné.
Bobby Jack

3
Ouais, peut-être pas, mais j'ai trouvé les connaissances dans les réponses excellentes. Je pense que vous auriez du mal à exposer un programmeur C # moyen à tout cela en un seul endroit si ce n'est pour SO. Cela prendrait des années à jouer avec pour arriver à la même liste durement gagnée.
Allain Lalonde le

7
J'écris JavaScript professionnellement depuis 10 ans maintenant et j'ai appris une chose ou trois de ce fil. Merci Alan!
Andrew Hedges

Réponses:


373

Vous n'avez pas besoin de définir de paramètres pour une fonction. Vous pouvez simplement utiliser l' argumentsobjet de type tableau de la fonction .

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
Il convient de noter que, bien que les arguments agissent comme un tableau, ce n'est pas un véritable tableau javascript - c'est juste un objet. Vous ne pouvez donc pas faire join (), pop (), push (), slice () et ainsi de suite. (Vous pouvez le convertir en un vrai tableau si vous le souhaitez: "var argArray = Array.prototype.slice.call (arguments);")
Jacob Mattison

51
Il convient également de noter que l'accès à l'objet Arguments est relativement coûteux - les meilleurs exemples se trouvent dans les nightlies Safari, Firefox et Chrome où le simple fait de référencer l' argumentsobjet rend l'appel d'une fonction beaucoup plus lent, par exemple. if (faux) arguments; nuira à la perf.
olliej

48
Dans le même ordre d'idées, les arguments ont une propriété "callee" qui est la fonction actuelle elle-même. Cela permet de faire de la récursivité avec des fonctions anonymes, cool!
Vincent Robert

4
@Nathan "f (x, y, z)" est meilleur que "f ([x, y, z])".
Mark Cidade

16
@Vincent Robert: veuillez noter qu'il arguments.calleeest déprécié.
ken

204

Je pourrais citer la plupart de l'excellent livre JavaScript de Douglas Crockford : The Good Parts .

Mais je n'en prend qu'un pour vous, utilisez toujours ===et !==au lieu de ==et!=

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

==n'est pas transitif. Si vous l'utilisez, ===cela donnerait false pour toutes ces déclarations comme prévu.


29
C'est dommage que tant de gens pensent que Crockford est omniscient. Certes, le gars est sur la bonne voie avec la plupart de ses critiques, mais j'arrête de donner à ses trucs une approbation générale comme le font tant de développeurs ...
Jason Bunting

21
J'appuie l'avertissement de Jason. Le livre en lui-même est très intéressant, et il donne beaucoup de bons conseils, mais DC est bien trop convaincu que sa façon de faire est la seule manière correcte, tout le reste est "défectueux". Si vous souhaitez des exemples, regardez ses réponses sur le groupe Yahoo JSLint.
Zilk

30
Utiliser === au lieu de == est un bon conseil si vous êtes confus par la frappe dynamique et que vous voulez juste qu'il soit "vraiment" égal. Ceux d'entre nous qui comprennent le typage dynamique peuvent continuer à utiliser == pour les situations où nous savons que nous voulons caster, comme dans 0 == '' ou 0 == '0'.
thomasrutter

20
Eh bien == et === ne concernent pas la frappe dynamique. == type coersion, qui est une bête différente. Si vous savez que vous souhaitez convertir en chaîne / numéro / etc, vous devez le faire explicitement.
René Saarsoo

15
Je pense que la partie la plus effrayante ==est '\n\t\r ' == 0=> true...: D
Shrikant Sharat

189

Les fonctions sont des citoyens de première classe 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

Des techniques de programmation fonctionnelles peuvent être utilisées pour écrire du javascript élégant .

En particulier, les fonctions peuvent être passées en tant que paramètres, par exemple Array.filter () accepte un rappel:

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

Vous pouvez également déclarer une fonction "privée" qui n'existe que dans le cadre d'une fonction spécifique:

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

3
Il existe trois façons de créer des fonctions en javascript: function sum (x, y, z) {return (x + y + z); } et var sum = new Function ("x", "y", "z", "return (x + y + z);"); sont les autres façons.
Marius

6
Le concept de fonctions en tant que données gagne définitivement de gros points dans mon livre.
Jason Bunting le

Je viens de mettre à jour l'exemple pour montrer comment utiliser une fonction "privée" qui n'existe que dans le cadre d'une fonction spécifique.
Chris Pietschmann

new Function()est aussi mauvais que eval. Ne pas utiliser.
Nicolás

11
pas sûr que ce soit une fonctionnalité cachée ... plus comme une fonctionnalité de base.
Claudiu

162

Vous pouvez utiliser l' opérateur in pour vérifier si une clé existe dans un objet:

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 vous trouvez les objets littéraux trop laids, vous pouvez les combiner avec la fonction sans paramètre:

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
Pas si intelligent, cela vérifie si une clé est présente, pas si une valeur l'est. x dans la liste; ne fonctionne que parce que x [1]! = null, pas parce que la valeur 1 est là.
Armin Ronacher

1
Je n'ai pas utilisé la technique depuis un moment donc j'ai oublié que j'avais déjà utilisé des littéraux d'objets auparavant. Merci pour la correction.
Mark Cidade

34
Attention aussi: l'opérateur in teste également la chaîne prototype! Si quelqu'un a mis une propriété appelée '5' sur le Object.prototype, le deuxième exemple retournerait vrai même si vous appeliez '5 dans la liste (1, 2, 3, 4)' ... Vous feriez mieux d'utiliser hasOwnProperty méthode: list (1, 2, 3, 4) .hasOwnProperty (5) retournera false, même si Object.prototype a une propriété '5'.
Martijn

3
Pour la solution la plus générale, celle qui peut tester si un objet a sa propre propriété, même s'il est nommé "hasOwnProperty", vous devez aller jusqu'à: Object.prototype.hasOwnProperty.call (object, name) ;
Kris Kowal le

1
@Kris, sauf si quelqu'un écrase Object.prototype.hasOwnProperty;)
Nick

153

Affectation de valeurs par défaut aux variables

Vous pouvez utiliser le logique ou l'opérateur ||dans une expression d'affectation pour fournir une valeur par défaut:

var a = b || c;

La avariable récupérera la valeur cque si best falsy (si est null, false, undefined, 0, empty stringou NaN), sinon ava obtenir la valeur de b.

Ceci est souvent utile dans les fonctions, lorsque vous souhaitez donner une valeur par défaut à un argument au cas où il ne serait pas fourni:

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

Exemple de repli IE dans les gestionnaires d'événements:

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

Les fonctionnalités de langage suivantes sont avec nous depuis longtemps, toutes les implémentations JavaScript les prennent en charge, mais elles ne faisaient pas partie de la spécification jusqu'à ECMAScript 5e édition :

La debuggerdéclaration

Décrit dans: § 12.15 La déclaration du débogueur

Cette instruction vous permet de placer des points d'arrêt par programmation dans votre code simplement en:

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

Si un débogueur est présent ou actif, il provoquera sa rupture immédiatement, directement sur cette ligne.

Sinon, si le débogueur n'est pas présent ou actif, cette instruction n'a aucun effet observable.

Littéraux de chaînes multilignes

Décrit dans: § 7.8.4 Littéraux de chaîne

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

Vous devez être prudent parce que le caractère à côté de la \ doit être une terminaison de ligne, si vous avez un espace après le \par exemple, le code regarder exactement la même chose, mais il soulèvera un SyntaxError.


28
Pas si c'est nul, si c'est considéré comme faux. a = 0 || 42; vous donnera 42. C'est comparable avec Python ou pas C # ?? opérateur. Si vous voulez le comportement C #, faites a = (b === null)? c: b;
Armin Ronacher

Cela fonctionne également dans Visual Studio, si vous développez sur ASP.NET :)
chakrit

2
J'aurais aimé qu'il y ait du bon || pour undefined uniquement. J'ai été mordu par cela aujourd'hui pour 0, car je voulais créer une émulation de méthode surchargée, de sorte que le dernier argument soit facultatif et qu'une valeur par défaut soit utilisée à la place.
egaga

+1 cette astuce est utilisée par l'extrait de code Google Analytics par défaut. `var _gaq = _gaq || []; »; il empêche les utilisateurs trop zélés d'écraser leur propre travail.
Yahel

2
Je ne connaissais pas la technique littérale de chaîne multiligne. C'est fantastique, merci.
Charlie Flowers

145

JavaScript n'a pas de portée de bloc (mais il a une fermeture alors appelons-le même?).

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

3
En voilà un bon. C'est une différence très importante par rapport à la plupart des langages de type C.
Martin Clarke

9
Vous pouvez toujours faire "var tmp = function () {/ * block scope * /} ();". La syntaxe est moche, mais ça marche.
Joeri Sebrechts

3
Ou vous pouvez utiliser "let" si c'est uniquement Firefox: stackoverflow.com/questions/61088/…
Eugene Yokota

10
ou simplement: (function () {var x = 2;}) (); alerte (type de x); // non défini
Pim Jager

@Pim: JSLint dit: "Déplacez l'invocation dans les parens qui contiennent la fonction.". Avec "Espéré exactement un espace entre 'fonction' et '('.".
Hello71

144

Vous pouvez accéder aux propriétés des objets avec []au lieu de.

Cela vous permet de rechercher une propriété correspondant à une variable.

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

Vous pouvez également l'utiliser pour obtenir / définir des propriétés d'objet dont le nom n'est pas un identifiant légal.

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.

Certaines personnes ne le savent pas et finissent par utiliser eval () comme ceci, ce qui est une très mauvaise idée :

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

C'est plus difficile à lire, plus difficile à trouver des erreurs dans (ne peut pas utiliser jslint), plus lent à exécuter et peut conduire à des exploits XSS.


eval est diabolique, bien que rarement nécessaire
Doug Domeny

Je n'utilise jamais eval et je me souviens quand je l'ai découvert. Ça m'a fait très plaisir.

En résumé, les propriétés des objets sont accessibles à la fois par notation par points et par indice
Russ Cam

9
Il est intéressant de noter que le référencement de points est en fait du sucre de syntaxe pour le bracketref. foo.bar, selon la spécification de toute façon, se comporte comme foo["bar"]. notez également que tout est une propriété de chaîne. même lorsque vous faites un accès au tableau array[4], le 4 est converti en chaîne (encore une fois, au moins selon les spécifications ECMAScript v3)
Claudiu

Je suppose que chaque programmeur JS devrait le savoir.
Cem Kalyoncu

144

Si vous cherchez sur Google une référence JavaScript décente sur un sujet donné, incluez le mot clé "mdc" dans votre requête et vos premiers résultats proviendront du Mozilla Developer Center. Je n'ai pas de références ou de livres hors ligne avec moi. J'utilise toujours le mot-clé "mdc" pour accéder directement à ce que je recherche. Par exemple:

Google: javascript array sort mdc
(dans la plupart des cas, vous pouvez omettre "javascript")

Mise à jour: Mozilla Developer Center a été renommé Mozilla Developer Network . L'astuce de mot clé "mdc" fonctionne toujours, mais bientôt nous devrons peut-être commencer à utiliser "mdn" à la place .


50
Wow, grande ressource. Instantanément mieux que W3Schools merdiques ...
DisgruntledGoat

11
Vous n'avez même pas besoin de Google, si vous êtes sur Firefox: tapez simplement "array mdc" dans la barre d'adresse et appuyez sur Entrée.
Sasha Chedygov

2
la meilleure partie est de savoir comment cette question de débordement de pile est sur la première page de résultats :)
Jiaaro

5
Une proposition: promouvoirjs.com , une initiative de référencement de base pour faire progresser les résultats MDC dans les résultats de recherche Google.
Yahel

3
Maintenant, c'est le centre de documentation MDN, donc le mot clé 'mdc' est toujours valide :)
Aleadam

143

Peut-être un peu évident pour certains ...

Installez Firebug et utilisez console.log ("bonjour"). Tellement mieux que d'utiliser random alert (); c'est ce dont je me souviens avoir fait beaucoup il y a quelques années.


12
N'oubliez pas de supprimer les instructions de la console avant de diffuser votre code à d'autres personnes sur lesquelles Firebug n'est peut-être pas installé.
Chris Noe

161
journal des fonctions (msg) {if (console) console.log (msg) else alert (msg)}
Josh

4
Encore mieux, faites précéder les instructions du journal par ';;;' puis minify s'en charge pour vous. (Au moins, le module Perl que j'utilise a cette fonctionnalité, et prétend que c'est courant.)
Kev

10
Josh: Cela ne fonctionnera pas car la console n'est pas définie. Vous pouvez vérifier le type de console! == "undefined" ou window.console.
Eli Gray

23
Incluez toujours: if (typeof ('console') == 'undefined') {console = {log: function () {}}; } alors vous pouvez continuer à utiliser console.log, et cela ne fait rien.
gregmac

120

Méthodes privées

Un objet peut avoir des méthodes privées.

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
Ce n'est pas vraiment une fonction privée - c'est plutôt une variable de fonction dans une portée locale.
Keith

6
Certes, mais selon toutes les définitions opérationnelles, je peux penser que c'est une méthode. C'est un bloc de code avec un nom qui a accès à l'état de l'instance et ne peut être vu que par cette instance. Quelle est votre définition d'une méthode privée?
Allain Lalonde

14
@Zach, exactement! Il est facile, après avoir passé des années à travailler avec des langages OO basés sur des classes, d'oublier qu'ils ne sont qu'une implémentation des concepts OO. Bien sûr, les différentes bibliothèques qui tentent de caser OO basé sur des quasi-classes dans JS ne sont pas utiles non plus ...
Shog9

5
Je me demande simplement, est-ce que person1 a un blog sur le droit? ;-)
travis

4
+1 pour la référence de développement arrêtée
Domenic

99

Également mentionné dans "Javascript: The Good Parts" de Crockford:

parseInt()est dangereux. Si vous lui passez une chaîne sans l'informer de la base appropriée, il peut retourner des nombres inattendus. Par exemple, parseInt('010')renvoie 8, pas 10. Passer une base à parseInt le fait fonctionner correctement:

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

13
Lorsque vous effectuez des révisions de code, recherchez toujours celui-ci. Laisser le ", 10" est une erreur courante qui passe inaperçue dans la plupart des tests.
Doug Domeny

J'ai été brûlé par le problème Radix il y a des années et je n'ai jamais oublié quelque chose d'aussi contre-intuitif en tant que tel. Une bonne chose à souligner car cela vous fera vous demander pendant un moment.
JamesEggers

4
Pourquoi ne pas utiliser Math.floorou Number? 10 === Math.floor("010"); 10 === Number("010");flotte:42 === Math.floor("42.69"); 42.69 === Number("42.69");
juste quelqu'un

1
@Infinity Si ce n'est pas déjà une réponse publiée, vous devriez. Je n'avais aucune idée aussi simple que cela de remplacer le comportement de la fonction intégrée. Bien sûr, cela devrait permettre de regarder de plus près les packages de code qu'ils empruntent à d'autres sites. Cette parseIntfonction inoffensive pourrait facilement être faite pour faire quelque chose de moins inoffensif.
bob-the-destroyer

6
@Infinity: qu'en est-il de la redéfinition du fn pour mettre en évidence «l'erreur de codage»? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
JBRWilkinson

97

Les fonctions sont des objets et peuvent donc avoir des propriétés.

fn = fonction (x) {
   // ...
}

fn.foo = 1;

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

13
Ceci est une astuce très utile. Par exemple, vous pouvez définir des valeurs par défaut en tant que propriété de la fonction. Par exemple: myfunc.delay = 100; Les utilisateurs peuvent ensuite modifier la valeur par défaut et tous les appels de fonction utiliseront la nouvelle valeur par défaut. Par exemple: myfunc.delay = 200; myfunc ();
BarelyFitz

Utile ... et dangereux!
palswim

Semble bâclé, pourquoi utiliser ceci au lieu d'une variable?
instantsetsuna

1
@instantsetsuna: Pourquoi avoir une autre variable distincte ? Comme d'habitude, cela se résume à "l'utiliser lorsque cela est approprié / utile" ;-)
VolkerK

91

Je devrais dire des fonctions auto-exécutables.

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

Parce que Javascript n'a pas de portée de bloc , vous pouvez utiliser une fonction auto-exécutable si vous souhaitez définir des variables locales:

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

Ici, myvaris n'interfère pas avec la portée globale et ne la pollue pas, et disparaît lorsque la fonction se termine.


2
À quoi cela sert-il? Vous obtenez les mêmes résultats en mettant l'alerte en dehors de la fonction.
PotatoEngineer

7
Il ne s'agit pas de l'alerte, il s'agit de définir et d'exécuter une fonction à la fois. Vous pouvez demander à cette fonction auto-exécutable de renvoyer une valeur et de transmettre la fonction comme paramètre à une autre fonction.
ScottKoon

5
@Paul c'est bon pour l'encapsulation.
Mike Robinson

22
Il est également bon pour l'étendue des blocs.
Jim Hunziker

24
Oui, j'enferme tous mes .jsfichiers dans une fonction auto-exécutable anonyme et attache tout ce que je veux globalement accessible à l' windowobjet. Empêche la pollution globale des espaces de noms.
cdmckay

83

Savoir combien de paramètres sont attendus par une fonction

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

Savoir combien de paramètres sont reçus par la fonction

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

23
Je n'ai jamais su la première partie. Agréable!
mcjabberz

1
De même, vous pouvez savoir avec combien d'arguments une fonction attend function.length.
Xavi

6
@Xavi qui est la 1ère partie de la réponse
pramodc84

79

Voici quelques choses intéressantes:

  • Comparer NaNavec quoi que ce soit (même NaN) est toujours faux, cela inclut ==, <et >.
  • NaN Signifie Pas un nombre mais si vous demandez le type, il retourne en fait un nombre.
  • Array.sort peut prendre une fonction de comparaison et est appelé par un pilote de type quicksort (dépend de l'implémentation).
  • Les expressions constantes "constantes" peuvent conserver leur état, comme la dernière chose à laquelle elles correspondent.
  • Certaines versions de JavaScript vous permettent d'accéder $0, $1, les $2membres sur une expression régulière.
  • nullne ressemble à rien d'autre. Ce n'est ni un objet, ni un booléen, ni un nombre, ni une chaîne, ni undefined. C'est un peu comme un "suppléant" undefined. (Note: typeof null == "object")
  • Dans le contexte le plus externe, thisrenvoie l'objet [Global] autrement innommable.
  • Déclarer une variable avec var, au lieu de simplement se fier à la déclaration automatique de la variable, donne au runtime une réelle chance d'optimiser l'accès à cette variable
  • La withconstruction détruira de telles optimisations
  • Les noms de variable peuvent contenir des caractères Unicode.
  • Les expressions régulières JavaScript ne sont pas réellement régulières. Ils sont basés sur les expressions rationnelles de Perl, et il est possible de construire des expressions avec des anticipations qui prennent très, très longtemps à évaluer.
  • Les blocs peuvent être étiquetés et utilisés comme cibles de break. Les boucles peuvent être étiquetées et utilisées comme cible de continue.
  • Les tableaux ne sont pas rares. La définition du 1000e élément d'un tableau par ailleurs vide devrait le remplir undefined. (dépend de la mise en œuvre)
  • if (new Boolean(false)) {...} exécutera le {...}bloc
  • Les moteurs d'expression régulière de Javascript sont spécifiques à l'implémentation: par exemple, il est possible d'écrire des expressions régulières "non portables".

[mis à jour un peu en réponse à de bons commentaires; veuillez voir les commentaires]


5
null est en fait un objet (spécial). typeof nullrenvoie "objet".
Ates Goral

4
Vous pouvez également obtenir l'objet [Global] de n'importe où comme ceci: var glb = function () {return this; } ();
Zilk

2
L'objet global en javascript dans un navigateur est l'objet fenêtre. Lorsque vous êtes dans la portée globale, faites: window.a == a;
Pim Jager le

8
"Les tableaux ne sont pas rares" dépend de l'implémentation. Si vous définissez la valeur d'un [1000] et regardez un [999], alors oui, c'est le cas undefined, mais c'est juste la valeur par défaut que vous obtenez lorsque vous recherchez un index qui n'existe pas. Si vous avez coché un [2000], ce serait également le cas undefined, mais cela ne signifie pas que vous lui avez encore alloué de la mémoire. Dans IE8, certains tableaux sont denses et certains sont clairsemés, selon la façon dont le moteur JScript se sentait à l'époque. En savoir plus ici: blogs.msdn.com/jscript/archive/2008/04/08/…
Chris Nielsen

2
@Ates et @SF: typeof renvoie "objet" pour une gamme de types différents. Mais une fois que vous savez comment cela fonctionne et quels types identifient comme «objet», il est au moins fiable et cohérent dans sa mise en œuvre.
thomasrutter du

77

Je sais que je suis en retard à la fête, mais je n'arrive pas à croire que l' +utilité de l' opérateur n'ait pas été mentionnée au-delà de "convertir quoi que ce soit en nombre". C'est peut-être à quel point une fonctionnalité est bien cachée?

// 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

Bien sûr, vous pouvez faire tout cela à la Number()place, mais l' +opérateur est tellement plus joli!

Vous pouvez également définir une valeur de retour numérique pour un objet en remplaçant la valueOf()méthode du prototype . Toute conversion de nombre effectuée sur cet objet n'entraînera pas NaN, mais la valeur de retour de la valueOf()méthode:

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

Vous pouvez faire simplement 0xFFetc., pas besoin +"0xFF".
nyuszika7h

9
@ Nyuszika7H: vous manquez en quelque sorte le point, qui contraint d'autres primitives et objets aux nombres. Bien sûr, vous pouvez simplement écrire 0xFF, de la même manière que vous pouvez écrire 1au lieu de +true. Je suggère que vous pouvez utiliser +("0x"+somevar)comme alternative à parseInt(somevar, 16), si vous le souhaitez.
Andy E

75

" Méthodes d'extension en JavaScript " via la propriété prototype.

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

Cela ajoutera une containsméthode à tous les Arrayobjets. Vous pouvez appeler cette méthode en utilisant cette syntaxe

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

18
Ceci est généralement considéré comme une mauvaise idée, car un autre code (pas le vôtre) peut émettre des hypothèses sur l'objet Array.
Chris Noe

39
Il est également généralement considéré comme une mauvaise idée de faire des hypothèses sur l'objet Array. :(
eyelidlessness

Uhmmmm .. extras de tableau javascript 1.6? Indice de? sonner des cloches?
Breton

2
@Breton: Ce n'est pas quelque chose de spécifique à la classe Array, c'est juste un exemple. J'utilise ceci pour étendre le nouveau Date (). ToString (); , permettant d'utiliser une chaîne de masque. Tout objet peut être étendu et toutes ses instances obtiennent la nouvelle méthode.
Esteban Küber

1
@Mathias: il ne s'agit pas du DOM.
dolmen

60

Pour supprimer correctement une propriété d'un objet, vous devez supprimer la propriété au lieu de simplement la définir sur undefined :

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

obj.prop2 = undefined;

for (var key in obj) {
    ...

La propriété prop2 fera toujours partie de l'itération. Si vous voulez vous débarrasser complètement de prop2 , vous devriez plutôt faire:

delete obj.prop2;

La propriété prop2 n'apparaîtra plus lorsque vous parcourez les propriétés.


3
Notez que l'instruction delete n'est pas sans ses bizarreries spécifiques au navigateur. Par exemple, cela échouera avec une grosse erreur si vous l'essayez dans IE et que l'objet n'est pas un objet JS natif (même lorsque vous supprimez une propriété que vous avez ajoutée vous-même). Il n'est pas non plus destiné à supprimer une variable, comme dans delete myvar; mais je pense que cela fonctionne dans certains navigateurs. Le code dans la réponse ci-dessus semble cependant assez sûr.
thomasrutter

au fait, undefined peut aussi être une variable! Essayez var undefined = "quelque chose"
Johann Philipp Strathausen

57

with.

Il est rarement utilisé, et franchement, rarement utile ... Mais, dans des circonstances limitées, il a ses utilisations.

Par exemple: les littéraux d'objet sont très pratiques pour configurer rapidement les propriétés d'un nouvel objet. Mais que faire si vous devez modifier la moitié des propriétés d'un objet existant?

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

// ...

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

Alan tempête souligne que cela peut être un peu dangereux: si l'objet utilisé comme contexte ne possède l' une des propriétés étant affecté à, il sera résolu dans le cadre extérieur, créant peut - être ou une variable globale d' écraser. Cela est particulièrement dangereux si vous avez l'habitude d'écrire du code pour travailler avec des objets dont les propriétés avec des valeurs par défaut ou vides ne sont pas définies:

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

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

Par conséquent, c'est probablement une bonne idée d'éviter d'utiliser l' withinstruction pour une telle affectation.

Voir aussi: Y a-t-il des utilisations légitimes pour la déclaration «with» de JavaScript?


29
La sagesse conventionnelle doit être évitée. Si l'objet utilisateur n'avait pas l'une des propriétés que vous avez mentionnées, la variable en dehors de la pseudo-portée du bloc with serait modifiée. De cette façon, il y a des bugs. Plus d'infos sur yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Alan Storm

1
Shog, les objections ne concernent pas les variables mal orthographiées, elles consistent à regarder un bloc de code et à pouvoir dire avec certitude ce que fait une ligne particulière de ce bloc. Parce que les objets Javascript sont si dynamiques, vous ne pouvez pas dire avec certitude quelles propriétés / membres il a à tout moment.
Alan Storm

2
Amen - si je voyais la déclaration "with" dans n'importe quel JS que je trouvais, je l'éliminerais et interrogerais le développeur qui l'a écrit pour m'assurer qu'il savait pourquoi ce n'était pas une bonne chose de l'utiliser ... "fonctionnalité cachée?" Plus comme «caractéristique abhorrante».
Jason Bunting le

1
considérer une chaîne plus complexe abcd "avec (abc) {d.foo = bar;} est puissant et n'est pas intrinsèquement sujet aux erreurs. La clé est de réduire la racine d'un niveau vers le haut. Et de mal orthographier un nom de variable? Vous introduisez un bug si vous faites cela que vous le fassiez, peu importe "avec"
annakata

4
Douglas Crockford a récemment déclaré que "avec" était l'une des pires parties de JavaScript dans un .NET Rocks! Podcast.
core

51

Les méthodes (ou fonctions) peuvent être appelées sur des objets qui ne sont pas du type avec lequel ils ont été conçus. C'est génial d'appeler des méthodes natives (rapides) sur des objets personnalisés.

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

Ce code se bloque car listNodesn'est pas unArray

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

Ce code fonctionne car listNodesdéfinit suffisamment de propriétés de type tableau (longueur, opérateur []) pour être utilisées par sort().


43

L'héritage prototypique (popularisé par Douglas Crockford) révolutionne complètement votre façon de penser à beaucoup de choses en Javascript.

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

C'est un tueur! Dommage que presque personne ne l'utilise.

Il vous permet de «générer» de nouvelles instances de n'importe quel objet, de les étendre, tout en conservant un lien d'héritage prototypique (en direct) vers leurs autres propriétés. Exemple:

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

Certains appelleraient cela une question de goût, mais:

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

L'opérateur trinaire peut être enchaîné pour agir comme Scheme (cond ...):

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

peut s'écrire comme ...

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

C'est très "fonctionnel", car il branche votre code sans effets secondaires. Donc au lieu de:

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

Tu peux écrire:

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

Fonctionne bien avec la récursivité aussi :)


J'aime la syntaxe de prédicat que vous donnez. Je n'ai jamais pensé à enchaîner comme ça. soigné.
Allain Lalonde

2
Euh ... JavaScript a une instruction switch (). :-)
staticsan

Je ne suis pas un grand fan des instructions de commutateur - elles sont un artefact de C, pas une programmation fonctionnelle. Dans mon exemple, une instruction switch aurait toujours besoin de trois instructions distinctes, toutes commençant par "foo =" - répétition inutile évidente.
Andrey Fedorov

14
Pour ma part, je souhaite la bienvenue à l'opérateur ternaire.
thomasrutter

8
En relisant, je voudrais souligner qu'il ne s'agit pas de "faire ressembler le code à un autre langage", mais de simplifier la signification sémantique du code: lorsque vous essayez de dire "définissez foo sur l'un des trois" choses ", c'est une déclaration qui devrait commencer par" foo = ... ", pas" si ".
Andrey Fedorov

41

Les nombres sont aussi des objets. Vous pouvez donc faire des trucs sympas comme:

// 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

OMG! Je ne connaissais pas toString (radix) ...
Ates Goral

1
Cette implémentation de timesn'est pas efficace: Math.floorest appelée à chaque fois au lieu d'une seule fois.
dolmen

33

Que diriez-vous des fermetures en JavaScript (similaires aux méthodes anonymes dans C # v2.0 +). Vous pouvez créer une fonction qui crée une fonction ou une "expression".

Exemple de fermetures :

//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
je ne suis pas sûr, mais je peux retourner (numberToCheck> lowerBound)? vrai faux; devenez simplement return (numberToCheck> lowerBound); essayant juste d'augmenter ma compréhension ...
davidsleeps

4
Je dirais que les fonctions anonymes en C # sont équivalentes à des fermetures, et non l'inverse :)
vava

11
Les fermetures et les fonctions anonymes sont des concepts séparés et distincts. Que des fonctions puissent être créées sans être nommées a des fonctions anonymes. Le fait qu'une variable dans la portée «création» soit liée à la fonction créée est une fermeture. En bref, une fermeture ressemble plus à une variable globale cachée.
slebetman

1
C'est vrai. Ce n'est que lorsque les méthodes anonymes utilisent une variable de la portée de création qu'elle est similaire à une fermeture. J'ai mis à jour l'anglais sur la réponse. Cela laisse toujours à désirer, mais je suis perdu pour le bon anglais.
Tyler

2
Je ne pense pas que ce soit l'exemple le meilleur ou le plus facile à comprendre de ce qu'est une fermeture. Je dis juste. Le point d'une fermeture est que même lorsqu'un groupe de variables semble «sortir du champ d'application», elles peuvent toujours rester disponibles pour une fonction qui a été initialement définie dans ce domaine. Dans l'exemple ci-dessus, cela signifie que la variable lowerBound est toujours accessible par cette fonction anonyme interne même lorsque la fonction externe, buildGreaterThanFunction, se termine.
thomasrutter

32

Vous pouvez également étendre (hériter) des classes et remplacer les propriétés / méthodes à l'aide de la chaîne de prototype cuillère16 mentionnée.

Dans l'exemple suivant, nous créons une classe Pet et définissons certaines propriétés. Nous substituons également la méthode .toString () héritée d'Object.

Après cela, nous créons une classe Dog qui étend Pet et remplace la méthode .toString () en modifiant à nouveau son comportement (polymorphisme). De plus, nous ajoutons d'autres propriétés à la classe enfant.

Après cela, nous vérifions la chaîne d'héritage pour montrer que Dog est toujours de type Dog, de type Pet et de type 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

Les deux réponses à cette question étaient des codes modifiés à partir d'un excellent article MSDN par Ray Djajadinata.


31

Vous pouvez intercepter des exceptions en fonction de leur type. Cité 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
}

REMARQUE: les clauses de capture conditionnelle sont une extension Netscape (et donc Mozilla / Firefox) qui ne fait pas partie de la spécification ECMAScript et ne peut donc pas être invoquée sauf sur des navigateurs particuliers.


29
Je n'ai pas pu m'en empêcher: attraper (moi si vous pouvez)
Ates Goral

6
Lisez la note de la page MDC que vous avez citée: les clauses de capture conditionnelle sont une extension Netscape (et donc Mozilla / Firefox) qui ne fait pas partie de la spécification ECMAScript et ne peut donc être invoquée que sur des navigateurs particuliers.
Jason S

31

Du haut de ma tête...

Les fonctions

arguments.callee se réfère à la fonction qui héberge la variable "arguments", elle peut donc être utilisée pour recésurer des fonctions anonymes:

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

C'est utile si vous voulez faire quelque chose comme ça:

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

Objets

Une chose intéressante à propos des membres d'objet: ils peuvent avoir n'importe quelle chaîne comme nom:

//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);
}

Cordes

String.split peut prendre des expressions régulières comme paramètres:

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

String.replace peut prendre une expression régulière comme paramètre de recherche et une fonction comme paramètre de remplacement:

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

Les choses que vous mentionnez ... Sont-elles implémentées dans tous les navigateurs?
cllpse le

4
Non, je suis quasiment sûr que Mosaic en manque la plupart.
jsight

2
Les fonctionnalités javascript, oui, elles sont implémentées dans tous les principaux navigateurs (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 et Chrome). document.querySelectorAll n'est pas encore pris en charge dans tous les navigateurs (il s'agit de la version W3C de $ () de JQuery et $$ () de Prototype)
Leo

6
arguments.calleeest obsolète et lancera une exception dans ECMAScript 5.
Hello71

pas tout à fait vrai. Une clé d'objet ne peut pas (ou plutôt ne devrait pas) utiliser la chaîne "hasOwnProperty" comme nom, car cela remplacerait la méthode d'objet intégrée.
Breton

29

Vous pouvez utiliser des objets au lieu de commutateurs la plupart du temps.

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

Mise à jour: si vous craignez que les cas évalués à l'avance soient inefficaces (pourquoi vous inquiétez-vous de l'efficacité si tôt dans la conception du programme ??), vous pouvez faire quelque chose comme ceci:

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)]();
}

C'est plus onéreux à taper (ou à lire) qu'un commutateur ou un objet, mais cela préserve les avantages de l'utilisation d'un objet au lieu d'un commutateur, détaillés dans la section commentaires ci-dessous. Ce style rend également plus simple de transformer cela en une «classe» appropriée une fois qu'il a suffisamment grandi.

update2: avec les extensions de syntaxe proposées pour ES.next, cela devient

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

3
Voilà comment Python s'en sort sans une instruction switch.
outis

2
Le problème est qu'il évalue toujours tous les cas.
Kornel

@porneL c'est vrai, mais cela confère certains avantages: C'est logiquement plus propre: les cas sont des chaînes qui sont recherchées sur une table de hachage, pas des expressions dont chacune doit être évaluée pour l'égalité jusqu'à ce que l'on retourne vrai. Ainsi, alors que davantage de «valeurs» sont évaluées, moins de «clés» sont évaluées. Les objets peuvent être générés dynamiquement et modifiés pour une évolutivité ultérieure, reflétés pour l'impression de l'interface utilisateur ou la génération de documents, et même remplacés par une fonction de «recherche» dynamique, ce qui est mieux que d'avoir des cas de copie / collage. Il n'y a aucune confusion sur les ruptures, les retombées ou les valeurs par défaut. Peut être sérialisé JSON ...
Breton

@porneL oh oui, et encore une fois pour l'évolutivité, un objet peut même facilement être transformé en une configuration externe ou un fichier de données, un changement un peu plus simple qu'avec une instruction switch - mais trivial s'il est conçu avec un objet en tête pour commencer avec.
Breton

Je sais que c'est une entrée tardive, mais à moins que vous ayez une logique de vérification de type personnalisée, quand un tableau fonctionnera-t-il jamais avec votre exemple? var arr = []; typeof arr; // object
keeganwatkins

25

Veillez à utiliser la méthode hasOwnProperty lors de l'itération à travers les propriétés d'un objet:

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

Ceci est fait pour que vous n'accédiez qu'aux propriétés directes d'un objet , et que vous n'utilisiez pas les propriétés situées en bas de la chaîne de prototype.


23

Variables privées avec une interface publique

Il utilise une petite astuce avec une définition de fonction auto-appelante. Tout ce qui est retourné à l'intérieur de l'objet est disponible dans l'interface publique, tandis que tout le reste est privé.

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
cela s'appelle le modèle de module, comme l'a surnommé Eric Miraglia sur yuiblog.com/blog/2007/06/12/module-pattern Je pense que le nom est trompeur, devrait être appelé le modèle Singleton ou quelque chose comme ça. Je pourrais également ajouter que les méthodes publiques peuvent également appeler d'autres méthodes publiques en utilisant l'objet 'this'. J'utilise ce modèle tout le temps dans mon code pour garder les choses organisées et propres.
mikeycgto
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.