Cette section donne un petit aperçu de JavaScript pour vous aider à comprendre pourquoi c'est comme ça.
ECMAScript est le nom officiel de JavaScript. Un nouveau nom est devenu nécessaire car il existe une marque déposée sur JavaScript (détenue à l'origine par Sun, maintenant par Oracle).
Le créateur de JavaScript, Brendan Eich, n'a pas eu d'autre choix que de créer le langage très rapidement (ou d'autres technologies pires auraient été adoptées par Netscape). Il a emprunté à plusieurs langages de programmation: Java (syntaxe, valeurs primitives par rapport aux objets), Scheme et AWK (fonctions de première classe), Self (héritage prototypique) et Perl et Python (chaînes de caractères, tableaux et expressions régulières).
JavaScript n'avait pas de traitement d'exception jusqu'à ECMAScript 3, ce qui explique pourquoi le langage convertit si souvent automatiquement des valeurs et échoue si souvent silencieusement: il ne pouvait pas lancer d'exceptions au départ.
D'une part, JavaScript a des bizarreries et manque de fonctionnalités (variables à champ de blocs, modules, prise en charge des sous-classes, etc.). D'autre part, il a plusieurs fonctionnalités puissantes qui vous permettent de contourner ces problèmes. Dans d'autres langages, vous apprenez les fonctionnalités du langage. Dans JavaScript, vous apprenez souvent des modèles à la place.
Compte tenu de ses influences, il n'est pas surprenant que JavaScript permette un style de programmation qui est un mélange de programmation fonctionnelle (fonctions d'ordre supérieur; carte intégrée, réduire, etc.) et de programmation orientée objet (objets, héritage).
Cette section explique les principes syntaxiques de base de JavaScript.
Quelques exemples de syntaxe:
// Two slashes start single-line comments
var x; // declaring a variable
x = 3 + y; // assigning a value to the variable `x`
foo(x, y); // calling function `foo` with parameters `x` and `y`
obj.bar(3); // calling method `bar` of object `obj`
// A conditional statement
if (x === 0) { // Is `x` equal to zero?
x = 123;
}
// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
return a + b;
}
Notez les deux utilisations différentes du signe égal:
Pour comprendre la syntaxe de JavaScript, vous devez savoir qu'il a deux grandes catégories syntaxiques: les déclarations et les expressions:
var foo;
3 * 7
La distinction entre les instructions et les expressions est mieux illustrée par le fait que JavaScript a deux façons différentes de faire if-then-else
var x;
if (y >= 0) {
x = y;
} else {
x = -y;
}
ou comme une expression:
var x = y >= 0 ? y : -y;
Vous pouvez utiliser ce dernier comme argument de fonction (mais pas le premier):
myFunction(y >= 0 ? y : -y)
Enfin, partout où JavaScript attend une instruction, vous pouvez également utiliser une expression; par exemple:
foo(7, 1);
La ligne entière est une instruction (appelée instruction d'expression), mais l'appel de la fonction foo(7, 1) est une expression.
Les points-virgules sont facultatifs dans JavaScript. Cependant, je recommande toujours de les inclure, car sinon JavaScript peut deviner mal la fin d'une instruction. Les détails sont expliqués dans l'insertion automatique de points-virgules.
Les points-virgules terminent les instructions, mais pas les blocs. Il y a un cas où vous verrez un point-virgule après un bloc: une expression de fonction est une expression qui se termine par un bloc. Si une telle expression arrive en dernier dans une instruction, elle est suivie d'un point-virgule:
// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { }; // function expr. inside var decl.
JavaScript a deux types de commentaires: les commentaires en une ligne et les commentaires en plusieurs lignes.
x++; // single-line comment
Les commentaires à plusieurs lignes sont délimités par * et *:
/* This is
a multiline
comment.
*/
Les variables en JavaScript sont déclarées avant d'être utilisées:
var foo; // declare variable `foo`
Vous pouvez déclarer une variable et assigner une valeur en même temps:
var foo = 6;
Vous pouvez également attribuer une valeur à une variable existante:
foo = 4; // change variable `foo`
Il existe des opérateurs d'affectation composés tels que +=. Les deux affectations suivantes sont équivalentes:
x += 1;
x = x + 1;
Les identifiants sont des noms qui jouent différents rôles syntaxiques en JavaScript.
En gros, le premier caractère d'un identifiant peut être n'importe quelle lettre Unicode, un signe de dollar ($) ou un soulignement (_).
arg0
_tmp
$elem
π
Les identifiants suivants sont des mots réservés, ils font partie de la syntaxe et ne peuvent pas être utilisés comme noms de variables (y compris les noms de fonctions et de paramètres):
Les trois identifiants suivants ne sont pas des mots réservés, mais vous devez les traiter comme s'ils l'étaient:
Enfin, vous devriez également rester à l'écart des noms des variables globales standard. Vous pouvez les utiliser pour les variables locales sans casser quoi que ce soit, mais votre code devient toujours déroutant.
JavaScript possède de nombreuses valeurs que l'on attend des langages de programmation: booléens, nombres, chaînes, tableaux, etc. Toutes les valeurs de JavaScript ont des propriétés. Chaque propriété a une clé (ou un nom) et une valeur. Vous pouvez penser aux propriétés comme des champs d'un enregistrement. Vous utilisez l'opérateur point (.) pour lire une propriété:
value.propKey
Par exemple, la chaîne
> var str = 'abc';
> str.length
3
Le précédent peut aussi s'écrire:
> 'abc'.length
3
The dot operator is also used to assign a value to a property:
> var obj = {}; // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123
Et vous pouvez l'utiliser pour appeler des méthodes:
> 'hello'.toUpperCase()
'HELLO'
Dans l'exemple précédent, nous avons appelé la méthode toUpperCase() sur la valeur
JavaScript fait une distinction quelque peu arbitraire entre les valeurs:
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> obj1 === obj1
true
En revanche, toutes les valeurs primitives codant la même valeur sont considérées comme les mêmes:
> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true
Les deux sections suivantes expliquent plus en détail les valeurs et les objets primitifs.
Voici toutes les valeurs primitives (ou primitives en abrégé):
Les primitifs ont les caractéristiques suivantes:
Le
> 3 === 3
true
> 'abc' === 'abc'
true
Toujours immuable Les propriétés ne peuvent pas être modifiées, ajoutées ou supprimées:
> var str = 'abc';
> str.length = 1; // try to change property `length`
> str.length // ⇒ no effect
3
> str.foo = 3; // try to create property `foo`
> str.foo // ⇒ no effect, unknown property
undefined
(La lecture d'une propriété inconnue renvoie toujours indéfinie.)
Toutes les valeurs non primitives sont des objets.
{
firstName: 'Jane',
lastName: 'Doe'
}
L'objet précédent a deux propriétés: la valeur de la propriété firstName est
[ 'apple', 'banana', 'cherry' ]
Le tableau précédent comporte trois éléments auxquels on peut accéder via des indices numériques.
/^a+b+$/
Les objets présentent les caractéristiques suivantes:
Les identités sont comparées; chaque valeur a sa propre identité:
> ({} === {}) // two different empty objects
false
> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true
Vous pouvez normalement modifier, ajouter et supprimer librement des propriétés (voir Objets uniques):
> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123
La plupart des langages de programmation ont des valeurs désignant des informations manquantes.
> var foo;
> foo
undefined
Les paramètres manquants ne sont pas définis:
> function f(x) { return x }
> f()
undefined
Si vous lisez une propriété inexistante, vous obtenez indéfinie:
> var obj = {}; // empty object
> obj.foo
undefined
undefined et null n'ont pas de propriétés, pas même les méthodes standard telles que toString().
Les fonctions vous permettent normalement d'indiquer une valeur manquante via undefined ou null. Vous pouvez faire de même via une vérification explicite:
if (x === undefined || x === null) {
...
}
Vous pouvez également exploiter le fait que les deux indéfinis et nul sont considérés comme faux:
if (!x) {
...
}
false, 0, NaN et
Il existe deux opérateurs pour catégoriser les valeurs: typeof est principalement utilisé pour les valeurs primitives, tandis que instanceof est utilisé pour les objets. Le type ressemble à ceci:
typeof value
Il renvoie une chaîne décrivant le type de valeur. Voici quelques exemples:
> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'
Le tableau suivant répertorie tous les résultats de type:
typeof null retour
L'exemple est le suivant:
value instanceof Constr
Il renvoie true si la valeur est un objet qui a été créé par le constructeur Constr (voir Constructors: Factories for Objects).
> var b = new Bar(); // object created by constructor Bar
> b instanceof Bar
true
> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object // Array is a subconstructor of Object
true
> undefined instanceof Object
false
> null instanceof Object
false
Le type booléen primitif comprend les valeurs vrai et faux.
Chaque fois que JavaScript s'attend à une valeur booléenne (par exemple, pour la condition d'une instruction if), n'importe quelle valeur peut être utilisée. Elle sera interprétée comme vraie ou fausse. Les valeurs suivantes sont interprétées comme fausse:
Toutes les autres valeurs (y compris tous les objets!) sont considérées comme vraies. Les valeurs interprétées comme fausses sont appelées faux, et les valeurs interprétées comme vraies sont appelées vraies. Boolean(), appelé comme une fonction, convertit son paramètre en boolean. Vous pouvez l'utiliser pour tester comment une valeur est interprétée:
> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true
Les opérateurs logiques binaires en JavaScript sont des courts-circuits, c'est-à-dire que si le premier opérand est suffisant pour déterminer le résultat, le deuxième opérand n'est pas évalué.
false && foo()
true || foo()
En outre, les opérateurs logiques binaires renvoient l'un de leurs opérands
Si le premier opérand est faux, retournez-le. Sinon, retournez le deuxième opérand:
> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'
Si le premier opérand est vrai, retournez-le. Sinon, retournez le deuxième opérand:
> 'abc' || 123
'abc'
> '' || 123
123
JavaScript a deux types d'égalité:
L'égalité normale considère (trop) de valeurs comme égales (les détails sont expliqués dans l'égalité normale (==,!=)), ce qui peut cacher des bugs. Par conséquent, il est toujours recommandé d'utiliser une égalité stricte.
Tous les nombres en JavaScript sont en virgule flottante:
> 1 === 1.0
true
Les numéros spéciaux sont les suivants:
NaN (
> Number('xyz') // 'xyz' can’t be converted to a number
NaN
L' infini C'est aussi une valeur d'erreur:
> 3 / 0
Infinity
> Math.pow(2, 1024) // number too large
Infinity
L'infini est plus grand que n'importe quel autre nombre (sauf NaN). De même, -Infinity est plus petit que n'importe quel autre nombre (sauf NaN). Cela rend ces nombres utiles comme valeurs par défaut (par exemple, lorsque vous recherchez un minimum ou un maximum).
JavaScript possède les opérateurs arithmétiques suivants (voir Opérateurs arithmétiques):
L'objet global Math (voir Math) fournit plus d'opérations arithmétiques, via des fonctions.
JavaScript possède également des opérateurs pour les opérations bitwise (par exemple, bitwise And; voir Bitwise Operators).
Les chaînes peuvent être créées directement via des littéraux de chaîne. Ces littéraux sont délimités par des guillemets simples ou doubles. Le backslash () évite les caractères et produit quelques caractères de contrôle. Voici quelques exemples:
'abc'
"abc"
'Did she say "Hello"?'
"Did she say \"Hello\"?"
'That\'s nice!'
"That's nice!"
'Line 1\nLine 2' // newline
'Backlash: \\'
Les caractères individuels sont accessibles par des parenthèses carrées:
> var str = 'abc';
> str[1]
'b'
La longueur de propriété compte le nombre de caractères dans la chaîne:
> 'abc'.length
3
Comme tous les primitifs, les chaînes sont immuables; vous devez créer une nouvelle chaîne si vous voulez en changer une existante.
Les chaînes sont concaténées via l'opérateur plus (+), qui convertit l'autre opérand en chaîne si l'un des opérands est une chaîne:
> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'
Pour concatener des chaînes en plusieurs étapes, utilisez l'opérateur +=:
> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'
Les chaînes ont de nombreuses méthodes utiles (voir méthodes de prototypage de chaînes). Voici quelques exemples:
> 'abc'.slice(1) // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'
> '\t xyz '.trim() // trim whitespace
'xyz'
> 'mjölnir'.toUpperCase()
'MJÖLNIR'
> 'abc'.indexOf('b') // find a string
1
> 'abc'.indexOf('x')
-1
Les conditionnels et les boucles en JavaScript sont introduits dans les sections suivantes.
L'instruction if a une clause then et une clause else optionnelle qui sont exécutées en fonction d'une condition booléenne:
if (myvar === 0) {
// then
}
if (myvar === 0) {
// then
} else {
// else
}
if (myvar === 0) {
// then
} else if (myvar === 1) {
// else-if
} else if (myvar === 2) {
// else-if
} else {
// else
}
Je recommande toujours d'utiliser des crochets (ils désignent des blocs de zéro ou plus d'instructions). Mais vous n'êtes pas obligé de le faire si une clause n'est qu'une seule instruction (il en va de même pour les instructions de flux de contrôle pour et pendant):
if (x < 0) return -x;
Ce qui suit est une instruction de commutation. La valeur de fruit détermine quel cas est exécuté:
switch (fruit) {
case 'banana':
// ...
break;
case 'apple':
// ...
break;
default: // all other cases
// ...
}
L'opérand
La boucle for a le format suivant:
for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
«statement»
init est exécuté au début de la boucle. condition est vérifiée avant chaque itération de boucle; si elle devient fausse, alors la boucle est terminée. post_iteration est exécuté après chaque itération de boucle.
Cet exemple imprime tous les éléments du tableau arr sur la console:
for (var i=0; i < arr.length; i++) {
console.log(arr[i]);
}
La boucle while continue de boucler sur son corps pendant que son état est maintenu:
// Same as for loop above:
var i = 0;
while (i < arr.length) {
console.log(arr[i]);
i++;
}
La boucle do-while continue de boucler sur son corps pendant que sa condition est maintenue.
do {
// ...
} while (condition);
Dans toutes les boucles:
Une façon de définir une fonction est via une déclaration de fonction:
function add(param1, param2) {
return param1 + param2;
}
Le code précédent définit une fonction, add, qui a deux paramètres, param1 et param2, et renvoie la somme des deux paramètres.
> add(6, 1)
7
> add('a', 'b')
'ab'
Une autre façon de définir add() est d'assigner une expression de fonction à une variable add:
var add = function (param1, param2) {
return param1 + param2;
};
Une expression de fonction produit une valeur et peut donc être utilisée pour passer directement des fonctions en tant qu'arguments à d'autres fonctions:
someOtherFunction(function (p1, p2) { ... });
Les déclarations de fonctions sont hissées
function foo() {
bar(); // OK, bar is hoisted
function bar() {
...
}
}
Notez que, bien que les déclarations var soient également hissées (voir Les variables sont hissées), les affectations qu'elles effectuent ne sont pas:
function foo() {
bar(); // Not OK, bar is still undefined
var bar = function () {
// ...
};
}
Vous pouvez appeler n'importe quelle fonction en JavaScript avec un nombre arbitraire d'arguments; le langage ne se plaindra jamais. Il rendra cependant tous les paramètres disponibles via les arguments de variables spéciales. arguments ressemble à un tableau, mais n'a aucune des méthodes de tableau:
> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0] // read element at index 0
'a'
Utilisons la fonction suivante pour explorer comment trop ou trop peu de paramètres sont manipulés en JavaScript (la fonction toArray( est montrée dans Convertir des arguments en un Array):
function f(x, y) {
console.log(x, y);
return toArray(arguments);
}
Des paramètres supplémentaires seront ignorés (sauf par les arguments):
> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]
Les paramètres manquants rendront la valeur indéfinie:
> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]
Voici un modèle commun pour attribuer des valeurs par défaut aux paramètres:
function pair(x, y) {
x = x || 0; // (1)
y = y || 0;
return [ x, y ];
}
Dans la ligne (1), l'opérateur de référence renvoie x s'il est vrai (pas nul, indéfini, etc.).
> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]
Si vous voulez imposer une arité (un nombre spécifique de paramètres), vous pouvez vérifier arguments.length:
function pair(x, y) {
if (arguments.length !== 2) {
throw new Error('Need exactly 2 arguments');
}
...
}
arguments n'est pas un tableau, il n'est qu'un tableau (voir Array-Like Objects and Generic Methods). Il a une longueur de propriété, et vous pouvez accéder à ses éléments via des indices entre parenthèses carrées. Vous ne pouvez cependant pas supprimer des éléments ou invoquer l'une des méthodes de tableau sur celui-ci. Ainsi, vous devez parfois convertir des arguments en un tableau, ce que fait la fonction suivante (elle est expliquée dans Array-Like Objects and Generic Methods):
function toArray(arrayLikeObject) {
return Array.prototype.slice.call(arrayLikeObject);
}
La manière la plus courante de traiter les exceptions (voir chapitre 14) est la suivante:
function getPerson(id) {
if (id < 0) {
throw new Error('ID must not be negative: '+id);
}
return { id: id }; // normally: retrieved from database
}
function getPersons(ids) {
var result = [];
ids.forEach(function (id) {
try {
var person = getPerson(id);
result.push(person);
} catch (exception) {
console.log(exception);
}
});
return result;
}
La clause try entoure le code critique, et la clause catch est exécutée si une exception est jetée à l'intérieur de la clause try.
> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]
Le mode strict (voir Mode strict) permet plus d'avertissements et rend JavaScript un langage plus propre (le mode non strict est parfois appelé
'use strict';
Vous pouvez également activer le mode strict par fonction:
function functionInStrictMode() {
'use strict';
}
En JavaScript, vous déclarez des variables via var avant de les utiliser:
> var x;
> x
undefined
> y
ReferenceError: y is not defined
Vous pouvez déclarer et initialiser plusieurs variables avec une seule instruction var:
var x = 1, y = 2, z = 3;
Mais je recommande d'utiliser une instruction par variable (la raison est expliquée dans la syntaxe).
var x = 1;
var y = 2;
var z = 3;
En raison de l'élévation (voir Variables Are Hoisted), il est généralement préférable de déclarer des variables au début d'une fonction.
La portée d'une variable est toujours la fonction complète (par opposition au bloc actuel).
function foo() {
var x = -512;
if (x < 0) { // (1)
var tmp = -x;
...
}
console.log(tmp); // 512
}
Nous pouvons voir que la variable tmp n'est pas limitée au bloc commençant à la ligne (1); elle existe jusqu'à la fin de la fonction.
Chaque déclaration de variable est hissée: la déclaration est déplacée au début de la fonction, mais les affectations qu'elle effectue restent posées.
function foo() {
console.log(tmp); // undefined
if (false) {
var tmp = 3; // (1)
}
}
En interne, la fonction précédente est exécutée comme ceci:
function foo() {
var tmp; // hoisted declaration
console.log(tmp);
if (false) {
tmp = 3; // assignment stays put
}
}
Chaque fonction reste connectée aux variables des fonctions qui l'entourent, même après avoir quitté la portée dans laquelle elle a été créée.
function createIncrementor(start) {
return function () { // (1)
start++;
return start;
}
}
La fonction commençant à la ligne (1) quitte le contexte dans lequel elle a été créée, mais reste connectée à une version en direct de start:
> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8
Une clôture est une fonction plus la connexion aux variables de ses champs environnants.
Parfois, vous voulez introduire une nouvelle variable scope
(function () { // open IIFE
var tmp = ...; // not a global variable
}()); // close IIFE
Assurez-vous de taper l'exemple précédent exactement comme indiqué (à l'exception des commentaires). Un IIFE est une expression de fonction qui est appelée immédiatement après sa définition.
Les clôtures gardent leurs connexions aux variables extérieures, ce qui n'est parfois pas ce que vous voulez:
var result = [];
for (var i=0; i < 5; i++) {
result.push(function () { return i }); // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)
La valeur renvoyée dans la ligne (1) est toujours la valeur actuelle de i, et non la valeur qu'elle avait lorsque la fonction a été créée. Après la fin de la boucle, i a la valeur 5, c'est pourquoi toutes les fonctions dans le tableau renvoient cette valeur. Si vous voulez que la fonction dans la ligne (1) reçoive un instantané de la valeur actuelle de i, vous pouvez utiliser un IIFE:
for (var i=0; i < 5; i++) {
(function () {
var i2 = i; // copy current i
result.push(function () { return i2 });
}());
}
Cette section couvre deux mécanismes de base orientés objet de JavaScript: les objets simples et les constructeurs (qui sont des usines d'objets, similaires aux classes dans d'autres langages).
Comme toutes les valeurs, les objets ont des propriétés. Vous pourriez, en fait, considérer un objet comme un ensemble de propriétés, où chaque propriété est une paire (clé, valeur). La clé est une chaîne, et la valeur est n'importe quelle valeur JavaScript.
En JavaScript, vous pouvez directement créer des objets simples, via des littéraux d'objets:
'use strict';
var jane = {
name: 'Jane',
describe: function () {
return 'Person named '+this.name;
}
};
Vous pouvez lire (
> jane.name // get
'Jane'
> jane.name = 'John'; // set
> jane.newProperty = 'abc'; // property created automatically
Les propriétés à valeur de fonction telles que describe sont appelées méthodes.
> jane.describe() // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'
L'opérateur in vérifie si une propriété existe:
> 'newProperty' in jane
true
> 'foo' in jane
false
Si vous lisez une propriété qui n'existe pas, vous obtenez la valeur undefined.
> jane.newProperty !== undefined
true
> jane.foo !== undefined
false
L'opérateur supprimer supprime une propriété:
> delete jane.newProperty
true
> 'newProperty' in jane
false
Une clé de propriété peut être n'importe quelle chaîne. Jusqu'à présent, nous avons vu des clés de propriété dans des littéraux d'objets et après l'opérateur de points. Cependant, vous ne pouvez les utiliser de cette façon que s'ils sont des identifiants (voir Identifiants et noms de variables). Si vous voulez utiliser d'autres chaînes comme clés, vous devez les citer dans un littérale d'objet et utiliser des crochets carrés pour obtenir et définir la propriété:
> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;
Les parenthèses carrées vous permettent également de calculer la clé d'une propriété:
> var obj = { hello: 'world' };
> var x = 'hello';
> obj[x]
'world'
> obj['hel'+'lo']
'world'
Si vous extraisez une méthode, elle perd sa connexion avec l'objet.
À titre d'exemple, revenons à l'objet précédent jane:
'use strict';
var jane = {
name: 'Jane',
describe: function () {
return 'Person named '+this.name;
}
};
Nous voulons extraire la méthode describe de jane, la mettre dans une variable func, et l'appeler.
> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined
La solution est d'utiliser la méthode bind() que toutes les fonctions ont.
> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'
Chaque fonction a sa propre variable spéciale this. Ceci est gênant si vous imbriquez une fonction à l'intérieur d'une méthode, car vous ne pouvez pas accéder à la méthode this à partir de la fonction. Voici un exemple où nous appelons forEach avec une fonction pour itérer sur un tableau:
var jane = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
// `this` is undefined here
console.log(this.name+' says hi to '+friend);
});
}
}
L'appel au journal HiToFriends produit une erreur:
> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined
Voyons deux façons de résoudre ce problème.
logHiToFriends: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' says hi to '+friend);
});
}
Ou, forEach a un deuxième paramètre qui vous permet de fournir une valeur pour cela:
logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' says hi to '+friend);
}, this);
}
Les expressions de fonction sont souvent utilisées comme arguments dans les appels de fonction en JavaScript.
Jusqu'à présent, vous pensez peut-être que les objets JavaScript ne sont que des cartes de chaînes vers des valeurs, une notion suggérée par les littéraux d'objets JavaScript, qui ressemblent aux littéraux de carte / dictionnaire d'autres langages. Cependant, les objets JavaScript prennent également en charge une fonctionnalité vraiment orientée objet: l'héritage. Cette section n'explique pas complètement comment fonctionne l'héritage JavaScript, mais elle vous montre un modèle simple pour vous aider à démarrer. Consultez le chapitre 17 si vous voulez en savoir plus.
En plus d'être des fonctions et des méthodes réelles, les fonctions jouent un autre rôle dans JavaScript: elles deviennent des constructeurs pour les objets si elles sont appelées via le nouvel opérateur.
// Set up instance data
function Point(x, y) {
this.x = x;
this.y = y;
}
// Methods
Point.prototype.dist = function () {
return Math.sqrt(this.x*this.x + this.y*this.y);
};
Nous pouvons voir qu'un constructeur a deux parties. Premièrement, la fonction Point définit les données d'instance. Deuxièmement, la propriété Point.prototype contient un objet avec les méthodes. Les données précédentes sont spécifiques à chaque instance, tandis que les données suivantes sont partagées entre toutes les instances.
Pour utiliser Point, nous l'invoquons via le nouvel opérateur:
> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301
p est une instance de Point:
> p instanceof Point
true
Les tableaux sont des séquences d'éléments auxquelles on peut accéder via des indices entiers commençant à zéro.
Les littéraux de tableau sont utiles pour créer des tableaux:
> var arr = [ 'a', 'b', 'c' ];
Le tableau précédent comporte trois éléments: les chaînes
> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]
La propriété longueur indique le nombre d'éléments d'un tableau.
> var arr = ['a', 'b'];
> arr.length
2
> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3
> arr.length = 1;
> arr
[ 'a' ]
L'opérateur in fonctionne aussi pour les tableaux:
> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false
Notez que les tableaux sont des objets et peuvent donc avoir des propriétés d'objet:
> var arr = [];
> arr.foo = 123;
> arr.foo
123
Les tableaux ont de nombreuses méthodes (voir les méthodes de prototypes de tableaux). Voici quelques exemples:
> var arr = [ 'a', 'b', 'c' ];
> arr.slice(1, 2) // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]
> arr.push('x') // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]
> arr.pop() // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]
> arr.shift() // remove first element
'a'
> arr
[ 'b', 'c' ]
> arr.unshift('x') // prepend an element
3
> arr
[ 'x', 'b', 'c' ]
> arr.indexOf('b') // find the index of an element
1
> arr.indexOf('y')
-1
> arr.join('-') // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'
Il existe plusieurs méthodes de tableau pour l'itération sur des éléments (voir Iteration (Non destructive)). Les deux plus importantes sont forEach et map.
forEach itère sur un tableau et remet l'élément actuel et son index à une fonction:
[ 'a', 'b', 'c' ].forEach(
function (elem, index) { // (1)
console.log(index + '. ' + elem);
});
Le code précédent donne la sortie suivante:
0. a
1. b
2. c
Notez que la fonction dans la ligne (1) est libre d'ignorer les arguments.
map crée un nouveau tableau en appliquant une fonction à chaque élément d'un tableau existant:
> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]
JavaScript prend en charge les expressions régulières, délimitées par des traits:
/^abc$/
/[A-Za-z0-9]+/
> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false
> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]
Le tableau retourné contient la correspondance complète à l'index 0, la capture du premier groupe à l'index 1, etc. Il existe un moyen (discuté dans RegExp.prototype.exec: Capture Groups) d'appeler cette méthode à plusieurs reprises pour obtenir toutes les correspondances.
> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'
Le premier paramètre de remplacement doit être une expression régulière avec un drapeau /g; sinon, seule la première occurrence est remplacée.
Les mathématiques sont des objets avec des fonctions arithmétiques.
> Math.abs(-2)
2
> Math.pow(3, 2) // 3 to the power of 2
9
> Math.max(2, -1, 5)
5
> Math.round(1.9)
2
> Math.PI // pre-defined constant for π
3.141592653589793
> Math.cos(Math.PI) // compute the cosine for 180°
-1