JavaScript Unary Operators: Simple and Useful

Esther Dama Kahindi
πŸ‘οΈ 13,806 views
πŸ’¬ comments

You might have come across things like i++, --i in loops or ! when writing conditions. Ever wondered how these operations work? Well, these are unary operators and we are going to take an in-depth look into how they work.

What is an operator?

Operator-operand Mathematically, an operation is a calculation on one or more values called operands mapping to an output value. An operator is a symbol/sign that maps operands to output values.

What is an unary operator?

A unary operator is one that takes a single operand/argument and performs an operation.

Table of Contents

    A unary operation is an operation with only one operand. This operand comes either before or after the operator. Unary operators are more efficient than standard JavaScript function calls. Additionally, unary operators can not be overridden, therefore their functionality is guaranteed.

    Summary of all unary operators

    Operator Explanation
    Unary plus (+) Tries to convert the operand into a number
    Unary negation (-) Tries to convert the operand into a number and negates after
    Logical Not (!) Converts to boolean value then negates it
    Increment (++) Adds one to its operand
    Decrement (--) Decrements by one from its operand
    Bitwise not (~) Inverts all the bits in the operand and returns a number
    typeof Returns a string which is the type of the operand
    delete Deletes specific index of an array or specific property of an object
    void Discards a return value of an expression.

    Unary plus (+)

    This operator precedes the operand and tries to convert it to a number.

    It can convert all string representations of numbers, boolean values(true and false) and null to numbers. Numbers will include both integers, floats, hexadecimal, scientific (exponent) notation and Infinity.

    If the operand cannot be converted into a number, the unary plus operator will return NaN.

    Examples:

        +3                                   // returns 3
        +'-3'                                // returns -3
        +'3.14'                              // returns 3.14
        +'3'                                 // returns 3
        +'0xFF'                              // returns 255
        +true                                // returns 1
        +'123e-5'                            // returns 0.00123
        +false                               // returns 0
        +null                                // returns 0
        +'Infinity'                          // returns Infinity
        +'infinity'                          // returns NaN
        +function(val){  return val }        // returns NaN

    The illustration above clearly shows how the + operator will behave when applied to different data types.

    • Numbers will not be altered.
    • A string notation of a number e.g '3' is converted to that number (3).
    • Boolean value true is converted to a 1 and false to 0.
    • Other types e.g functions and objects are converted to NaN.

    Note

    An object can only be converted if it has a key valueOf and it's function returns any of the above types.

    +{
      valueOf: function(){
        return '0xFF'
      }
    }
    //returns 255

    Unary negation (-)

    It also precedes the operand and converts non-numbers data types to numbers like unary plus, however, it performs an additional operation, negation.

    Unary plus is considered the fastest and preferred way of making conversions because it doesn't perform any additional operation. Both the unary negation and plus perform the same operation as the Number() function for non-numbers.

    Examples:

        -3                               // returns -3
        -'-3'                            // returns 3
        -'3.14'                          // returns -3.14
        -'3'                             // returns -3
        -'0xFF'                          // returns -255
        -true                            // returns -1
        -'123e-5'                        // returns -0.00123
        -false                           // returns -0
        -null                            // returns -0
        -'Infinity'                      // returns -Infinity
        -'infinity'                      // returns NaN
        -function(val){  return val }    // returns NaN
      -{                                 
        valueOf: function(){
          return '0xFF'
        }
      }                                //returns -255

    Logical Not (!)

    This operator comes before the operand. It converts the operand into it's boolean equivalent before negating it.

    Examples:

    !false        // returns true
    !NaN          // returns true
    !0            // returns true
    !null         // returns true
    !undefined    // returns true
    !""           // returns true
    !true         // returns false
    !-3           // returns false
    !"-3"         // returns false
    !42           // returns false
    !"42"         // returns false
    !"foo"        // returns false
    !"true"       // returns false
    !"false"      // returns false
    !{}           // returns false
    ![]           // returns false
    !function(){} // returns false

    The above illustration demonstrates how logical not returns false if the operand can be converted to true, if not it returns false.

    You can use double negation(!!)

    Let us take a look at a more awesome example:

    !!'hi' === true  // returns true
    !!1 === true    // returns true
    !!0 === false  // returns true

    Why true? So we execute from right to left.

    !'hi'  //returns false

    Then:

    !false //returns true

    Thus:

    true === true //returns true

    Increment (++)

    This operator adds one to its operand and returns the result.

    It can be used as a postfix or prefix operator.

    • Postfix : - meaning the operator comes after the operand(y++). This returns the value before incrementing.
    • Prefix: - the operator comes before the operand (++y). Using it as a prefix, returns the value after incrementing.

    Examples:

    Postfix

    x = 4      // x=4
    y = x++    // y = 4 and  x = 5
    // y is set to the value before incrementing and it adds 1 to x
    
    // Be careful about resetting values when using postfix
    var a = 5     // a = 5
    a = a++       // a = 5
    // a is set to the value before incrementing
    

    Prefix

    x = 4      // x=4
    y = ++x    // y = 5 and  x = 5
    // y is set to the value after incrementing and it adds 1 to x
    
    var a = 5     // a = 5
    a = ++a       // a = 6
    // a is set to the value after incrementing

    Decrement (--)

    The decrement operator subtracts one from its operand.

    It returns a value before decrementing if it is postfix. Prefixing it returns the value after decrementing.

    Examples:

    Postfix

    var a = 5     // a = 5
    a = a--       // a = 5
    // a is set to the value before incrementing
    
    x = 4      // x=4
    y = x--    // y = 4 and  x = 3
    // sets y to the value before decrementing and it removes 1 from x

    Prefix

    var a = 5  // a = 5
    a = --a    // a = 4
    // a is set to the value after incrementing
    
    x = 4      // x=4
    y = --x    // y =3 and  x = 3
    // sets y to the value after incrementing and it adds 1 to x

    Bitwise not (~)

    Performs a binary NOT operation, by inverting all the bits in the operand and return a number.

    A bitwise not on a number results in: -(x + 1).

    a Not a
    0 1
    1 0

    Examples:

    ~2                                  //returns -3
    ~'2'                                //returns -3
    ~'-3'                               // returns 2
    -'-3.14'                            // returns 2
    ~'-3.54'                            // returns 2
    ~'0xFF'                             // returns -256
    ~true                               // returns -2
    ~'123e-5'                           // returns -1
    ~false                              // returns -1
    ~null                               // returns -1
    ~'Infinity'                         // returns -1
    ~'infinity'                         // returns -1
    ~function(val){  return val }       // returns -1
    ~{                                 
        valueOf: function(){
            return '0xFF'
        }
    }                                  //returns -256

    The table below takes a deeper look into how this operation is performed.

    (base 10) (base 2) Not (base 2) Not (base 10)
    2 00000010 11111101 -3
    1 00000001 11111110 -2
    0 00000000 11111111 -1
    -1 11111111 00000000 0
    -2 11111110 00000001 1
    -3 11111101 00000010 2

    typeof

    This operator comes before the operand. It returns a string indicating the data type of the operand.

    Examples:

    typeof 2                                       // returns 'number'
    typeof -3.14                                   // returns 'number'
    typeof 0xFF                                    // returns 'number'
    typeof 123e-5                                  // returns 'number'
    typeof true                                    // returns 'boolean'
    typeof false                                   // returns 'boolean'
    typeof null                                    // returns 'object'
    typeof Infinity                                // returns 'number'
    typeof '2'                                     // returns 'string'
    typeof '-3'                                    // returns 'string'
    typeof 'infinity'                              // returns 'string'
    typeof Date()                                  // returns 'string'
    typeof [1,2,3]                                 // returns 'object'
    typeof {hi: 'world'}                           // returns 'object'
    typeof function(val){  return val }            // returns 'function'
    typeof {                                 
        valueOf: function(){
            return '0xFF'
        }
    }                                              // returns 'object'
    typeof undefined                               // returns 'undefined'
    typeof hi                                      // returns 'undefined'
    typeof NaN                                     // returns 'number'
    typeof new Date()                              // returns 'object'
    typeof /ab+c/                                  // returns 'object'
    typeof new RegExp('ab+c')                      // returns 'object'
    typeof document                                // returns 'undefined'      

    delete:

    It also comes before the operand. It deletes values of a specific index of an array and a specific property of an object.

    It returns true if it successfully deleted the property or if the property does not exist. It returns false if it fails to delete an item.

    Delete does not have any effect on both functions and variables. Let's look at the following examples.

    // Deleting a variable
    var hi = 1;
    delete hi;          // returns false
    console.log(hi);    // returns 1
    
    // Deleting a function
    function yo(){ };
    delete yo;           // returns false
    console.log(yo);     // returns function foo(){ }
    
    // Deleting an object
    var pub = {bar: '1'}
    delete pub           // returns false
    console.log(pub);    // returns {bar: '1'}
    
    //Deleting an array
    var code = [1,1,2,3,5]
    delete code          // returns false
    console.log(code);   //  [1,1,2,3,5]

    Objects

    As earlier stated, it deletes the property or the whole object. Examples:

    // Deleting a property with the literal notation
    var fruits = {1: 'apple', 2: 'mango'}
    delete fruits[1]             // returns true
    console.log(fruits);         // returns { '2': 'mango' }
    console.log(fruits[1]);      // returns undefined
    
    // Deleting a property with the dot notation
    var pub = { bar: "42" };
    delete pub.bar;              // returns true
    console.log(pub);            // returns {}
    
    // Deleting a property that does not exist
    var lunch = { fries: 1 };
    delete lunch.beans;          // returns true
    console.log(lunch);          // returns { fries: 1 }
    
    // Deleting a non-configurable property of a predefined object
    delete Math.PI;              // returns false  
    console.log(Math.PI);        // returns 3.141592653589793
    

    Non-Configurable properties

    Delete has no effect on an object property that is as non-configurable. It will always return false. In strict mode, this will raise a SyntaxError.

    Examples:

    // When Non Configurable
    var Person = {};
    Object.defineProperty(Person, 'name', {  value: 'Scot', configurable: false })
    // Defines an object property and sets it to non-configurable
    console.log(Person.value);                                    // returns 'Scot'
    delete Person.value                                           // returns false
    console.log(Person.value);                                    // returns 'Scot'
    
    // When configurable
    var b = {};
    Object.defineProperty(Person, 'name', {  value: 'Scot', configurable: true })
    console.log(b.value);                                    // returns 'Scot'
    delete b.value                                           // returns true
    console.log(b.value);                                    // returns undefined
    

    Read more about defineProperty()

    var, let and const create non-configurable properties that cannot be deleted with the delete operator:

    Example:

    var num = 1;
    // We can access this global property using:
    Object.getOwnPropertyDescriptor(window, 'num')  
    // returns { value: 'XYZ',
    //           writable: true,
    //           enumerable: true,
    //           configurable: false }  
    delete num;                                     // returns false
    // Node
    Object.getOwnPropertyDescriptor(global, 'num')
    // returns { value: 'XYZ',
    //           writable: true,
    //           enumerable: true,
    //           configurable: false }
    // regular objects
    var lunch = { fries: 1 };                                                                                   
    Object.getOwnPropertyDescriptor(lunch, 'fries')
    // returns { value: 1,
    //           writable: true,
    //           enumerable: true,
    //           configurable: true }

    Notice that the var keyword is marked as non-configurable

    Arrays

    Arrays are considered type object in javascript. Thus this method will work on them.

    Examples:

    // Deleting values of an array index
    var lol=[20,30,40];
    console.log(lol.length);     // returns 3
    delete lol[2]                // returns true
    console.log(lol);            // returns [ 20, 30,  ]
    console.log(lol[2]);         // returns undefined
    console.log(lol.length);     // returns 3

    Note

    The delete operator will only delete the value and not the index of the array. It will leave the value of that particular index as undefined. This is why the length does not change.

    strict mode

    In strict mode, delete throws a SyntaxError due to the use of direct reference to a variable, a function argument or a function name.

    β€˜use strict’
    var fruits = {1:'mango', 2:'apple'};
    delete fruits;
    // Output: Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
    
    function Person() {
     delete name;             // SyntaxError
     var name;        
    }
    
    function yo() {
    }
    
    delete yo; // SyntaxError

    Here are a few pointers to always consider when using delete:

    • Trying to delete a property that does not exist, delete will return true but will not have an effect on the object.

    • Delete only affects object own properties. This means if a property with the same name exists on the object's prototype chain, then delete will not affect it. After deletion, the object will use the property from the prototype chain.

    • Variable declared var, let and const cannot be deleted from the global scope or from a function's scope.

      • Meaning:- Delete cannot delete any functions in the global scope or in the function scope.
      • Delete works on functions which are part of an object (apart from the global scope).
    • Non-configurable properties cannot be removed.

    • Delete does not work on any of the built-in objects like Math, Array, Object or properties that are created as non-configurable with methods like Object.defineProperty().

    Void operator:

    It precedes an operation. It discards the return value of an expression, meaning it evaluates an expression but returns undefined.

    Void operator's main purpose is to return undefined. The void operator specifies an expression to be evaluated without returning a value.

    The void operator is used in either of the following ways: void (expression) void expression

    Note:

    The void operator is not a function, so () are not required, but it is good style to use them according to MDN

    Example:

    void 0                                              // returns undefined
    var hi = function () {
        console.log('Yap')
        return 4;
    }
    var result = hi()                                   // logs 'Yap' and returns 4
    console.log(result);                                // returns 4
    
    var voidResult = void (hi())                        // logs 'Yap' and returns undefined
    console.log(result);                                // returns undefined

    The void operator can be used to specify an expression as a hypertext link. The expression is evaluated but is not loaded in place of the current document.

    Some more examples

    <a href="javascript:void(0)">Click here to do nothing</a>

    The code above creates a link that does nothing when a user clicks it. This is because void(0) evaluates to undefined.

    <a href="javascript:void(document.form.submit())">
    Click here to submit</a>

    The code creates a link that submits a form when the user clicks it.

    Conclusion

    Always consider the order of operations when dealing with more than one operator. This is good practice in mitigating unforeseen bugs.

    Here is a brief table that shows the order of precedence in operations when using Javascript. Operands on the same level have the same order of precedence.

    Operator type Operators Example
    member . [] [1,2,3]
    call / create instance () new var vehicle = new Vehicle();
    negation/increment ! ~ - + ++ -- typeof void delete typeof [1,2,2]
    multiply/divide * / % 3 % 3
    addition/subtraction + - 3 + 3
    bitwise shift << >> >>> 9 << 2
    relational < <= > >= in instanceof [1] instanceof Array
    equality == != === !== void(0) === undefined
    bitwise-and & 5 & 1
    bitwise-xor ^ 5 ^ 1
    bitwise-or | 5 | 1
    logical-and && x < 10 && y > 1
    logical-or || x == 5 || y == 5
    conditional ?: (age < 18) ? "Too young":"Old enough"
    assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= x = 8
    comma , b = 3, c = 4

    Esther Dama Kahindi

    1 post

    I am passionate about making an impact in people's life. I'm a trained Computer Scientist who loves life, on a journey to discover what computing has to offer.Currently a Software Developer at Andela.