函数表达式(递归+闭包+this对象+私有作用域)

定义函数有两种方式:

1.函数声明(会发生函数声明提升,即在执行代码前会先读取函数声明)

function functionDeclaration(){
    console.log("这是函数声明");
}

2.函数表达式(在这种方式下创建的函数叫做匿名函数,因为function关键字后面没有标识符)

 var fcn = function(){
    console.log("这是一个匿名函数");
};

【递归】(函数通过名字调用自身)

 function factorial(num){
     if(num<=1){
         return 1;
     }
     else{
         return num * factorial(num-1);
     }
 }
 console.log(factorial(4)); //24
 var anotherFactorial = factorial;
 factorial = null;
 console.log(anotherFactorial(4)); //"TypeError: factorial is not a function"
通常使用arguments.callee 这个指向正在执行函数的指针来代替函数名,以防函数名更改。
function factorial(num){
    if(num<=1){
        return 1;
    }
    else{
        return num * arguments.callee(num-1);
    }
}
console.log(factorial(4)); //24
var anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(4)); //24
但在严格模式下,访问arguments.callee这个属性会报错,解决方案:使用命名函数表达式
//创建了f()命名函数表达式
var factorial = (function f(num){
    if(num<=1){
        return 1;
    }
    else{
        return num * f(num-1);
    }
});

console.log(factorial(4)); //24

【闭包】(有权访问另一个函数作用域中的变量的函数)

创建闭包的常见方法:在函数内部创建另一个函数。即在函数内部定义了其他函数时,就创建了闭包
[个人对闭包的理解] 理解闭包本质上就是在理解作用域链,闭包之所以能够访问其外部的变量的函数,是因为闭包的作用域链中包含这些变量和函数。在后台执行环境中,闭包的作用域链包括它自己的作用域、其包含函数的作用域、全局作用域

【TIPS】

1. 包含闭包的函数在执行完毕时,虽然其执行环境的作用域链会被销毁,但其活动对象不会被销毁,因为作为闭包的匿名函数仍然引用着这些活动对象。直到匿名函数被销毁后,其活动对象才会被销毁。
2. 闭包会携带包含它的函数的作用域,会占用更多的内存,所以要慎重使用闭包。
3. 闭包只能取得包含函数中任何变量的最后一个值。因为闭包保存的是整个变量对象,而不是某个特殊的变量。
function createFunction(){
    var result = new Array();
    for(var i=0;i<10;i++){
         result[i] = function(){
             return i;
         };
    }
    return result;
}
var array = createFunction();
for(var j=0;j<10;j++){
    console.log(array[j]());//输出10个10
}
function createFunction(){
    var result = new Array();
    for(var i=0;i<10;i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i); 
    }
    return result;
}
var array = createFunction();
for(var j=0;j<10;j++){
    console.log(array[j]()); //输出0到9 
}

等同于

function createFunction(){
    var result = new Array();
    for(var i=0;i<10;i++){
        result[i] = i; 
    }
    return result;
}
var array = createFunction();
for(var j=0;j<10;j++){
    console.log(array[j]); //输出0到9
}

【this】

  • this对象是在运行时基于函数的执行环境绑定的。
  • 在匿名函数中,由于匿名函数的执行环境具有全局性,因此this对象通常指向window。
    • 但在使用call()或apply()改变函数执行环境的情况,this会指向其他对象。
    • 或者把this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象。(对于arguments也是一样,如果想访问作用域中的arguments对象,可以把对arguments的引用保存在闭包能够访问到的变量中)
    var name = "the window";
    var object ={
        name: "My Object",
        getNameFun: function(){
        //匿名函数的this对象指向window
            return function(){
                return this.name;
            };
        }
    };
    console.log(object.getNameFun()()); //the window
    var name = "the window";
    var object ={
        name: "My Object",
        getNameFun: function(){
            var that = this;
            //把this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象。
            return function(){
                return that.name;
            };
        }
    };
    console.log(object.getNameFun()()); //My Object
    var name = "the window";
    var object ={
        name: "My Object",
        getNameFun: function(){
            return this.name;
        }
    };
    function test(){
        return this.name;
    }

    console.log(object.getNameFun()); //My Object
    console.log((object.getNameFun)());//My Object
    console.log((object.getNameFun = object.getNameFun)()); //the window
    //该语句先执行赋值,再调用赋值后返回的结果。由于object.getNameFun是函数本身,等同于下面的语句,先把函数赋值给一个变量名,这时的this会发生改变,不再指向object,而是指向全局window
    var t = test;
    console.log(t()); //the window

【块级作用域/私有作用域】

JavaScript没有块级作用域的概念,因此在块级语句中定义的变量是存在于其包含函数中的。而匿名函数可以模仿(块级作用域/私有作用域)。
语法:
(function(){
    //这里是块级作用域
})();
定义并立即执行该匿名函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。由于JavaScript会将function关键字当作函数声明的开始,但是函数声明后面是不能加圆括号立即执行的,所以将函数声明放在一对圆括号中表示它实际上是一个函数表达式。
function UsePrivateScope(count){
    (function(){
        //for语句是私有作用域,变量i只能在该循环语句中使用
        for(var i=0;i<count;i++){
            console.log(i);
        }
    })();

    console.log("test:"+i); //ReferenceError: i is not defined
}
UsePrivateScope(3);

当需要一些临时变量时,就可以使用匿名函数模仿私有作用域。在实际开发中,通过创建私有作用域,
1. 可以避免使用过多的全局变量和函数而导致的命名冲突情况。
2. 可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链。

【私有变量】

  • 任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问到这些变量。

  • 私有变量包括:函数的参数、局部变量、函数内部定义的其他函数

  • 由于闭包通过其作用域链可以访问到其包含函数的所有变量,因此可以利用闭包创建一个访问私有变量的公有方法。一般把这种能访问私有变量和私有函数的公有方法称为“特权方法”。


一、为自定义类型创建私有变量“特权方法”的两种方法:

1. 在构造函数中定义
function MyObject(){
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
    //特权方法
    this.publicMethod = function(){
        console.log(privateVariable);
        return privateFunction();
    }

}
var instance1 = new MyObject();
console.log(instance1.privateVariable); //undefined
console.log(instance1.privateFunction()); //TypeError: instance1.privateFunction is not a function
console.log(instance1.publicMethod()); //10 false

除了特权方法,没有其他方法可以直接访问privateVariable和privateFunction()。

【缺点】:对于每个实例都会创建同样的一组新方法,不能实现函数复用。
2. 在私有作用域中定义
(function(){
    var privateVariable = 10; //私有变量
    //私有函数
    function privateFunction(){
        return false;
    }
    MyObject = function(){}; //构造函数(使用函数表达式是因为函数声明只能创建局部变量,没有使用var是因为这样会使MyObject成为一个全局变量,能在私有作用域外被访问。)
    //特权方法
    MyObject.prototype.publicMethod = function(){
        console.log(privateVariable);
        return privateFunction();
    }
})();
var instance1 = new MyObject();
console.log(instance1.publicMethod()); //10 false
该方法的私有变量和私有函数由实例共享,同时,其特权方法在原型上定义,因此所有实例使用同一个特权函数。
【缺点】:每个实例没有自己的私有变量。

二、为单例创建私有变量和特权方法

【单例】:只有一个实例的对象,通常都是作为全局对象存在的。惯例用对象字面量创建单例对象。

1. 作为Object的实例的单例

var singleton = function(){
    var privateVariable = 10; //私有变量
    //私有函数
    function privateFunction(){
        return false;
    }
    return {
        //特权方法
        publicMethod: function(){
            console.log(privateVariable);
            return privateFunction();
        }
    };
}();
console.log(singleton.publicMethod()); // 10 false

2. 作为特定类型的实例的单例

var singleton = function(){
    var privateVariable = 10; //私有变量
    //私有函数
    function privateFunction(){
        return false;
    }
    //创建特定类型的对象
    var someObject = new someClass();
    //特权方法
    someObject.publicMethod = function(){
        console.log(privateVariable);
        return privateFunction();
     };
    //返回对象,将这个局部变量赋值给全局变量singleton
    return someObject;
}();
console.log(singleton.publicMethod()); // 10 false
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值