Default value of a type in javascript

前端 未结 3 2008
长情又很酷
长情又很酷 2021-02-15 00:48

Is there a function in JavaScript that returns the default value for a type name that is passed to it? For example:

var defaultValue = built_in_getDefaultValue(\         


        
3条回答
  •  长发绾君心
    2021-02-15 01:31

    Both previous answers correctly note that the default (unassigned) value of a variable in JavaScript is undefined, but neither addresses what you seem to be looking for—an equivalent of e.g. the default operator in C#.

    Fortunately, JavaScript has a very simple type system: everything that isn't one of the seven primitive types is an object and can be called via its constructor.¹ The following function, given a string representing

    • the name of a JS primitive type, e.g.'string' or 'null';
    • any valid production of the typeof operator, e.g. 'function';
    • a function name either in the this value it is called with or its local scope²

    will produce a reasonable interpretation of a "default" value:

    function defaultVal(type) {
        if (typeof type !== 'string') throw new TypeError('Type must be a string.');
        
        // Handle simple types (primitives and plain function/object)
        switch (type) {
            case 'bigint'    : return BigInt(0);
            case 'boolean'   : return false;
            case 'function'  : return function () {};
            case 'null'      : return null;
            case 'number'    : return 0;
            case 'object'    : return {};
            case 'string'    : return "";
            case 'symbol'    : return Symbol();
            case 'undefined' : return void 0;
        }
        
        try {
            // Look for constructor in this or current scope
            var ctor = typeof this[type] === 'function'
                       ? this[type]
                       : eval(type);
            
            return new ctor;
        
        // Constructor not found, return new object
        } catch (e) { return {}; }
    }
    

    You can call it like this:

    defaultVal( typeof 1 ); // -> 0
    defaultVal( 'bigint' ); // -> 0n
    defaultVal( 'object' ); // -> {}
    defaultVal( 'RegExp' ); // -> /(?:)/
    

    Unfortunately, the JS typeof operator may be a bit… anameic for our purposes. For example, typeof null === 'object'¹, and typeof [] === 'object', which probably isn't what you want.

    To work around this, here's a function that returns the result of calling typeof on a value unless the result is 'object', in which case it will return 'null' if the value is null and obj.constructor.name otherwise (with 'object' as the fallback value if all else fails):

    function getType(obj) {
        var type = typeof obj;
        
        if (type !== 'object') return type; // primitive or function
        if (obj === null) return 'null';    // null
        
        // Everything else, check for a constructor
        var ctor = obj.constructor;
        var name = typeof ctor === 'function' && ctor.name;
    
        return typeof name === 'string' && name.length > 0 ? name : 'object';
    }
    

    And now we can have:

    defaultVal( getType( 1234 ) );  // -> 0
    defaultVal( getType( [99] ) );  // -> []
    defaultVal( getType( 'ab' ) );  // -> ""
    defaultVal( getType( null ) );  // -> null   (Not possible with typeof!)
    defaultVal( getType( /.*/ ) );  // -> /(?:)/ (Not possible with typeof!)
    
    function T () {}
    defaultVal( getType( new T ) ); // -> T {}   (Not possible with typeof!)
    

    I suspect this is as close to what you're looking for as you're going to get. Also, to head off any criticism: the use of eval above might seem a bit dirty, but it's the only way to get a named value from the current scope.


    ¹ null is a bit of an oddity. The ECMAScript 2020 specification clearly refers to it as a primitive; however, typeof null is 'object' due to a bug in the original JavaScript implementation. At one point MDN excluded null from its list of primitives, counting only six primitives and stating that null should be considered a "special case for every Object".

    Based on the spec, I refer to null as a primitive in this answer, and MDN has since been updated to agree with this stance.

    ² This function can't call a constructor instantiated in another scope; instead, I've made it possible to call it with this bound to an object containing any such constructors it may reference:

    function innerScopeCtor() {
        function T () {}
        
        return defaultVal.call( { T : T }, 'T' );
    }
    
    innerScopeCtor(); // T {}
    

    If you don't do this, it will just fall back to returning a new object.

提交回复
热议问题