Heim > Web-Frontend > js-Tutorial > Hauptteil

Einführung in anonyme JavaScript-Funktionen und Tipps zu „closures_javascript'.

WBOY
Freigeben: 2016-05-16 16:04:26
Original
1257 Leute haben es durchsucht

Anonyme Funktion: eine Funktion ohne Namen;
Abschluss: Eine Funktion, die auf Variablen in einem Funktionsbereich zugreifen kann;

1 Anonyme Funktion

// 普通函数
  function box(){            // 函数名是box;
    return 'Lee';           
  }
  box();                // =>Lee; 调用函数;
// 匿名函数
  function(){              // 匿名函数,会报错;
    return 'Lee';
  }
// 通过表达式自我执行
  (function(name){
    console.log(name);        // =>Lee;
  })("Lee");              // "()"表示执行函数,并且可以传参;
// 把匿名函数赋值给变量
  var box = function(){        // 将匿名函数赋给变量;
    return 'Lee';
  };
  console.log(box());         // 调用方式和函数调用相似;
// 函数里的匿名函数
  function box(){
    return function(name){      // 函数里的匿名函数,产生闭包;
      return name;
    };
  };
  console.log(box()("Lee"));      // 函数box()调用匿名函数,并传参;
Nach dem Login kopieren

2 Schließung

Abschluss: Eine Funktion, die Zugriff auf Variablen im Gültigkeitsbereich einer anderen Funktion hat;

Gängige Methoden zum Erstellen von Abschlüssen: Erstellen Sie eine weitere Funktion innerhalb einer Funktion. Greifen Sie über eine andere Funktion auf die lokalen Variablen dieser Funktion zu

// 通过闭包可以返回局部变量
  function box(){
    var user = 'Lee';
    return function(){        // 通过匿名函数返回box()的局部变量user;
      return user;
    };
  }
  console.log(box()());        // =>Lee; 直接调用box()()来获得匿名函数的返回值;

  var b = box();
  console.log(b());          // =>Lee; 另一种调用匿名函数方式;

// 优点:可以把局部变量驻留在内存中,可以避免使用全局变量; 
// (全局变量污染导致应用程序不可预测性,每个模块都可调用必将引来灾难;所以推荐使用私有的,封装的局部变量);
Nach dem Login kopieren
// 缺点:
// 通过全局变量来累加
  var age = 100;            // 全局变量;
  function box(){
    age++;              // 模块级可以调用全局变量,进行累加;
  };
  box();                // 执行函数,累加一次;
  console.log(age);           // =>101; 输出全局变量;
  box();                // 执行函数,累加一次;
  console.log(age);           // =>102; 输出全局变量;
// 通过局部变量无法实现累加
  function box(){
    var age = 100;
    age++;              // 实现累加;
    return age;
  }
  console.log(box());          // =>101;
  console.log(box());          // =>101; 无法实现累加,因为第二次调用函数时,函数内部变量age又被初始化了;

// 通过闭包可以实现局部变量的累加
  function box(){
    var age = 100;
    return function(){        // 匿名函数内实现累加;
      age++;
      return age;          // 并返回累加后的变量; 
    };                // 此时box()函数的局部变量age的值已经被修改为累加后的值;
  }
  var b = box();            // 给box()函数赋值给变量;
  console.log(b());           // =>101; 调用匿名函数,累加一次;
  console.log(b());           // =>102; 第二次调用匿名函数,累加两次;

// PS:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存;所以过度使用闭包会导致性能下降;(将闭包引用在"私有作用域"中即可实现变量销毁)
// 作用域链的机制导致一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值; ?
Nach dem Login kopieren
// 循环里包含匿名函数
  function box(){
    var arr = [];
    for(var i=0; i<5; i++){     // 当声明变量i=5时,循环停止;而此时循环里的变量i==5; 
      arr[i] = function(){    // arr[i]得到的只是没有执行的匿名函数function(){};
        return i;        
      };
    };
    return arr;           // arr = [function,function,function,function,function];
  }
  var b = box();           // =>[function,function,function,function,function]; 得到函数box()返回的数组arr;
  console.log(b.length);       // =>5; 得到函数集合数组长度;
  for(var i=0; i<b.length; i++){
    console.log(box()[i]());    // =>5,5,5,5,5; 输出每个函数的值,都是最后一个值;
  }
  // 上面的例子输出的结果都是5,也就是循环后得到的最大i值;
  // 因为b[i]调用的是匿名函数,匿名函数并没有自我执行,等到调用的时候,box()已执行完毕,i早已变成5;

// 循环里包含匿名函数-改1,自我执行匿名函数
  function box(){
    var arr = [];
    for(var i=0; i<5; i++){
      arr[i] = (function(num){  // arr[i]得到的是匿名函数执行后的结果数值0-4;
        return num; 
      })(i);           // 自我执行并传参;
    }
    return arr; 
  }
  var b = box();           // =>[0,1,2,3,4]; 此时b代表box()返回的数组;
  for (var i = 0; i < b.length; i++) {
    console.log(b[i]);       // 0 1 2 3 4; 这里返回的是数值;
  };
  // 例子中,我们让匿名函数进行自我执行,导致最终返回给a[i]的是数组而不是函数了;最终导致b[0]-b[4]中保留了0,1,2,3,4的值;

// 循环里包含匿名函数-改2,匿名函数里再做个匿名函数;
  function box(){
    var arr = []; 
    for(var i=0; i<5; i++){
      arr[i] = (function(num){
        return function(){   // 返回函数;
          return num;      
        }
      })(i);
    }
    return arr;           // arr = [function,function,function,function,function];
  }
  var b = box();
  for (var i = 0; i < b.length; i++) {
    console.log(b[i]());      // 0,1,2,3,4; 
  };

// 改1和改2中,我们通过匿名函数自我执行,立即把结果赋值给arr[i];
// 每一个i,是调用方通过按值传递的,所以最终返回的都是指定的递增的i;而不是box()函数中的变量i;
Nach dem Login kopieren
Drei dieser Objekte


// 在闭包中使用this对象可能会导致一些问题;this对象是在运行时基于函数的执行环境绑定的;
// 如果this在全局范围就是指向window,如果在对象内部就指向这个对象;
// 而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法; 
  var user = 'Window';
  var obj = {
    user:'Object',
    getUserFunction:function(){
      return function(){            // 闭包不属于obj,里面的this指向window;
        return this.user;
      };
    }
  };
  console.log(obj.getUserFunction()());      // =>Window;

  // 可以强制指向某个对象
  console.log(obj.getUserFunction().call(obj));  // =>Object;

  // 也可以从上一个作用域中的得到对象
  getUserFunction:function(){
    var that = this;               // 从对象的方法里得到this;此时that指向obj对象;
    return function(){
      return that.user;
    }
  }
  console.log(obj.getUserFunction()());      // =>Object;
Nach dem Login kopieren
4 Speicherverlust


// 由于IE的JScript对象和DOM对象使用不同的垃圾收集方式,因此闭包在IE中会导致内存泄漏问题,也就是无法销毁驻留在内存中的元素;
  function box(){
    var oDiv = document.getElementById('oDiv'); // oDiv用完之后一直驻留在内存中;
    oDiv.onclick = function(){
      alert(oDiv.innerHTML);          // 这里用oDiv导致内存泄漏;
    };
    oDiv = null;                 // 解除引用;
  }
  box();
  // 由于匿名函数保存了一个对box()的活动对象的引用,因此就会导致无法减少oDiv的引用数;
  // 只要匿名函数存在,oDiv的引用数至少也是1;因此它所占用的内存就永远不会被回收;
  // PS:如果没有使用解除引用,那么要等到浏览器关闭才得以释放;
Nach dem Login kopieren
5 Imitieren Sie den Bereich auf Blockebene (definieren Sie eine anonyme Funktion und rufen Sie sie sofort auf)


// JS没有块级作用域的概念;
// 这意味着在块语句(for语句/if语句)中定义的变量,实际上是在包含函数中而非语句中创建的;
  function box(count){
    for(var i=0; i<count; i++){}        // box(2); => count=2; i=2时循环停止,此时i=2;
    console.log(i);               // =>2; i不会因为离开了for块就失效;
  }
  box(2);

  function box(count){
    for(var i=0; i<count; i++){}
    var i;                   // 就算重新声明,也不会覆盖前面的值;
    console.log(i);
  }
  box(2);
// 在JavaScript中,变量i是定义在box()的活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它;
// 以上两个例子,说明JavaScript没有块级语句的作用域,if(){}/for(){}等没有作用域;
// 如果有作用域的话,出了这个范围i就应该被销毁;

// JavaScript不会提醒是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果是初始化并赋值,还是会执行的);

// 模仿块级作用域(私有作用域)
  (function(){
    // 这里是块级作用域;
  })();
  // 以上代码定义并立即调用了一个匿名函数;将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式;

// 使用块级作用域(私有作用域)改写
  function box(count){
    (function(){
      for(var i=0; i<count; i++){}
    })();
    console.log(i);                // 报错,无法访问;变量i在私有作用域中,出了私有作用域即被销毁了.
  }
  box(2);
// 使用了块级作用域后,匿名函数中定义的任何变量,都会在执行结束时被销毁;(i只能在循环中使用,使用后即被销毁);
// 而私有作用域中能够访问变量count,是因为这个匿名函数是一个闭包,他能够访问包含作用域中的所有变量;
// 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数;
// 一般来说,我们都应该尽可能少向全局作用域中添加变量和函数;过多的全局变量和函数很容易导致命名冲突;
// 使用块级作用域,每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域;
  (function(){
    var box = [1,2,3,4];
    console.log(box);              // =>[1,2,3,4]; box出来就不被认识了;
  })();                      // 销毁匿名函数中的变量;
  console.log(box);                // =>box is not defined;
  // 在全局作用域中使用块级作用域可以减少闭包占用的内存问题;因为没有指向匿名函数的引用
  // 只要函数执行完毕,就可以立即销毁其作用域链了;
Nach dem Login kopieren
6 Private Variablen


// JavaScript没用私有属性的概念;所有的属性都是公用的;
// 不过有一个私有变量的概念:在任何函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量;
// 私有变量包括函数的参数/局部变量和在函数内部定义的其他函数;
  function box(){
    var age = 100;                 // 私有变量,外部无法访问;
  }

// 而通过内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量;
// 而利用这一点,可以创建用于访问私有变量的公用方法;特权方法;
  function Box(){                  // 构造函数;
    var age = 100;                 // 私有变量;
    function run(){                // 私有函数;
      return '运行中...';
    };
    this.get = function(){             // 对外公共的特权方法;
      return age+run();             // 将闭包赋值给变量;
    };
  }
  var box = new Box();
  console.log(box.get());

// 可以通过构造方法传参来访问私有变量
  function Person(name){
    var user = name;              // 这句其实可以省略;
    this.getUser = function(){
      return user;
    };
    this.setUser = function(name){
      user = name;
    }
  }
  var p = new Person('Lee');
  console.log(p.getUser());            // =>Lee;
  console.log(p.setUser('Jack'));
  console.log(p.getUser());            // =>Jack;
  // 但是,构造函数模式的缺点是针对每个实例都会创建同样一组新方法;而使用静态私有变量来实现特权方法就可以避免这个问题;
Nach dem Login kopieren
Sieben statische private Variablen


// 通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公共的特权方法;
  (function(){                  // 创建私有作用域;
    var age = 100;               // 静态私有变量;
    function run(){
      return '运行中...';
    };
    Box = function(){};             // 使用函数表达式定义构造函数;
    Box.prototype.go = function(){       // 公有(特权)方法;在原型上定义的;
      return age+run();
    };
  })();
  var box = new Box();
  console.log(box.go());             // 100运行中...;
// 上面的对象声明,采用的是Box = function(){}而不是functiong Box(){};并且在声明Box时没有使用var关键字
// 导致:初始化未经声明的变量,总是会创建一个全局变量;因此,Box就成了一个全局变量,能够在私有作用域之外被访问到;
// 因为如果用函数声明定义构造函数,那么就变成私有函数了,无法在全局访问到了,所以要使用函数式定义构造方法;
  (function(){
    var user = "";
    Person = function(value){          // 此处定义的Person是全局变量;
      user = value;              // 这里的构造函数有权访问私有变量name;
    };
    Person.prototype.getUser = function(){
      return user;
    };
    Person.prototype.setUser = function(value){
      user = value;
    }
  })();
  var person = new Person();
  person.setUser('Lee');
  console.log(person.getUser());          // =>Lee;
// 使用了prototype导致方法共享了,而user也就变成静态属性了;
// 所谓静态属性:即共享于不同对象中的属性;&#63;
Nach dem Login kopieren
Acht-Modul-Modus


// 简言之,如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式;
// 之前采用的都是构造函数的方式来创建私有变量和特权方法;
// 那么对象字面量方式就采用模块模式来创建;
  var box = {                   // 字面量对象,也是单例对象:只有一个实例的对象;
    age:100,                   // 这是公有属性,将要改成私有;
    run:function(){
      return '运行中...';
    };
  };

// 模块模式私有化变量和函数:
  var box = function(){
    var age = 100;
    function run(){
      return '运行中...';
    }
    return {                   // 将一个字面量对象作为函数的值返回;
      go:function(){              // 返回的对象字面量中只包含可以公开的属性和方法;
        return age+run();          // 由于这个对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数;
      }                  
    };                      // 从本质上讲,这个对象字面量定义的是单例的公共接口;
  }();
// 这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的; 

// 上面直接返回对象的例子,也可以这么写:
  var box = function(){
    var age = 100;
    function run(){
      return '运行中...';
    }
    var obj = {                  // 创建字面量对象;
      go:function(){
        return age+run();
      }
    };
    return obj;                  // 返回刚创建的对象;
  }();

// 字面量的对象声明,其实在设计模式中可以看作是一种单例模式;
// 所谓单例模式,就是永远保持对象的只有一个实例;

// 增强的模块模式:适合返回自定义对象,也就是构造函数;
  function Desk(){};
  var box = function(){
    var age = 100;
    function run(){
      return '运行中...';
    };
    var desk = new Desk();
    desk.go = function(){
      return age+run();
    };
    return desk;
  }();
  console.log(box.go());              // =>100运行中;
Nach dem Login kopieren
Neun Zusammenfassung

Funktionsausdrücke sind in der JavaScript-Programmierung eine sehr nützliche Technologie. Durch die Verwendung von Funktionsausdrücken kann eine dynamische Programmierung erreicht werden, ohne die Funktion zu benennen.

1. Funktionsausdruck

Funktionsausdrücke unterscheiden sich von Funktionsdeklarationen; Funktionsdeklarationen erfordern einen Namen, Funktionsausdrücke jedoch nicht;

Ein Funktionsausdruck ohne Namen wird als anonyme Funktion bezeichnet. 2. Abschluss

Wenn andere Funktionen innerhalb einer Funktion definiert werden, wird ein Abschluss erstellt. Der Abschluss hat Zugriff auf alle in der Funktion enthaltenen Variablen In der Hintergrundausführungsumgebung umfasst die Bereichskette des Abschlusses seinen eigenen Bereich, den Bereich der enthaltenden Funktion und den globalen Bereich Normalerweise werden der Umfang einer Funktion und alle ihre Variablen zerstört, nachdem die Funktionsausführung beendet ist
Wenn eine Funktion jedoch einen Abschluss zurückgibt, wird der Gültigkeitsbereich der Funktion im Speicher gespeichert, bis der Abschluss nicht mehr vorhanden ist. 3. Gültigkeitsbereich auf Blockebene
Sie können Abschlüsse verwenden, um den Bereich auf Blockebene in JavaScript zu imitieren (JavaScript selbst kennt kein Konzept für den Bereich auf Blockebene); Erstellen Sie eine Funktion und rufen Sie sie sofort auf, damit der darin enthaltene Code ausgeführt werden kann, ohne einen Verweis auf die Funktion im Speicher zu hinterlassen;
Das Ergebnis ist, dass alle Variablen innerhalb der Funktion sofort zerstört werden – es sei denn, einige Variablen werden Variablen im enthaltenden Bereich (d. h. externen Bereich) zugewiesen. 4. Private Variablen
Abschlüsse können auch zum Erstellen privater Variablen in Objekten verwendet werden. Die wichtigsten Punkte sind wie folgt:
Auch wenn es in JavaScript kein wirkliches Konzept für private Objekteigenschaften gibt, können Abschlüsse zum Implementieren öffentlicher Methoden verwendet werden, und über öffentliche Methoden können Sie auf Variablen zugreifen, die im enthaltenden Bereich
definiert sind Sie können den Konstruktormodus und den Prototypmodus verwenden, um privilegierte Methoden benutzerdefinierter Typen zu implementieren, und Sie können auch den Modulmodus verwenden, um Singleton-privilegierte Methoden zu implementieren

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage