函数与闭包

165 阅读4分钟

阅读《JavaScript面向对象编程指南(第2版)》 -- 第三章


笔记

函数的声明


  • 关键字function
  • 函数名称
  • 函数所需的参数
  • 函数要执行的代码块
  • return 子句 一个函数只有一个返回值

参数


  • arguments变量返回函数所接收的所有参数
  • arguments.length返回函数调用时所接收的所有参数数量
  • arguments是类数组

预定义函数


  • parseInt() 转为整数,失败NaN,第二参数(10|16|8)进制,遇到第一个异常字符就放弃。
  • parseFloat() 转为浮点数,遇到第一个异常字符就放弃,支持指数
  • isNaN() 是否可以转为数字
  • isFinite() 是否是一个非Infinity也非NaN的数字
  • encodeURI() 转义完整url(识别http://)
  • decodeURI() 反转义完整url(识别http://)
  • edcodeURIComponent() 转义部分url(不识别http://)
  • decodeURIComponent() 反转义部分url(不识别http://)
  • eval() 将输入的字符串当做JavaScript代码来执行 JavaScript中最大的数字为1e+308,最小数字是5e-324

变量作用域


  • 变量的定义是以函数为作用域的。
  • 全局作用域是指定义在所有函数之外的变量。
  • 局部作用域是指定义在某个函数中的变量。
  • 声明时没有var,默认为全局变量。

变量提升


  • 函数局部作用域会覆盖全局作用域。
  • JavaScript在执行函数时,所有变量声明(函数)都会被提升到最先执行,赋值操作不会提升。
  • 将函数赋值给变量,称之为函数表达式。
  • 函数表达式不会提升,函数声明会提升,函数声明可以在声明之前调用函数。

回调/即时/私有/重写自己的函数


  • 将函数A传递给函数B,并有函数B来执行函数A。A就是回调函数。

  • 将匿名函数放进一对括号中,然后外面在紧跟一对括号,称即时函数。第二对括号起立即调用的作用。适合执行一些一次性的初始化任务。

    (function(name) {...}) ('lili') ||  (function(name) {...} ('lili') )

  • 在函数内部声明和调用的函数为私有函数。

  • 函数在第一次调用后重写自己,从而避免了每次调用时一些不必要的操作,适用于执行某些初始化工作。让函数根据浏览器重新定义自己。

  function a(){
     ... // 初始化
     return function (){
     	... 
     }
  }
  a = a()
  function a(){
     ... // 初始化
     a =  function (){
     	... 
     }
  }

闭包


  • 在函数内部定义的所有变量在该函数外部是不可见的。
  • 有权访问另一个函数作用域中的变量的函数,称为闭包。或者一个函数在其父极函数返回之后留住对父极作用域的链接。
  • 函数所绑定的是作用域本身,而不是函数定义时的值。
  // 闭包1 返回函数,赋值全局变量
  function F() {
  	var b = 'local variable'
    var N = function(){
    	return b;
    }
    return N
  }
  var inner = F()
  inner()  // 'local variable'
  // 闭包2 无需返回,内部赋值全局变量
  var inner;
  function F() {
  	var b = 'local variable'
    var N = function(){
    	return b;
    }
    inner = N
  }
  inner()  // 'local variable'
  // 闭包3 返回函数,使用函数参数
  function F(param) {
    var N = function(){
    	return param;
    }
    param++;
    return N
  }
  var inner = F(123)
  inner() // 124 
  • 在循环中,创建闭包时,不会记录变量的值,拥有的只是相对域的一个链接。
  function F(){
  	var arr = [];
    for(var i=0;i<3;i++){
      arr[i] = function(){
        return i;
      }
    }
    return arr
  }
  var arr = F()
  arr[0]() // 3
  arr[1]() // 3
  arr[2]() // 3
  function F(){
  	var arr = [];
    for(var i=0;i<3;i++){
      arr[i] = (function(x){
      	return function(){
          return x;
        }
      })(i)
    }
    return arr
  }
  var arr = F()
  arr[0]() // 0
  arr[1]() // 1
  arr[2]() // 2

getter and setter


  • 闭包的应用
  • 假设有一个变量,它所表示某类特定的值或区间,不想暴露给外部,不想其他代码有修改的变量值得可能,所以要将它保留在相关函数内部。然后提供了两个额外的函数。一个用于获取变量的值。另一个用于给变量重新复制。并在函数中引入某种验证措施,以便赋值前给与一定的变量保护。
  var get,set ;
  (function(){
  	var value = 0;
    
    get = function(){
    	return value
    }
    
    set = function(newVal){
    	... // 验证
    	value = newVal
    }
  })()

迭代器


  • 闭包的应用
  • 通常我们都知道如何遍历简单的数组,但有时我们需要面对更复杂的数据结构,它们通常会有着数组截然不同的序列规则,这时候需要将“谁是下一个”的复杂逻辑封装成易于使用的next()函数,然后我们只需简单的调用next()就能实现相关的遍历操作了。
  function setup(arr){
  	var i = 0;
    return function(){
      return arr[i++]
    }
  }
  var next = setup(['a','b','c','d'])
  next() // 'a'
  next() // 'b'
  next() // 'c'
  next() // 'd'

练习题

  function getRGB(color){
  	var colorArr = color.split('')
    var one = parseInt((colorArr[1]+ colorArr[2]),16)
    var two = parseInt((colorArr[3]+ colorArr[4]),16)
    var three = parseInt((colorArr[5]+ colorArr[6]),16)
    return `rgb(${one},${two},${three})`
  }
  
  function getRGB(color){
  	var arr=[];
	var colorArr = color.split('')
    for(var i=1;i<colorArr.length;i=i+2){
      arr.push(parseInt((colorArr[i]+ colorArr[i+1]),16))
    }
	 return 'rgb('+arr[0]+','+arr[1]+','+arr[2]+')'
  }
  var a = getRGB('#00FF00')
  a //rgb(0,255,0)
  // 2. 求值
  parseInt(1e1)  // 10  不支持指数为负数 (1e-1)
  parseInt('1e1')  // 1
  parseFloat('1e1')  // 10 指数为负数 (1e-1)0.1
  // 3. alert的内容是什么
  var a = 1;
  function f(){
  	function n(){
    	alert(a)
    }
    var a = 2;
    n()
  }
  f() // alert(2)