第三章 函数

变量作用域
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)匿名回调函数可以节省全局变量
//实现一个函数对数组中的各个元素先乘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')
  • 内部(私有)函数
    全局函数a内部定义的局部函数b,在全局函数外不可见执行a时会执行b
functiona(){
var b =  function b(){}
}

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

闭包
  • 作用域链
      js不存在大括号级作用域,但有函数作用域。即变量如果在某代码块中定义,在代码块外可见;在函数内定义的变量函数外不可见。如果在函数f内定义函数n则n不仅可以访问自身作用域中的变量,还能访问其父级作用域,这就形成一条作用域链(scope chain)。
  • 词法作用域
      在js中,每个函数都有自己的词法作用域。每个函数在被定义(而非执行)时,都会创建一个属于自己的环境(作用域)
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
  • 利用闭包突破作用域链


    1552717788(1).jpg
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和不使用的区别
  • 在函数内部如果用var声明变量和不用时有很大差别,用var声明的是局部变量,在函数外部访问这个变量是访问不到的,没var声明的是全局变量。在函数外部是可以访问到的。
  • 全局作用域内声明变量
    在这里用var声明的变量我们之所以认为声明的是全局变量是因为它现在处于的作用域范围是全局,实际上它声明也是局部变量,只是现在它的局部变量是全局而已,所以就相当于起着全局变量的作用。全局作用域中不用var声明的也是全局变量,那么它俩有什么区别呢?
    比较var a= 1 跟 a= 1,前者是变量声明,带不可删除属性,因此无法被删除;后者为全局变量的一个属性,因此可以从全局变量中删除

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容