Is there a standard function to check for null, undefined, or blank variables in JavaScript?

前端 未结 30 3611
眼角桃花
眼角桃花 2020-11-21 23:37

Is there a universal JavaScript function that checks that a variable has a value and ensures that it\'s not undefined or null? I\'ve got this code,

相关标签:
30条回答
  • 2020-11-21 23:58

    Vacuousness

    I don't recommend trying to define or use a function which computes whether any value in the whole world is empty. What does it really mean to be "empty"? If I have let human = { name: 'bob', stomach: 'empty' }, should isEmpty(human) return true? If I have let reg = new RegExp('');, should isEmpty(reg) return true? What about isEmpty([ null, null, null, null ]) - this list only contains emptiness, so is the list itself empty? I want to put forward here some notes on "vacuousness" (an intentionally obscure word, to avoid pre-existing associations) in javascript - and I want to argue that "vacuousness" in javascript values should never be dealt with generically.


    Truthiness/Falsiness

    For deciding how to determine the "vacuousness" of values, we need to accomodate javascript's inbuilt, inherent sense of whether values are "truthy" or "falsy". Naturally, null and undefined are both "falsy". Less naturally, the number 0 (and no other number except NaN) is also "falsy". Least naturally: '' is falsy, but [] and {} (and new Set(), and new Map()) are truthy - although they all seem equally vacuous!


    Null vs Undefined

    There is also some discussion concerning null vs undefined - do we really need both in order to express vacuousness in our programs? I personally avoid ever having the letters u, n, d, e, f, i, n, e, d appear in my code in that order. I always use null to signify "vacuousness". Again, though, we need to accomodate javascript's inherent sense of how null and undefined differ:

    • Trying to access a non-existent property gives undefined
    • Omitting a parameter when calling a function results in that parameter receiving undefined:

    let f = a => a;
    console.log(f('hi'));
    console.log(f());

    • Parameters with default values receive the default only when given undefined, not null:

    let f = (v='hello') => v;
    console.log(f(null));
    console.log(f(undefined));


    Non-generic Vacuousness

    I believe that vacuousness should never be dealt with in a generic fashion. We should instead always have the rigour to get more information about our data before determining if it is vacuous - I mainly do this by checking what type of data I'm dealing with:

    let isType = (value, Cls) => {
      try {
        return Object.getPrototypeOf(value).constructor === Cls;
      } catch(err) {
        return false;
      }
    };

    Note that this function ignores polymorphism - it expects value to be a direct instance of Cls, and not an instance of a subclass of Cls. I avoid instanceof for two main reasons:

    • ([] instanceof Object) === true ("An Array is an Object")
    • ('' instanceof String) === false ("A String is not a String")

    Note that Object.getPrototypeOf is used to avoid a case like let v = { constructor: String }; The isType function still returns correctly for isType(v, String) (false), and isType(v, Object) (true).

    Overall, I recommend using this isType function along with these tips:

    • Minimize the amount of code processing values of unknown type. E.g., for let v = JSON.parse(someRawValue);, our v variable is now of unknown type. As early as possible, we should limit our possibilities. The best way to do this can be by requiring a particular type: e.g. if (!isType(v, Array)) throw new Error('Expected Array'); - this is a really quick and expressive way to remove the generic nature of v, and ensure it's always an Array. Sometimes, though, we need to allow v to be of multiple types. In those cases, we should create blocks of code where v is no longer generic, as early as possible:

    if (isType(v, String)) {
      /* v isn't generic in this block - It's a String! */
    } else if (isType(v, Number)) {
      /* v isn't generic in this block - It's a Number! */
    } else if (isType(v, Array)) {
      /* v isn't generic in this block - it's an Array! */
    } else {
      throw new Error('Expected String, Number, or Array');
    }

    • Always use "whitelists" for validation. If you require a value to be, e.g., a String, Number, or Array, check for those 3 "white" possibilities, and throw an Error if none of the 3 are satisfied. We should be able to see that checking for "black" possibilities isn't very useful: Say we write if (v === null) throw new Error('Null value rejected'); - this is great for ensuring that null values don't make it through, but if a value does make it through, we still know hardly anything about it. A value v which passes this null-check is still VERY generic - it's anything but null! Blacklists hardly dispell generic-ness.
    • Unless a value is null, never consider "a vacuous value". Instead, consider "an X which is vacuous". Essentially, never consider doing anything like if (isEmpty(val)) { /* ... */ } - no matter how that isEmpty function is implemented (I don't want to know...), it isn't meaningful! And it's way too generic! Vacuousness should only be calculated with knowledge of val's type. Vacuousness-checks should look like this:

      • "A string, with no chars": if (isType(val, String) && val.length === 0) ...
      • "An Object, with 0 props": if (isType(val, Object) && Object.entries(val).length === 0) ...
      • "A number, equal or less than zero": if (isType(val, Number) && val <= 0) ...
      • "An Array, with no items": if (isType(val, Array) && val.length === 0) ...

      • The only exception is when null is used to signify certain functionality. In this case it's meaningful to say: "A vacuous value": if (val === null) ...

    0 讨论(0)
  • 2020-11-21 23:59

    The probably shortest answer is

    val==null || val==''
    

    if you change rigth side to val==='' then empty array will give false. Proof

    function isEmpty(val){
        return val==null || val==''
    }
    
    // ------------
    // TEST
    // ------------
    
    var log = (name,val) => console.log(`${name} -> ${isEmpty(val)}`);
    
    log('null', null);
    log('undefined', undefined);
    log('NaN', NaN);
    log('""', "");
    log('{}', {});
    log('[]', []);
    log('[1]', [1]);
    log('[0]', [0]);
    log('[[]]', [[]]);
    log('true', true);
    log('false', false);
    log('"true"', "true");
    log('"false"', "false");
    log('Infinity', Infinity);
    log('-Infinity', -Infinity);
    log('1', 1);
    log('0', 0);
    log('-1', -1);
    log('"1"', "1");
    log('"0"', "0");
    log('"-1"', "-1");
    
    // "void 0" case
    console.log('---\n"true" is:', true);
    console.log('"void 0" is:', void 0);
    log(void 0,void 0); // "void 0" is "undefined" - so we should get here TRUE

    More details about == (source here)

    BONUS: Reason why === is more clear than ==

    To write clear and easy understandable code, use explicite list of accepted values

    val===undefined || val===null || val===''|| (Array.isArray(val) && val.length===0)
    

    function isEmpty(val){
        return val===undefined || val===null || val==='' || (Array.isArray(val) && val.length===0)
    }
    
    // ------------
    // TEST
    // ------------
    
    var log = (name,val) => console.log(`${name} -> ${isEmpty(val)}`);
    
    log('null', null);
    log('undefined', undefined);
    log('NaN', NaN);
    log('""', "");
    log('{}', {});
    log('[]', []);
    log('[1]', [1]);
    log('[0]', [0]);
    log('[[]]', [[]]);
    log('true', true);
    log('false', false);
    log('"true"', "true");
    log('"false"', "false");
    log('Infinity', Infinity);
    log('-Infinity', -Infinity);
    log('1', 1);
    log('0', 0);
    log('-1', -1);
    log('"1"', "1");
    log('"0"', "0");
    log('"-1"', "-1");
    
    // "void 0" case
    console.log('---\n"true" is:', true);
    console.log('"void 0" is:', void 0);
    log(void 0,void 0); // "void 0" is "undefined" - so we should get here TRUE

    0 讨论(0)
  • 2020-11-22 00:00

    You are a bit overdoing it. To check if a variable is not given a value, you would only need to check against undefined and null.

    function isEmpty(value){
        return (typeof value === "undefined" || value === null);
    }
    

    This is assuming 0, "", and objects(even empty object and array) are valid "values".

    0 讨论(0)
  • 2020-11-22 00:01

    This is the safest check and I haven't seen it posted here exactly like that:

    if (typeof value !== 'undefined' && value) {
        //deal with value'
    };
    

    It will cover cases where value was never defined, and also any of these:

    • null
    • undefined (value of undefined is not the same as a parameter that was never defined)
    • 0
    • "" (empty string)
    • false
    • NaN

    Edited: Changed to strict equality (!==) because it's the norm by now ;)

    0 讨论(0)
  • 2020-11-22 00:02
    var myNewValue = myObject && myObject.child && myObject.child.myValue;
    

    This will never throw an error. If myObject, child, or myValue is null then myNewValue will be null. No errors will be thrown

    0 讨论(0)
  • 2020-11-22 00:03

    Code on GitHub

    const isEmpty = value => (
      (!value && value !== 0 && value !== false)
      || (Array.isArray(value) && value.length === 0)
      || (isObject(value) && Object.keys(value).length === 0)
      || (typeof value.size === 'number' && value.size === 0)
    
      // `WeekMap.length` is supposed to exist!?
      || (typeof value.length === 'number'
          && typeof value !== 'function' && value.length === 0)
    );
    
    // Source: https://levelup.gitconnected.com/javascript-check-if-a-variable-is-an-object-and-nothing-else-not-an-array-a-set-etc-a3987ea08fd7
    const isObject = value =>
      Object.prototype.toString.call(value) === '[object Object]';
    

    Poor man's tests

    0 讨论(0)
提交回复
热议问题