JS 中闭包的变量 闭包与this

闭包与变量:

作用域链的一个副作用,闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。

    function fn1(){
        //创建一个数组
        var arr = new Array();
        //为数组赋值,此时i是保存在fn1 这个作用域中
        for (var i = 0; i <10; i++) {
            arr[i] = function(){
                return i
            }
        }
    return arr;
   }

   var fs = fn1();
   for (var i = 0; i < fs.length; i++) {
       fs[i]();   //此时通过闭包来调用函数,会去上一级作用域中找,这是i的值已经是10,所以会连续输出10个  10;
   };

解决方法:通过创建另一个匿名函数强制让闭包的行为符合预期,

 function fn1(){
        var arr = new Array();
        for (var i = 0; i <10; i++) {
            arr[i] = (function(num){
                return function(){
                    return num;
                }
            })(i);  //此时 有大量的作用域
        }
    return arr;
   }

   var fs = fn1();
   for (var i = 0; i < fs.length; i++) {
       fs[i]();    //此时就是 0,1,2,3,4,5,6,7,8,9  ,
   };

消耗大量的内存,

闭包的this问题:

在闭包中使用 this 对象也可能会导致一些问题,this 对象是在运行时基于函数的执行环境绑定的:
在全局函数中, this 等于 window,当函数作为某个对象的方法调用时, this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

var name ="A";
   var object = {
        name: "obj",
        say: function(){
            return function(){
                return this.name;
            }
        }
   }
   object.say()();  //此时,输出的是A,当调用object.say()后,object的函数 内存空间已经释放,但是里面的闭包 依然存在,此时这个this就是指向window

解决方法:

 var name ="A";
   var object = {
        name: "obj",
        say: function(){
            //此时 that 就指向了 object
            var that = this;
            return function(){
                return that.name;
            }
        }
   }
 object.say()();  //此时,输出的是obj,

内存的泄露:

如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。

  function assignHandler(){
            var element = document.getElementById("someElement");
            element.onclick = function(){
      alert(element.id);
    };
  }

创建了一个作为 element 元素事件处理程序的闭包,匿名函数保存了一个对 assignHandler()的活动对象的引用,只要匿名函数存在, element 的引用数至少也是 1,因此它所
占用的内存就永远不会被回收。可以通过稍微改写一下代码来解决,

function assignHandler(){
        var element = document.getElementById("someElement");
        var id = element.id;
        element.onclick = function(){
            alert(id);
        };
        element = null;
  }

把 element.id 的一个副本保存在一个变量中,并且在闭包中引用该变量消除了循环引用。仅仅做到这一步,还是不能解决内存泄漏。闭包会引用包含函数
的整个活动对象,而其中包含着 element。即使闭包不直接引用 element,包含函数的活动对象中也仍然会保存一个引用。因此,有必要把 element 变量设置为 null。

块级作用域:

JavaScript 没有块级作用域的概念,不管是使用循环还是判断之后,这个变量会一直存在,所以当全局中使用了 循环 或者判断后,这个变量可能会影响到函数的变量,所以除非特殊情况,不要使用全局变量,且 全局变量在作用域链最顶部,访问最慢,

for (var i = 0; i < 10; i++) {

  };
  alert(i);   //此时i就是 10;

解决的方式是匿名函数,用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。

 (function(){
    //这里是块级作用域
    })();

定义并立即调用了一个匿名函数。在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员既可
以使用自己的变量,又不必担心搞乱全局作用域。
这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

私有变量:

私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

function add(num1, num2){
    var sum = num1 + num2;
    return sum;
}这个函数内部,有 3 个私有变量: num1、 num2 和 sum。在函数内部可以访问这几个变量,但在函数外部则不能访问它们。如果在这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访
问这些变量。

有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。

function Person(name){
    //特权方法
    this.setName = function(value){
        name = value;
    }
    //特权方法
    this.getName = function(){
        return name;
    }
 }
 var p = new Person("Linda");   p.setName("Joke");   p.getName(); //Joke

两个特权方法: getName()和 setName()。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量 name。但在 Person 构造函数外部,没有任何办法访问 name。
它们作为闭包能够通过作用域链访问 name。私有变量 name在 Person 的每一个实例中都不相同,因为每次调用构造函数都会重新创建这两个方法。
构造函数模式的缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题。

静态私有变量:

多查找作用域链中的一个层次,就会在一定程度上影响查找速度。而这正是使用闭包和私有变量的一个显明的不足之处

模块模式

模块模式通过为单例添加私有变量和特权方法能够使其得到增强,

var singleton = (function(){
    //私有变量和私有函数
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }

    //特权/公有 方法和属性
    return {
        publicProperty: true,
        publicMethod : function(){
                privateVariable++;
                return privateFunction();
        }
    }
 })();
这个模块模式使用了一个返回对象的匿名函数。匿名函数内部,定义了私有变量和函数。返回的对象 字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的,因此它的公有方法 有权 访问私有变量和函数。这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的:
 var application = (function(){
    //私有变量和函数
    var components = new Array();
    components.push(new BaseComponent());
    //公有
    return{
        getComponent:function(){
            return components.length;
        },
        registerComponent:function(component){
            if(typeof component == "object")
            {
                components.push(component);
            }
        }
    }
 })();

如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。以这种模式创建的每个单例都是 Object 的实例,

增强的模块模式:

这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。

var singleton = (function(){
    //私有变量和私有函数
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
    //创建对象
    var object = new CustomType();
    //添加特权/公有属性和方法
    object.publicProperty = true;
    object.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    };
    //返回这个对象
    return object;
})();

总结:

函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫做匿名函数。
递归函数应该始终使用 arguments.callee 来递归地调用自身,不要使用函数名——函数名可能会发生变化。

当在函数内部定义了其他函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量,原理:

闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。

函数的作用域及其所有变量都会在函数执行结束后被销毁。

当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。使用匿名函数可以在 JavaScript 中模仿块级作用域(JavaScript 本身没有块级作用域的概念),要点如下。

? 创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。
? 结果就是函数内部的所有变量都会被立即销毁—

闭包还可以用于在对象中创建私有变量,相关概念和要点如下。
? 即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量。
? 有权访问私有变量的公有方法叫做特权方法。
? 可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式、增强的模块模式来实现单例的特权方法。

JavaScript 中的函数表达式和闭包都是极其有用的特性,利用它们可以实现很多功能。因为创建闭包必须维护额外的作用域,所以过度使用它们可能会占用大量内存。


时间: 2024-10-03 14:57:26

JS 中闭包的变量 闭包与this的相关文章

JS中的作用域和闭包

作用域:在编程语言中,作用域控制着变量与参数的可见性及生命周期.JS确实有函数作用域,那意味着定义在函数中的参数和变量在函数外部是不可见的,而且在一个函数中的任何位置定义的变量在该函数中的任何地方都是可见的. var a = 1; var fs = function (){ var b = 2; var c = 4 var fun = function (){ var c = 3; alert(a) //输出1 alert(b) //输出2 alert(c) //输出3 } fun(); } f

JS中的let变量

介绍JS中的let变量: let允许你声明一个作用域被限制在块级中的变量.语句或者表达式.在Function中局部变量推荐使用let变量,避免变量名冲突. 作用域规则 let 声明的变量只在其声明的块或子块中可用,这一点,与var相似.二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数. function varTest() { var x = 1; if (true) { var x = 2; // 同样的变量! console.log(x); // 2 } console.log

JS中的函数与闭包

JavaScript中的函数由于其独特的功能和特性大大增强了JS的编程实力.在JS中函数也是对象,所以可以为它添加属性和方法,但typeof操作符对函数返回字符串’fnuction’,原生的toString()方法返回代表这个函数的源代码. 一.函数的实质      函数本身也是JS中创建的一种变量(准确分类为对象),它由一系列可执行代码+name属性+形参列表组成.函数可以通过name属性或者变量名来调用.函数具有函数作用域,相对于全局作用域对程序执行环境的变换进行控制. 二.函数的定义   

JS中,什么是闭包?闭包有什么好处?

相信网上对闭包有很多的解释,毕竟这个玩意儿是仁者见仁智者见智,对于我来说,总结了2个特性: 1.函数嵌套函数,内部函数可以引用外部函数的参数和变量 function aaa() { var a = 5; function bbb() { alert(a) } return bbb } var c = aaa(); c(); 2.参数和变量不会被垃圾回收机制所收回 JS中的垃圾回收机制 function test(){ var a = 1; alert(a) } test(); // 执行完这个函

js中,var 修饰变量名和不修饰的区别

js中 允许在定义变量的时候 不加var 修饰符.js会在当前作用域下寻找上下文是否定义了此变量, 如果没有找到则会为这个变量分配内存.当且将其视为window的成员. 也就是全局变量. 如果加了var 修饰符. js会重新为这个变量分配内存,不论当前上下文中是否已经定义过了.这个变量的作用域就为当前上下文. 即局部变量. 不加var的写法是强烈不推荐的.1. 语义不清楚. 2. 团队开发时,容易覆盖掉其它作用域内的变量,引发异常.3. 给window对象添加不必要成员. 等等

js中静态函数与变量

一 私有变量和函数 js中没有概念上的私有,公有也没有静态和非静态相关概念,有的只能是通过作用于来模仿 函数的块级作用域使得函数内部成员可以不被外部所访问,比如我们使用块级作用于定义一个类 //定义一个命名空间,用于相关类挂靠到该空间下,使得外部通过该命名空间访问某个类型 var MyLib = window.MyLib = MyLib || {}; (function{ // 在函数内部 name 是不会被外部直接能够访问的,除非通过方法暴露接口 var name=''; //同样的道理,如果

JS中的this变量的使用介绍

脚本之家 在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 对于this变量最要的是能够理清this所引用的对象到底是哪一个,也许很多资料上都有自己的解释,但有些概念讲的偏繁杂.而我的理解是:首先分析this所在的函数是当做哪个对象的方法调用的,则该对象就是this所引用的对象. 示例一. 代码如下: var obj = {}; obj.x = 100; obj.y

js中要声明变量吗?

你好,js语言是弱类型语言,无需申明即可直接使用,默认是作为全局变量使用的.建议:在function里时应使用var 申明变量,这样改变量仅仅只在function的生存周期内存在,不会污染到,全局控件.至于直接在<script>标签内使用的话则申明不声明效果都是一样的. 下面的文章可以帮助你更清楚的了解js变量一.变量的类型 Javascript和Java.C这些语言不同,它是一种无类型.弱检测的语言.它对变量的定义并不需要声明变量类型,我们只要通过赋值的形式,可以将各种类型的数据赋值给同一个

JS中的 公有变量、私有变量 !

公有变量.私有变量 ! 初学者的见解,算是记录学习过程,也算是分享以便共同成长,如有不正确的地方,还请不吝赐教! 先看代码1: function car(){ var wheel = 3; //私有变量 this.wheel = 4; //公有变量 alert(wheel); alert(this.wheel); } var car1 = new car();结果是:3 4 代码2: function car(){ var wheel = 3;//私有变量 this.wheel = 4;//公有