第七章 函数表达式(js高级程序设计)

  • 函数变量提升:在执行代码之前会先读取函数声明。这就意味着可以把函数声明放在调用它的语句后面。
  • 函数声明:
say()
function say(){

}
  • 函数表达式
fun ();//error 函数表达式不存在函数变量提升
var fun = function(){
  //这个function是匿名函数
}

递归

  • 使用arguments.callee()实现递归。在编写递归函数时,使用 arguments.callee 总比使用函数名更保险。
function factorial(num){ 
 if (num <= 1){ 
 return 1; 
 } else { 
 return num * arguments.callee(num-1); 
 } 
} 
  • 命名函数表达式的形式创建(在严格模式中arguments.callee会报错)
var factorial = (function f(num){ 
 if (num <= 1){ 
 return 1; 
 } else { 
 return num * f(num-1); 
 } 
}); //即使是修改了factorial变量名函数内部也能调用到f()命名函数

闭包

  • 闭包是指有权访问另一个函数作用域中的变量的函数

  • 捋一下一般函数的一生:

    1. 当创建一个函数时,会生成一个预先包含全局变量对象(变量对象包括作用域链上的所有对象,全局对象永远在变量对象上,而局部环境的变量对象只在函数执行过程中存在)作用域链,存放在函数的[[Scope]]属性中
    2. 当调用函数执行时,函数会创建执行环境。首先他会吸收[[Scope]]属性作为执行作用域链,随后再将arguments组成的活动对象推入执行环境作用域链的前端。
    3. 当函数执行完毕,局部活动对象销毁,全局活动对象保存在内存中


      一般函数的执行环境
  • 捋一下闭包函数的一生:
    首先用父函数和匿名函数来表示外层函数和返回的内层函数

    1. 当调用执行父函数创建匿名函数后,即使父函数已经执行完毕,但是其活动对象不会被销毁,因为他被匿名函数引用
    2. 匿名函数执行时,会吸收父函数和全局变量对象作为执行作用域链,随后再将自己局部变量和arguments组成的活动对象推入作用域链前端
      所以匿名函数的作用域链是:
      argument和局部变量组成的活动对象
      父函数的活动对象
      全局变量对象


      父函数和匿名函数执行环境
  • 闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。

闭包与变量

  • 闭包只能取得包含函数中任何变量的最后一个值

  • 闭包父函数的活动对象只会保留父函数执行完毕后的一次。举个栗子:父函数执行 a=1 a=2 a=3,执行完毕后a=3,保存给内部匿名函数的活动对象的a就是等于3。所有返回的匿名函数都是指向相同作用域下的那个a

  function createFunctions() {
    debugger
    var result = new Array();
    for (var i = 0; i < 10; i++) {
      result[i] = function () {
        return i;
      };
    }
    i = 2 //将i重新赋值为2,则返回结果都是2
    return result;
  }
  const a = createFunctions()
  console.log(a[0]())//2
  console.log(a[5]())//2
  • 当createFunctions执行时,父函数创建i变量,i随着程序执行变成0,变成1,变成2。。。变成10,变成2。当createFunctions完全执行完毕后,才会将自己的活动变量交给匿名函数,此时交出去的i是2!!!,所以返回的所有匿名函数得到的i都是2!!
  • 解决方法:
    1. 返回匿名自执行函数,匿名自执行函数再返回闭包函数,匿名自治性函数传参给闭包函数返回。
    2. 将var改成let,每个for循环都是不同的作用域,返回的不再是同一个i

关于this对象

  • this 对象是在运行时基于函数的执行环境绑定的。
    目前知道的this指向
  1. 全局下指向window
  2. 对象中函数执行对象
  3. 匿名闭包函数的this指向全局window(每个函数在被调用时都会自动取得两个特殊变量:this 和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量)
  • 获得父函数this的方法,将父函数this赋值给变量传递

闭包带来的内存泄露

  • 闭包会导致父函数局部变量一直保存在内存中,因为闭包函数一直保持着对他的引用
  • 怎么解决?
    1. 将局部变量中需要用到的值取出来引用,例如const age = obj.age
    2. 将父函数的局部变量变为null obj=null(为什么都没有引用obj还不会被垃圾回收呢?因为闭包函数会引用所有父函数的活动对象
function assignHandler(){ 
 var element = document.getElementById("someElement"); 
 var id = element.id; 
 
 element.onclick = function(){ 
 alert(id); 
 }; 
 
 element = null; 
} 

模仿块级作用域

  • 在匿名函数中定义的任何变量,都会在执行结束时被销毁
function outputNumbers(count){ 
 (function () { 
   for (var i=0; i < count; i++){ 
     alert(i); 
   } 
 })(); 
 
 alert(i); //导致一个错误!
} 
  • 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
  • 这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

私有变量(闭包还可以用在对象创建私有变量)

  • 私有变量:函数中固定的变量(函数参数和函数的局部变量)
  • 特权方法:能访问到私有变量的方法
    1. 使用闭包,匿名函数返回父函数中的局部变量
    2. 构造函数中,通过get(),set()返回私有变量属性

静态私有属性:在私有作用域中用原型返回私有属性

模块模式:如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。和闭包差不多,就是返回了个对象

  var application = function () {
    //私有变量和函数
    var components = new Array();
    //初始化
    components.push(new BaseComponent());
    //公共
    return {
      getComponentCount: function () {
        return components.length;
      },
      registerComponent: function (component) {
        if (typeof component == "object") {
          components.push(component);
        }
      }
    };
  }();

增强的模块模式

  • 单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况(就是返回了一个对象,该对象又是某个构造函数的实例)
  var application = function () {
    //私有变量和函数
    var components = new Array();
    //初始化
    components.push(new BaseComponent());
    //创建 application 的一个局部副本
    var app = new BaseComponent();
    //公共接口
    app.getComponentCount = function () {
      return components.length;
    };
    app.registerComponent = function (component) {
      if (typeof component == "object") {
        components.push(component);
      }
    };
    //返回这个副本
    return app;
  }();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354