Еще один неопределенный нулевой вопрос по Javascript
-
11-09-2019 - |
Вопрос
Итак, я ответил на большинство вопросов здесь.Также довольно много статей хороших и плохих.
Еще мне хотелось бы получить дополнительные разъяснения по поводу того, как обрабатываются неопределенные и необъявленные переменные.
Возьмите код ниже.
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
Все вышеперечисленное я понимаю.Но ситуация становится странной, когда вы смотрите на необъявленную переменную.Обратите внимание: я специально опускаю «var b;».
alert(typeof(b)) // undefined
if(typeof(b) == 'undefined') // True
if(typeof(b) === 'undefined') // True - This tells me the function typeof is returning a string value
if(typeof(b) == 'null') // False
if(typeof(b) === 'null') // False
if(typeof(b) == null) // False
if(typeof(b) === null) // False
if(b) // Runtime Error - B is undefined
Любая другая операция, кроме typeof(b), приводит к ошибке времени выполнения.Тем не менее, я могу понять логику того, как язык оценивает выражения.
Итак, теперь я смотрю на несуществующее свойство a и действительно запутался.
if(a.c) // Runtime Error - c is null or not an object
alert(typeof(a.c)) // Runtime Error - undefined is null or not an object
Я бы подумал, что c в этом случае будет рассматриваться как b в предыдущем примере, но это не так.Вам нужно фактически инициализировать a чем-то, тогда вы сможете заставить его вести себя так же, как b.И не позволяйте ему выдавать ошибки во время выполнения.
Почему это так?Существует ли какая-то специальная обработка неопределенного типа или функция typeof делает что-то рекурсивно для оценки подсвойства, которое выдает ошибку времени выполнения?
Я предполагаю, что практический вопрос здесь заключается в том, что если я проверяю вложенный объект c в a.c, я могу сразу предположить, что c не определен, если a не определен?
И как лучше всего проверить какой-то чрезвычайно вложенный объект, чтобы узнать, установлен ли он как x в MyObject.Something.Something.Something.x ?Мне нужно перемещаться по структуре элемент за элементом, проверяя, существует ли каждый из них, прежде чем перейти к следующему в цепочке?
Решение
Я могу сразу предположить, что C не определен, если A не определен?
Да.
Я должен перемещаться по элементу структуры по элементу, чтобы каждый из них был, прежде чем перейти к следующему в чанине?
Да.
Другие советы
Причина по которой
alert(typeof(a.c))
приводит к ошибке выполнения и
alert(typeof(b))
не заключается в том, что в первом примере вы пытаетесь получить доступ к свойству неопределенного объекта, что вызывает ошибку во время выполнения. до результат можно передать в typeof()
Не забывай это undefined
— это глобальная переменная (!), и вы (или кто-то другой) можете присвоить ей значение, поэтому ваш пример здесь может быть неправильным:
if(a == undefined) // True
if(a === undefined) // True
Если вам действительно нужен undefined, вы можете получить свою собственную «копию»
var local_undefined;
Обычно вы не столкнетесь с необходимостью проверки очень сильно (более трех уровней) вложенный объект, где любой из родителей могут быть undefined
.Поэтому, когда вам нужно протестировать, я бы написал это примерно так:
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.
}
Если a.c
может в любой момент содержать 0
или false
но вам все равно нужно пройти тест, тогда используйте его полностью 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.
}
JavaScript странен тем, что значение undefined
(также typeof a === "undefined"
) — это то, что имеют переменные, пока им не присвоено значение. null
это отдельное значение, отличное от undefined
.Поскольку система типов в JavaScript ненадежна, при сравнении и проверке значений переменных происходит неявное приведение типов.
Если переменная необъявлена, вы не можете ссылаться на нее без ошибок, но вы можете проверить ее с помощью typeof
оператор (и его результатом будет строка "undefined"
).На переменную, которая была объявлена, но не назначена, можно ссылаться, но она все еще содержит значение. undefined
.Вы всегда можете ссылаться на неопределенные свойства объектов, и если они не были назначены, они будут иметь значение undefined
.
См. также этот ответ, где я более подробно рассказал о приведении типов JavaScript и различных значениях, которые часто полезно считать пустыми:
Есть ли у IsEmpty VBScript эквивалент в JavaScript?
При тестировании вложенных объектов, если родительский
undefined
(илиnull
), то у него нет дочерних элементов, поэтому дальнейшее тестирование не требуется.Чтобы безопасно протестировать сильно вложенный объект, вам нужно будет протестировать самого верхнего родителя, используя
typeof
но вы можете проверить фактические значения любых дочерних элементов (см. тест на пустой ответ).Это связано с тем, что верхний уровень может быть не объявлен, но вы всегда можете ссылаться на неопределенные свойства объектов.
Для глубоко вложенный дети
try{ if(a.b.c.d.e) {
// do your stuff
}}catch(e){}
Попробуйте поймать маршрут — более элегантное решение с гораздо меньшим количеством типового кодирования.
И вот пример:
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 )
просто взгляните на скучный метод typeof:
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
}
}
}
}
}
Это могло бы быть еще более элегантным и идеальным решением после включения блока try-catch в функцию, однако не существует известного способа заменить имя переменной, на которую есть ссылка, которое можно было бы передать в функцию как «строку», содержимым переменной.напримерследующее невозможно:
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
в JavaScript не существует метода valueOfVar(), это всего лишь пример
но угадайте что, я получил просветление, зло решение :)
// 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') )
Важное обновление функции Exist() — две дополнительные функции, использующие Exist().
Они охватывают все, что когда-либо необходимо для проверки того, переменная/свойство/объект в любой уровень вложенности является определенный/пустой/необъявленный без каких-либо ошибок во время выполнения в 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)
);
Однако эти методы работают только тогда, когда exists() empty() value()
функции имеют доступ к этим переменным, т.е.и функция, и переменные определены в одной области.
Это необходимо также для проверки переменных локальной функции, в противном случае переменные локальной функции, объявленные с помощью var
будут undefined
в названном exists() empty() value()
функция
Чтобы проверить локальную переменную функции без включения exists() empty() value()
, try/catch
блок должен использоваться внутри этой функции
Вот альтернатива зло Решение для проверки локальных переменных функции. Эти фрагменты кода могут быть определены в глобальном объеме, а затем вызваны с помощью 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
Используйте это с умом
if ( eval(is_ +'any.var.or.property.from.local.or.global.scope'+ _exists) ) {
// do your stuff
}