Еще один неопределенный нулевой вопрос по Javascript

StackOverflow https://stackoverflow.com/questions/1828130

  •  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 делает что-то рекурсивно для оценки подсвойства, которое выдает ошибку времени выполнения?

  1. Я предполагаю, что практический вопрос здесь заключается в том, что если я проверяю вложенный объект c в a.c, я могу сразу предположить, что c не определен, если a не определен?

  2. И как лучше всего проверить какой-то чрезвычайно вложенный объект, чтобы узнать, установлен ли он как 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?

  1. При тестировании вложенных объектов, если родительский undefined (или null), то у него нет дочерних элементов, поэтому дальнейшее тестирование не требуется.

  2. Чтобы безопасно протестировать сильно вложенный объект, вам нужно будет протестировать самого верхнего родителя, используя 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
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top