第三章 函数

2019-03-15  本文已影响0人  Chasingcar_501
变量作用域
var a=123
function test(){
    alert(a)  //undefined
    var a=1
    alert(a)//1
}
test()

  以上例子中函数作用域会覆盖始终优先于全局作用域,所以局部变量a会覆盖掉全局作用域的a,变量提升不会提升赋值,所以第一个输出undefined,第二次输出时已经被正式定义了。

函数也是一种数据
var f = funtion(){return 1}
typeof f//'function'
1)匿名回调函数可以节省全局变量
//实现一个函数对数组中的各个元素先乘2再加1
function multi_two(a,b,c,callback){
    var arr=[],i
    for(i=0;i<3;i++){
        arr[i]=callback(arguments[i]*2)
    }
    return arr
}
alert(multi_two(1,2,3,function(a){return a+1}))
2)自调函数
不会产生任何全局变量,缺点是无法重复执行,一次性
{
  function (name){
    alert('hello'+name)
  }
}('MSR')
functiona(){
var b =  function b(){}
}

优点:有助于确保全局名字空间的纯净性
   私有性,不必暴露给外部世界

闭包
function f1(){
  var a=1;
  f2();
}
function f2(){return a};
f1();//undefined

  在以上代码中,当f2被定义(而非执行)时,a是不可见的,f2只能访问自身的和全局的。此时f1和f2不存在共享的词法作用域。【自己理解:f1和f2的定义不存在父子关系,不能形成作用域链,是兄弟关系】
  尽管函数在定义时会记录自身所在的环境和作用域链,但不意味着会对作用域中每个变量记录,我们可以在函数中对变量进行添加,修改,移除等操作,但函数只会看到最终状态。这意味着我们可以删除f2并定义一个新的执行体,f1也能正常工作,f1只需知道如何访问自身作用域,不需知道作用域什么时候发生了什么

 var a=1;
function f1(){
    return f2
}
function f2(){return a}
a=44
delete f2
var f2=function(){return a*2}
f1()();//88
1552717828(1).jpg

  N突破作用域链的方法:
1)闭包#1(在函数内返回一个全局函数)

function f(){
  var b='b'
  return function(){
    return b
}
}
b;//not defined

  该函数的 局部变量b在全局是不可见的,f的返回值相当于上图中的N,既可以访问f的空间也可以访问全局,所以b对它可见,因为f是全局函数,在全局空间可调用,所以讲它的返回值赋给另一个全局变量,从而生成一个可以访问f私有空间的新全局函数

var n=f()
n()//b

2)闭包#2(在函数内创建一个全局函数)
  与上法类似,不再返回函数,直接在函数体内创建一个新的全局函数

var n//声明一个全局函数的占位符,不是必须的
function f(){
  var b='b'
  n=function(){
  return b
  }
}
f()//b

调用f()后,由于n的定义没有使用var语句,所以属于全局,n在f内部,可以访问到f的作用域,即使n后来升级到了全局函数也能保留对f的访问权


使用var和不使用的区别

3)相关定义与闭包3#
  根据目前讨论发现,如果一个函数需要在其父级函数之后返回后继续保留对父级作用域的链接的话,就要建立一个闭包。函数所绑定的是作用域本身,不是作用域中的变量或变量当前返回的值。
4)循环中的闭包

function f(){
    var a=[],i;
    for(i=0;i<3;i++){
        a[i]=function(){
            return i;
        }
    }
    return a
}
var a=f()
console.log(a[0]())
console.log(a[1]())
console.log(a[2]())

结果都是3,创建的3个闭包都指向一个共同的局部变量i,但是闭包不会记录他们的值,他们拥有的只是i的引用,因此只能返回i的当前值。循环结束时i=3,因此三个函数都指向3。
解决方案:

function f(){
    var a=[],i;
    for(i=0;i<3;i++){
        a[i]=(function(x){
            return function(){
                return x
            };
        })(i)
    }
    return a
}
var a=f()
console.log(a[0]())
console.log(a[1]())
console.log(a[2]())

不再直接创建一个返回i的函数,而是将i传递给一个自调函数,在该函数中,i被赋值给局部变量x,每次迭代中x就有不同的值了。
或者不用自调函数,在中间函数内,将i的值本地化。

function f(){
    function makeClosure(x){
        return function(){
            return x
        }
    }
    var a=[],i
    for(i=0;i<3;i++){
        a[i]=makeClosure(i)
    }
    return a 
}
console.log(a[0]())
console.log(a[1]())
console.log(a[2]())
Getter与Setter

假设现有一个特殊区间的变量,不将其暴露在外部以免被篡改,我们需要将它保护在相关函数内部,再提供两个函数getter和setter用于赋值和访问,setter和getter在共同的函数中,并在该函数定义secret变量,使得两个函数能共享同一作用域。一切操作是通过一个匿名自调函数实现,在其中定义了全局的getValue和setValue,以此确保secret的不可直接访问性。??

var getValue,setValue
(function (){
    var secret=0
    getValue=function(){
        return secret
    }
    setValue=function(v){
        secret=v
    }
})()
console.log(getValue());
setValue(1234)
console.log(getValue());
迭代器

闭包实现迭代器

function setup(x){
    var i=0
    return function(){
        return x[i++]
    }
}
var next=setup(['m','s','r'])
console.log(next())//m
console.log(next())//s
console.log(next())//r
上一篇 下一篇

猜你喜欢

热点阅读