JavaScript闭包与箭头函数

闭包
闭包是JavaScript中最强大的特性之一

JavaScript允许函数嵌套

内部函数可以访问定义在外部函数中的所有变量和函数以及外部函数能访问的所有变量和函数

外部函数不能够访问定义在内部函数中的变量和函数

当内部函数生存周期大于外部函数时,由于内部函数可以访问外部函数的作用域,定义在外部函数的变量和函数的生存周期就会大于外部函数本身

当内部函数以某一种方式被任何一个外部函数作用域访问时,一个闭包就产生了

var pet = function(name) {   // The outer function defines a variable called "name"
  var getName = function() {
    return name;             // The inner function has access to the "name" variable of the outer function
  }
  return getName;            // Return the inner function, thereby exposing it to outer scopes
},
myPet = pet("Vivie");
   
myPet();                     // Returns "Vivie"

下面例子,返回了一个包含可以操作外部函数的内部变量方法的对象

var createPet = function(name) {
  var sex;
  
  return {
    setName: function(newName) {
      name = newName;
    },
    
    getName: function() {
      return name;
    },
    
    getSex: function() {
      return sex;
    },
    
    setSex: function(newSex) {
      if(typeof newSex == "string" 
        && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
pet.getName();                  // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex();                   // male
pet.getName();                  // Oliver

内嵌函数的内嵌变量就像内嵌函数的保险柜。它们会为内嵌函数保留“稳定”——而又安全——的数据参与运行。而这些内嵌函数甚至不会被分配给一个变量,或者不必一定要有名字。

var getCode = (function(){
  var secureCode = "0]Eal(eh&2";    // A code we do not want outsiders to be able to modify...
  
  return function () {
    return secureCode;
  };
})();

getCode();    // Returns the secret code

注意:如果一个闭包的函数用外部函数的变量名定义了同样的变量,那在外部函数域将再也无法指向该变量。

var createPet = function(name) {  // Outer function defines a variable called "name"
  return {
    setName: function(name) {    // Enclosed function also defines a variable called "name"
      name = name;               // ??? How do we access the "name" defined by the outer function ???
    }
  }
}

闭包中的神奇变量this是非常诡异的。
使用它必须十分的小心,因为this指代什么完全取决于函数在何处被调用,而不是在何处被定义。

使用arguments对象
函数的实际参数会被保存在一个类似数组的arguments对象中。在函数内,你可以按如下方式找出传入的引数:

arguments[i]
其中i是引数的序数编号,以0开始

第一个传来的参数会是arguments[0]

参数的数量由arguments.length表示

例如,设想有一个用来连接字符串的函数。唯一事先确定的参数是在连接后的字符串中用来分隔各个连接部分的字符(译注:比如例子里的分号“;”)。该函数定义如下:

function myConcat(separator) {
   var result = "", // initialize list
       i;
   // iterate through arguments
   for (i = 1; i < arguments.length; i++) {
      result += arguments[i] + separator;
   }
   return result;
}

你可以给这个函数传递任意数量的参数,它会将各个参数连接成一个字符串“列表”:

// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

注意:

arguments 变量只是 “类数组对象”,并不是一个数组

称其为类数组对象是说它有一个索引编号和Length属性

它并不拥有全部的Array对象的操作方法

函数参数
两个新的类型的参数:

默认参数(default parameters)

剩余参数(rest parameters)

默认参数
在JavaScript中,函数参数的默认值是undefined

    function multiply(a, b) {
      b = typeof b !== 'undefined' ?  b : 1;
    
      return a*b;
    }

multiply(5); // 5

调用函数时没有实参传递给b,那么它的值就是undefined,于是计算a*b得到、函数返回的是 NaN

剩余参数
剩余参数语法允许将不确定数量的参数表示为数组

function multiply(multiplier, ...theArgs) {
  return theArgs.map(x => multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

箭头函数
箭头函数表达式(也称胖箭头函数, fat arrow function)具有较短的语法相比函数表达式和词法绑定此值。箭头函数总是匿名的。

有两个因素会影响介绍箭头函数:

更简洁的函数

this

更简洁的函数

var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryl­lium"
];
var a2 = a.map(function(s){ return s.length });
var a3 = a.map( s => s.length );
this

在箭头函数出现之前,每一个新函数都重新定义了自己的this值(在严格模式下,一个新的对象在构造函数里是未定义的,通过上下文对象调用的函数被称为“对象方法”等)

function Person() {
  // The Person() constructor defines `this` as itself.
  this.age = 0;

  setInterval(function growUp() {
    // In nonstrict mode, the growUp() function defines `this` 
    // as the global object, which is different from the `this`
    // defined by the Person() constructor.
    this.age++;
  }, 1000);
}
var p = new Person();

在ECMAScript 3/5里,通过把this的值赋值给一个变量可以修复这个问题

function Person() {
  var self = this; // Some choose `that` instead of `self`. 
                   // Choose one and be consistent.
  self.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `self` variable of which
    // the value is the expected object.
    self.age++;
  }, 1000);
}

另外,创建一个约束函数(bound function)可以使得this值被正确传递给growUp()函数

箭头功能捕捉闭包上下文的this值,所以下面的代码工作正常

function Person(){
  this.age = 0;
  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}
var p = new Person();

阅读资料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide

复习方式:实践(代码写一次、跑一次、测试一次),不懂的地方谷歌,阅读和做笔记

底线原则:宁可重写一次,也不复制粘贴

本次复习内容有:函数的后一部分···

复习耗时:断断续续持续了几天···我也是醉了···


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据