2009-12-01 6 views
0

J'ai donc passé à travers la plupart des questions ici. Aussi quelques articles bons et mauvais. Une chose que je voudrais obtenir des précisions supplémentaires est de savoir comment les variables non définies et non déclarées sont traitées.Un autre Javascript Undefined Null Question

Prenez le code ci-dessous.

var a; 

if(a == null) // True - Due to Type Coercion 

if(a == 'null') // False 

if(a === null) // False 

if(a === 'null') // False 

if(a == undefined) // True 

if(a === undefined) // True 

if(a == 'undefined') // False 

if(a === 'undefined') // False 

if(a) // False - A is undefined 

alert(typeof(a)) // undefined 

Tout ce qui précède je comprends. Mais les choses deviennent bizarres quand vous regardez une variable non déclarée. Note J'omets spécifiquement un "var b;". Toute autre opération que typeof (b) entraîne une erreur d'exécution. Pourtant, je peux comprendre la logique derrière la façon dont le langage évalue les expressions. Alors maintenant, je regarde une propriété inexistante d'un et suis vraiment confus. Je pense que c dans ce cas serait traité comme b dans l'exemple précédent mais ce n'est pas le cas. Vous devez réellement initialiser un à quelque chose, alors vous pouvez l'amener à se comporter comme b. Et l'empêcher de lancer des erreurs d'exécution.

Pourquoi est-ce le cas? Existe-t-il un traitement spécial du type indéfini ou la fonction typeof fait-elle quelque chose de manière récursive pour évaluer la propriété sub qui génère l'erreur d'exécution?

  1. Je suppose que la question pratique est ici si je vérifie un objet imbriqué c dans a.c je peux immédiatement supposer c est indéfini si un est indéfini?

  2. Et quel est le meilleur moyen alors si je voulais vérifier un objet extrêmement imbriqué pour voir si elle était définie comme x dans MyObject.Something.Something.Something.x? Je dois naviguer à travers la structure élément par élément en s'assurant que chacun existe avant de passer au suivant dans la chaîne?

+0

** NOTE * *: dans votre exemple ci-dessus, * a * serait * non défini * et non * null *. – jldupont

Répondre

2

Je peux immédiatement supposer c est indéfinie si un est indéfini?

Oui.

Je dois naviguer à travers l'élément de structure par élément faisant que chacun existe avant d'aller la suivante dans la Chanin?

Oui.

+2

typeof null === 'object' // true – NVI

+0

a est indéfini, non nul – NVI

+0

@NV: peut-être pourriez-vous le signaler à l'auteur? c'est-à-dire "si (a.c) // Erreur d'exécution - c est nul ou pas un objet" * a * étant * non défini * et non * null *. – jldupont

0

Normalement, vous ne serez pas venu sur la nécessité de tester un objet imbriqué extrêmement (plus de trois niveaux) où tout des parents pourrait être undefined.Ainsi, lorsque vous avez besoin de tester, j'écrire quelque chose comme ceci:

if(typeof(a) != 'undefined' && a.c) { 
    // Do something won't run because a is undefined 
} 

var a = {}; 

if(typeof(a) != 'undefined' && a.c) { 
    // Do something won't run because though a is defined, 
    // a.c is undefined. This expression tests to see if a.c evaluates 
    // to true, but won't throw an error even if it is 
    // undefined. 
} 

Si a.c pourrait à tout moment contenir 0 ou false mais vous avez encore besoin le test à passer, puis utilisez le test complet typeof:

var a = {}; 
a.c = false; 

if(typeof(a) != 'undefined' && typeof(a.c) != 'undefined') { 
    // Do something will run because though both a and a.c are defined. 
} 
0

La raison pour laquelle

alert(typeof(a.c)) 

résultats dans une erreur d'exécution et

alert(typeof(b)) 

ne le fait pas est que dans le premier exemple que vous essayez accéder à une propriété sur un objet non défini, ce qui provoque une erreur d'exécution avant le résultat peut être introduit dans typeof()

0

JavaScript est étrange en ce que le La valeur undefined (également typeof a === "undefined") est ce que les variables ont jusqu'à ce qu'ils reçoivent une valeur. null est une valeur distincte qui est différente de undefined. Comme le système de type dans JavaScript est lâche, il y a une coercition de type implicite qui se produit lors de la comparaison et du test des valeurs de variables.

Si une variable n'est pas déclarée, vous ne pouvez pas la référencer sans erreur, mais vous pouvez la tester avec l'opérateur typeof (et son résultat sera la chaîne "undefined"). Une variable qui a été déclarée mais non affectée peut être référencée mais contient toujours la valeur undefined. Vous pouvez toujours référencer des propriétés non définies d'objets et si elles n'ont pas été affectées, elles auront la valeur undefined.

Voir cette réponse aussi que je suis allé plus en détail sur la coercition de type JavaScript et des valeurs différentes qui sont souvent utiles à considérer vide:

Does VBScript's IsEmpty have an equivalent in JavaScript?

  1. Lors du test des objets imbriqués, si le parent est undefined (ou null) alors il n'a pas d'enfants donc aucun autre test n'est nécessaire.

  2. Pour tester en toute sécurité un objet fortement imbriqué, vous devez tester le parent le plus élevé en utilisant typeof, mais vous pouvez tester tous les enfants pour les valeurs réelles (see the testing for empty answer). En effet, le niveau supérieur n'a peut-être pas été déclaré, mais vous pouvez toujours référencer des propriétés non définies d'objets.

1

N'oubliez pas que undefined est une variable globale (!), Et vous (ou quelqu'un d'autre) peut lui assigner une valeur, de sorte que votre exemple peut se tromper ici:

if(a == undefined) // True 

if(a === undefined) // True 

Si vous avez vraiment besoin non défini, vous pouvez obtenir votre propre « copie » de celui-ci

var local_undefined; 
0

Pour profondément imbriquées enfants

try{ if(a.b.c.d.e) { 
    // do your stuff 
}}catch(e){} 

try-catch route est un plus élégant et beaucoup moins type de codage solution

Et voici un exemple:

grand="" 
a={ b:{ c:{ d:{ e:"Hello Ancestor" } } } } 

try{ if(a.b.c.d.e) { 
    grand = a.b.c.d.e 
}}catch(e){} 

alert(grand) 

juste jeter un oeil à la méthode typeof ennuyeux:

if(typeof a === undefined) { 
    if(typeof a.b === undefined) { 
     if(typeof a.b.c === undefined) { 
      if(typeof a.b.c.d === undefined) { 
       if(typeof a.b.c.d.e === undefined) { 
        // your stuff 
       } 
      } 
     } 
    } 
} 

Il pourrait être encore plus élégant et idéal après avoir enveloppé le bloc try-catch dans une fonction, mais il n'existe aucun moyen de substituer un nom de variable référencé, qui pourrait être passé comme une chaîne à une fonction avec un contenu variable . par exemple. Ce qui suit est pas possible:

function isDefined(v) { 
    if (typeof valueOfVar(v) !== undefined) 
     return true 
    else 
     return false 
} 
alert(isDefined('a.b.c.d.e')) // must be passed as a string to avoid runtime error 

il n'y a pas valueOfVar() existe en JavaScript, c'est juste un exemple


mais devinez quoi, je suis un enlightment, un mal solution :)

// a={ b:{ c:{ d:{ e:0 } } } } 

function exist(v) { 
    var local_undefined 
    try{ if(eval(v) !== local_undefined) { 
     return true 
    }}catch(e){} 
    return false 
} 
alert(exist('a.b.c.d.e')) 
0

Une mise à jour importante à l'existence() Fonction - deux fonctions supplémentaires qui utilisent exists()

Ceux-ci couvrent tout ce qui est toujours nécessaire pour vérifier si une variable /propriété/objet à un niveau d'imbrication est défini/vide/non déclaréssans jamais provoquer une erreur d'exécution JavaScript

function exists (v) { 
    var local_undefined; 
    try{ if(eval(v) !== local_undefined) { 
     return true 
    }}catch(e){} 
    return false 
} 

function empty (v) { 
    if (exists(v)) { 
     v = eval(v); 
     if (typeof v == 'object') { 
      return Object.keys(v).length === 0 
     } else if (v) 
      return false 
    } 
    return true 
} 

function value (v) { 
    var local_undefined; 
    if (exists(v)) 
     return eval(v) 
    return local_undefined 
} 


///////////////////////////////////////// 
// TEST 

ref = 'a.b.c.d.e'; 

alert(ref +' : '+ value(ref) + '\n' 
     + '\nexists\t' + exists(ref) 
     + '\nempty\t' + empty(ref) 
     + '\nvalue\t' + value(ref) 
    ); 

a = { b:{ c:{ d:{ e:"Hello Ancestor" } } } }; 
alert(ref +' : '+ value(ref) + '\n' 
     + '\nexists\t' + exists(ref) 
     + '\nempty\t' + empty(ref) 
     + '\nvalue\t' + value(ref) 
    ) 

a = { b:{ c:{ d:{ e:0 } } } }; 
alert(ref +' : '+ value(ref) + '\n' 
     + '\nexists\t' + exists(ref) 
     + '\nempty\t' + empty(ref) 
     + '\nvalue\t' + value(ref) 
    ); 

b='a'; obj={a:5}; ref='obj[b]'; 
alert(ref +' : '+ value(ref) + '\n' 
     + '\nexists\t' + exists(ref) 
     + '\nempty\t' + empty(ref) 
     + '\nvalue\t' + value(ref) 
    ); 

Cependant, ces méthodes fonctionnent que lorsque exists() empty() value() fonctions ont accès à ces variables , c'est-à-dire que la fonction et les variables sont définies dans la même portée.

qui est nécessaire pour être en mesure de tester des variables de fonction locales aussi, sinon les variables de la fonction locale qui sont déclarées avec var vont être undefined dans l'appelé exists() empty() value() fonction

Pour tester la variable locale d'une fonction sans inclure exists() empty() value(), le bloc try/catch doit être utilisé dans cette fonction


Voici un alte rnative solution mal pour tester les variables de la fonction locale Ces extraits de code peuvent être définis dans le contexte global, puis appelé avec eval()

is_ = "v_='" 
var_ = "v_='" 
get_ = "v_='" 
set_ = "v_='" 

_exists = "';\nvar local_undefined;\n" 
     + "try{ if(eval(v_) === local_undefined) false; else true }\n" 
     + "catch(e){false}\n" 

_empty = "';\nif (eval(\"'\"+_exists)) {\n" 
     + " v_ = eval(v_);\n" 
     + " if (typeof v_ == 'object') {\n" 
     + "  Object.keys(v_).length === 0;\n" 
     + " }\n\telse if (v_)\n" 
     + "  false;\n" 
     + " else true\n" 
     + "} else true" 

_value = "';\nif (eval(\"'\"+_exists))\n" 
    + " eval(v_);\n" 
    + "else local_undefined" 

_valOrEmpty = "';\n(eval(\"'\"+_exists))\n" 
    + " ? eval(\"'\"+_value) : ''" 

_valOrDefault_ = "';\n(eval(\"'\"+_exists))\n" 
    + " ? eval(\"'\"+_value) : " 

function f() { 
    var a = { b:{ c:{ d:{ e:"Hello Ancestor" } } } }; 
    ref = 'a.b.c.d.e' 
    alert(ref+'\n' 
     +'\nexists\t\t'  + eval(is_ +ref+ _exists) 
     +'\nempty\t\t'  + eval(is_ +ref+ _empty) 
     +'\nvalue\t\t'  + eval(get_ +ref+ _value) 
     +'\n' 
     +'\nvalOrEmpty\t' + eval(get_ +ref+ _valOrEmpty) 
     +'\nvalOrDefault\t' + eval(get_ +ref+ _valOrDefault_ +'"Default Value"') 
     ) 
} 

d=""; while (d.length < 20) d="—"+d; d="\n\n// "+d+"\n// " 
jsCode ='// (is_ +var+ _exists)\n\n'    + is_ +'a.b.c.d.e'+_exists 
     +d+' (is_ +var+ _empty)\n\n'     + is_ +'a.b.c.d.e'+_empty 
     +d+' (get_ +var+ _value)\n\n'     + get_+'a.b.c.d.e'+_value 
     +d+' (get_ +var+ _valOrEmpty)\n\n'   + var_+'a.b.c.d.e'+_valOrEmpty 
     +d+' (get_ +var+ _valOrDefault_ default)\n\n' + var_+'a.b.c.d.e'+_valOrDefault_+"'Default Value'" 

alert(jsCode) 

f() 
// even though that looks ugly, this is the tidiest solution 
// to the awkward 17-year old JavaScript error-handling 

Utilisez cette sagesse

if (eval(is_ +'any.var.or.property.from.local.or.global.scope'+ _exists)) { 
    // do your stuff 
}