Undefined, typeof Undefined, hasOwnProperty

Возьмите этот фрагмент,

var a = {

}

if(typeof a.c === 'undefined'){
 console.log('inside if');
}
if(a.c === undefined){
 console.log('inside if');
}

Оба if приводят к true. Есть ли разница в обоих утверждениях, характерных для некоторых браузеров?

Кроме того, в моем последнем проекте я уже много раз использовал typeof a.c == 'undefined' для проверки значений в json данных.

Теперь я знаю, что это нехорошо, так как некоторое значение может быть undefined тоже, поэтому моя логика потерпит неудачу.

Я должен был использовать hasOwnProperty.

Но я уверен, что никакое значение не будет undefined, я могу использовать typeof a.c == 'undefined' вместо hasOwnProperty или мне нужно изменить все мои typeof на hasOwnProperty

Ответ 1

(ОБНОВЛЕНИЕ. Возможно, вы захотите проверить этот вопрос: variable === undefined void typeof variable === "undefined" .)

В очень старых браузерах (Netscape 2, IIRC и, возможно, IE 4 или ниже) вы не можете сравнивать значение с undefined, поскольку это вызвало ошибку. Однако в любом (полу) современном браузере нет причины проверять typeof value === 'undefined' вместо value === undefined (за исключением паранойи, что кто-то мог переопределить переменную undefined).

hasOwnProperty служит другой цели. Он проверяет, имеет ли объект свойство с заданным именем, а не его прототип; т.е. независимо от унаследованных свойств. Если вы хотите проверить, содержит ли объект определенное свойство, унаследованное или нет, вы должны использовать if ('c' in a) {...

Но в основном, они, вероятно, будут работать:

if (a.c === undefined) console.log('No c in a!');
if (typeof a.c === 'undefined') console.log('No c in a!');
if (!('c' in a)) console.log('No c in a!');
if (!a.hasOwnProperty('c')) console.log('No c in a!');

Основные отличия в том, что:

  • a.c === undefined приведет к неожиданному результату, если кто-то сделал undefined = 'defined' или какой-то такой трюк;
  • !('c' in a) не читается (IMHO)
  • !a.hasOwnProperty('c') вернет false, если объект a не содержит свойство c, но его прототип делает.

Лично я предпочитаю первый, так как он более читабельен. Если вы параноидально и хотите избежать риска переопределения undefined, оберните свой код в самоисполняющуюся анонимную функцию следующим образом:

(function (undefined) {
  // in here, 'undefined' is guaranteed to be undefined. :-)

  var a = {

  };

})();

Ответ 2

Если ваши стандартные объекты проверки, являющиеся результатом разбора строки JSON, .hasOwnProperty не имеют очевидной выгоды. Кроме того, конечно, если вы или какой-нибудь lib, который используете, были беспомощны с помощью Object.prototype.

В общем, undefined как таковое может быть переопределено, но я не сталкивался с этим сам - и я не думаю, что когда-нибудь это сделаю. Однако невозможно (AFAIK) испортить возвращаемые значения typeof. В этом отношении последний является самым безопасным способом. Я считаю, что некоторые древние браузеры плохо работают с ключевым словом undefined.

При возобновлении: нет необходимости идти и заменять каждую проверку typeof. В личном примечании: я считаю, что хорошей практикой является привычка использовать .hasOwnProperty. Поэтому я бы предположил, что в случае, если свойство может существовать, но undefined, .hasOwnPorperty является самой безопасной ставкой.


В ответ на ваш комментарий: да, typeof будет соответствовать вашим потребностям ~ 95% времени. .hasOwnProperty будет работать 99% раз. Однако, поскольку имя указывает: свойства выше, цепочка наследования не будет проверена, рассмотрите следующий пример:

Child.prototype = new Parent();
Child.prototype.constructor=Child;//otherwise instance.constructor points to parent

function Parent()
{
    this.foo = 'bar';
}

function Child()
{
    this.bar = 'baz';
}

var kiddo = new Child();
if (kiddo.hasOwnProperty('foo'))
{
    console.log('This code won\'t be executed');
}
if (typeof kiddo.foo !== 'undefined')
{
    console.log('This will, foo is a property of Parent');
}

Итак, если вы хотите проверить, имеет ли один объект свойство, hasOwnProperty - это то, что вам нужно. Особенно, если вы собираетесь изменить значение этого свойства (если это свойство прототипа, все экземпляры могут быть изменены).
Если вы хотите узнать, имеет ли свойство значение (другое, а затем undefined), независимо от того, где оно расположено в цепочке наследования, вам понадобится typeof. У меня есть рекурсивная функция где-то, чтобы определить, где свойство можно найти в цепочке наследования. Как только я его найду, я тоже отправлю его здесь.

Update:

Как и было обещано, функция поиска свойства в цепочке наследования. Это не фактическая функция, которую я использовал некоторое время назад, поэтому я собрал рабочий проект. Это не идеально, но это может помочь вам на вашем пути:

function locateProperty(obj,prop,recursion)
{
    recursion = recursion || false;
    var current = obj.constructor.toString().match(/function\s+(.+?)\s*\(/m)[1];
    if (!(obj.hasOwnProperty(prop)))
    {
        if (current === 'Function' || recursion === current)
        {
            return false;
        }
        return locateProperty(new window[current](),prop,current);
    }
    return current;
}
//using the object kiddo
locateProperty(kiddo,'foo');//returns 'Parent'
locateProperty(kiddo,'bar');//returns 'Parent', too

Чтобы избежать этого последнего сбоя, вы можете либо заменить последний оператор return current; на return obj;. Или, что еще лучше, добавьте следующую строку к приведенному выше фрагменту:

Child.prototype.constructor=Child;

Я забыл, что в первом редактировании...

Ответ 3

  • Скопируйте перечислимые свойства p в o и верните o.

  • Если o и p имеют свойство с тем же именем, o свойство остается в покое.

  • Эта функция не обрабатывает геттеры, сеттеры или атрибуты копирования.

    function merge(o, p) 
    {
        for(prop in p) 
        {   
            // For all props in p.
            if (o.hasOwnProperty[prop]) continue;  // Except those already in o.
            o[prop] = p[prop];                     // Add the property to o.
        }
    
        return o;
    }
    

В чем разница между o.hasOwnProperty[prop] и o.hasOwnProperty(prop)?