[JavaScript] function expression & closure

Function expression

Function declaration VS function expression

function functionName(arg0, arg1){
  //Function body
}

Function declaration has an important feature - function declaration promotion, that is, the function declaration will be read before code execution (which means that the function declaration can be located after the function call).

var functionName = function(arg0, arg1){
  //Function body
};

A function expression assigns an anonymous function (also known as lambda function, whose name attribute is an empty string) to a variable. The function will be declared only when the code runs to the function expression. Anonymous functions can be used when the function is used as a value.

recursion

arguments.callee is a pointer to the function being executed. In non strict mode, you can use it to implement the recursive call to the function to ensure the correctness of the call.

function factorial(num){
    if(num <= 1) {
        return 1;
    }else {
        return num * arguments.callee(num-1);
    }
}

In strict mode, you can use function expressions to achieve the same effect.

var factorial = (function f(num){
    if(num <= 1) {
        return 1;
    }else {
        return num * f(num-1);
    }
});

closure

A closure is a function that has access to a variable in the scope of another function. A common way to create closures is to create another function inside one function. In the background execution environment, the scope chain of a closure contains its own scope, the scope containing functions, and the global scope. Usually, the scope of a function and all its variables are destroyed after the execution of the function. However, when a function returns a closure, the scope of the function will exist until the closure is destroyed.

function createComparisonFunction(propertyName){
    return function(obj1, obj2){
        //Variables that access external functions
        var val1 = obj1[propertyName];
        var val2 = obj2[propertyName];

        if(val1 < val2){
            return -1;
        }else if(val1 > val2){
            return 1;
        }else{
            return 0;
        }
    };
}
//Create function
var compareNames = createComparisonFunction("name");
//Call function
var result = compareNames({name: "Lou"}, {name: "V"});
//Dereference anonymous functions (to free memory)
compareNames = null;

⚠️ Closures save the entire variable object, not a particular variable

function createArray(){
    var result = new Array();
    for(var i = 0; i < 5; i++){
        result[i] = function(){
            return i;
        };
    }
    return result;
}

var ans = createArray();
console.log(ans);   //[f, f, f, f, f]
function createArray(){
    var result = new Array();
    for(var i = 0; i < 5; i++){
        result[i] = function(){
            return i;
        }();
    }
    return result;
}

var ans = createArray();
console.log(ans);   //[0,1,2,3,4]

⚠️ The execution environment of anonymous functions is global, and its this object usually points to window

var name = "window";
var obj = {
    name: "obj",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
console.log(obj.getNameFunc()());   //undefined
var name = "window";
var obj = {
    name: "obj",
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
console.log(obj.getNameFunc()());   //"obj"
console.log((obj.getNameFunc = obj.getNameFunc)()());   //undefined

Block level scope

JavaScript has no concept of block level scope, which means that variables defined in block statements are actually created in containing functions rather than statements.

var func = function(){
    //Here is the block level scope
};
(function(){
    //Here is the block level scope
})();

The second approach above can reduce the memory problem occupied by closures, because there is no reference to anonymous functions. Once the function is executed, its scope chain can be destroyed immediately.

private variable

class Person {
    constructor(name) {
        this.getName = function () {
            return name;
        };
        this.setName = function (val) {
            name = val;
        };
    }
}

var person = new Person("Nick");
console.log(person.getName());
person.setName("Greg");
console.log(person.getName());

The private variable name can only be obtained through getName() and modified through setName(). However, the disadvantage of constructor mode is that the same set of new methods will be created for each instance, which can be avoided by using static private variables.

(function() {
    var name = "";
    Person = function(val){
        name = val;
    };
    Person.prototype.getName = function(){
        return name;
    };
    Person.prototype.setName = function(val){
        name = val;
    };
})();

var person = new Person("Nick");
console.log(person.getName());  //"Nick"
person.setName("Greg");
console.log(person.getName());  //"Greg"

var bb = new Person("Lou");
console.log(bb.getName());  //"Lou"
console.log(person.getName());  //"Lou"

Private variables and functions are shared by instances.

Module mode

Douglas's modular pattern is to create private variables and privileged methods for singletons. A singleton is an object with only one instance. Typically, JS creates singleton objects in the form of object literals.

var app = function(){
    //Private variables and functions
    var components = new Array();
    //initialization
    components.push(new BaseComponent());
    //public
    return {
        getComponentCount: function(){
            return components.length;
        }
    };
}();

Enhanced module mode

var application = function(){
    //Private variables and functions
    var components = new Array();
    //initialization
    components.push(new BaseComponent());
    //Partial copy
    var app = new BaseComponent();
    //public
    app.getComponentCount: function(){
        return components.length;
    };
    return app;
}();

Function expressions and closures in JS are extremely useful features, and many functions can be realized by using them. However, because additional scopes must be maintained to create closures, overusing them can consume a lot of memory.

Keywords: Javascript

Added by alanrenouf on Tue, 28 Dec 2021 03:02:05 +0200