07 js02 函数

函数 封装代码实现某个功能,最初思路解决代码重复度高的问题,类似于变量(就是一个筐,往里面填充内容即可) 【编程思想:高内聚、低耦合】【聚焦点:参数和返回值,后续就是函数的嵌套,才延生出作用域、闭包等问题,然后寻求解决方案】

1、函数的基本认知:

(1).函数声明、调用:function test(){};  test: 函数名/函数引用; test(); 函数执行;[function/var 都是关键字; 函数名命名规则:小驼峰规则(并非是语法规则,而是开发规范)]【functiontheFirstName(){}  document.write(theFirstName);// 打印出函数体,绝对不是地址,区别于编译性语言,解释性语言是打印不出地址的】

(2).函数表达式:指代的便是匿名函数表达式;

(3).参数的使用:使函数功能更加丰富;[形参==> 函数内隐式var了多个变量,形参和实参都是变量,它们可以是变量的任何类型:原始值/引用值,取决于函数封装的代码要实现的功能]

参数个数问题、实参列表;[实参与形参传递时最好要相互对应,要不后续会遇到函数内部封装好的对象,event/error等的使用,避免出错]  

映射规则、特殊性;[arguments:每个函数内部系统都会隐式定义一个实参列表,实参都有地方存放,console.log(arguments)]

例子:

(4).return(函数结束条件及返回值,如果不写 -> 函数末尾隐式添加 return undefined;)

(5).作用域:全局变量/局部变量的访问规则:

嵌套函数:里面可访问外面,外面不可访问里面;两个相互独立的函数也不可互相访问;

补充:定义函数的方式:

[1].函数声明;[2].函数表达式(匿名函数表达式);[3].使用对象Function,类似Number/Boolean/String/Array/Object;(了解即可)

[4].函数在对象中的使用,称为方法;[5].匿名函数function(){},箭头函数() => { }; 这两种一般作为回调函数使用(作为其他函数的参数),或者当作立即执行函数使用;

箭头函数:() => {};  => 表示函数中的大括号,{ } 函数体;( )函数参数,当函数只有一个形参()可省略,当函数体只有一条语句时,{}可省略,若有return,return单词也可省略;最好都别省略;

2、递归:有很强抽象规律使用,阶乘、斐波那契数列是典型的两个案例;[递归可以使代码更加简捷,缺点:运行速度较慢,往往有很强抽象规律才使用]

 【1.找规律;2.找出口,防止进入死循环】

//使用过程中,只需要在函数体内写入:return 公式  就OK;(尽量做到灵活使用,但其确实不容易想到,后续有些地方会用到递归的思想,例如:算法)

3、预编译:解决代码执行顺序问题;【聚焦点:GO全局形成、AO函数执行前形成,聚焦在变量】

js运行三部曲: 语法(语义)分析、预编译、解释执行;

(1).语法分析:检查代码是否有低级错误,若出现低级语法错误,则不执行;[关于错误后还有更文]

(2).预编译:代码执行前一刻需要进行预编译,全局的预编译/函数执行前的预编译;[日常开发编写代码是视觉效果展示,计算机执行过程是遵循预编译的顺序执行,预编译生成的对象就是一个空间存储库]

(3).解释执行;

window全局对象的整体认知:【补充:undefined:声明了但没值,例如console.log(window.a);其也是对象中很特殊的一类现象,a is not defined; 压根就不存在该变量】

window就是全局

预编译:变量声明提升、函数声明整体提升都只是预编译中的现象而已;一般遇到代码执行问题,上述两条就可以解决,不容易解决时创建GO、AO对象分析;[GO对象就是window对象,表示全局]

[定义变量=变量声明+变量初始化(赋值)  var a; //变量声明    a = 10; //变量初始化   var a=10; //定义变量 ]

先形成GO对象,再形成AO对象;[代码包含无非就是:变量、函数、语句,GO对象是在全局时候形成的,AO对象是函数执行前一刻形成的,一定要善于区分,函数输出的变量是全局还是AO]

升华例子1:函数执行前形成的AO对象中肯定会有变量b的声明,即使函数中该变量在if条件判断语句中,预编译过程中形成的对象就是后续代码执行的依据,符合条件的提升就OK;

升华例子2:关于代码执行顺序可以get到一些点(例子1显然返回函数、例子2因为有赋值,所以返回的是值),总之灵活应对;

【补充:如上console.log(bar);//输出整个函数;bar:函数名/函数引用;console.log(bar());  - - - > 输出函数执行结果】

----------->>>>>>生成的GO/AO对象放到什么地方?函数中里面的可访问外面的,外面的却不可访问里面的这类现象的底层逻辑是什么?[之前查找变量是从GO/AO对象中查找,底层实际是从函数的作用域链中去查找]

4、作用域、作用域链精解:【聚焦点是:函数嵌套时变量的查找】

【window:全局的域;函数都有自己的独立作用域】

每个函数都有自己的作用域链;产生的执行期上下文的集合形成了作用域链中,访问变量的顺序也遵循该链;里面可访问外面,外面无法访问里面;

一个函数只有执行的时候,产生AO对象,系统才能看到里面的东西,否则定义阶段根本就看不到里面的东西;只有执行的时候才开始读取函数里面的内容;

(1).函数也是对象(函数类对象):对象都有属性,有些属性我们可以直接访问test.name/prototype;但有些属性是不可直接进行访问,仅供javaScript引擎读取,[[scope]]就是其中的一个,test[[scope]];

[[scope]]:每个函数都有该属性,称为作用域,其内部存储了运行期上下文的集合,这个集合呈链式链接,就是作用域链;

运行期上下文:也称为执行期上下文,当函数执行的前一刻,会创建执行期上下文的内部对象,也就是预编译时产生的GO/AO对象;[函数每次执行时都会创建新的执行期上下文(对象),所以多次调用一个函数会创建多个执行期上下文,当函数执行完毕后,所产生的执行期上下文被销毁,这里的销毁指代的是引用销毁,而非代码直接销毁]

查找变量:从作用域链的顶端依次向下查找;

(2).过程剖析:

[1].函数定义的过程中函数的作用域便已经存在,即[[scope]]已经存在;首先生成GO对象,这时候a函数被定义,定义的过程中作用域链中 0 -->> GO对象;[函数定义的阶段(GO对象生成的过程),系统根本不会关注函数体里面的内容,不会解析,生成AO对象才开始分析函数体里面的内容]

[2].GO对象形成后,开始执行代码----->>a();--->>>函数执行前一刻形成AO对象,此时的作用域链中0 --->> AO对象;  1 --->>GO对象;新形成的AO对象会在作用域链的顶端;[执行过程中查找变量肯定是在作用链中去查找,先找自己AO对象,再查找GO对象,即从作用链顶端以此向下查找]

[3].a函数的执行过程中,b函数开始定义(预编译过程);定义的时候b函数会产生自己的[[scope]],其的作用域链环境是a函数给的,也就是a函数执行时候的环境;

[4].b函数执行中会产生自己的AO对象,然后放置到自己作用域链的最顶端;

[5].b函数执行结束后,销毁自己的执行期上下文对象即AO对象(回归到被定义的状态,如果有二次执行,再形成新的AO对象,执行的过程中创建AO对象,执行结束后销毁相应的AO对象即可)所谓的销毁并非是直接把AO对象抹掉了,而是把线剪断;[b函数作用域链中拿到的a函数执行过程中的作用域链,它们指向是相同的,只不过是生成自己的AO对象而已]

[函数内查找变量的过程:在函数自己的作用域链中自顶向下找,里层函数可访问外层函数的变量,但外层函数绝对不可能访问到里层函数的变量,外层函数作用域链中自己的AO与外层的环境,自己的AO对象只有里层函数的定义,定义阶段系统又不解析函数体内部,所以压根找不到函数里面的变量]

[6].b函数执行结束后,代码中a函数也执行结束了,a函数需要销毁自己的执行期上下文对象,回归到被定义的状态,销毁AO对象的同时,b函数定义也被销毁了,也就是b函数彻底结束了,作用域链中没东西了;

[7].若是a函数再次执行,需要从新开始,创建AO对象等一系列操作同上;若是函数内嵌套了多个函数,最里面的函数作用链会很长,当最外面函数执行完成后,里面的函数连定义状态都没有了,作用链为空; ---->>>>> 但有特殊情况,函数执行完成后却把里面的函数return出去了,形成闭包;

5、闭包:其是一类现象;函数嵌套中外部函数结束执行时,内部函数由于被return到了外部,依旧存活,内部函数生命周期比外部函数还长;【内部函数被保存到外部就会形成闭包,但这个过程很容易被忽视】

过程分析:[1].a函数结束执行后,return b;b函数依旧属于被定义的状态,环境是a函数执行时的作用域链,这时候b函数依旧可以操作外面函数的变量(作用域链的原因);[若是只是普通的在a函数外部定义的函数,由于作用域的原因,其必然是不能访问a函数中的变量的,但闭包可以]

闭包:函数执行结束会释放作用域链占用的空间,但若是函数嵌套多层,形成闭包则会导致原有作用域链不释放,占用内存空间(也称为内存泄漏【现象一致:都是占用内存,剩余内存少了】);闭包有坏处需要防治,但也有很好的应用场景来实现一些功能;

闭包的作用:

(1).实现公有变量:函数累加器;[return demo;demo的作用域链是add函数执行时的环境,指向的就是同一套,demo();每次指向环境没变只是创建了新AO对象而已,所以每次操作都会改变原来的值]

(2).可以做缓存;(缓存就是存储结构)

(3).可以实现封装、属性私有化;

(4).模块化开发、防止污染全局变量;,





闭包的bug以及解决方案:[下式是经常会遇到的一种闭包情况,高频触发,经常会被忽视] 考虑过程中需要结合预编译、作用域链、闭包的知识,开始写GO/AO对象分析,后续尽量抽象化去分析;      myArr = [function (){console.log(i} ......] //里面10个相同的函数;myArr[j]();这时候才开始执行函数,i自身AO对象中没有,沿着作用域链找test中有,此时i为10;【里面函数与test() 形成10对1的闭包】

闭包的解决方案:立即执行函数(其也是唯一的解决方案);[每循环一次立即执行函数就执行,过后销毁,但销毁并非是把里面的代码给抹掉了,只是把引用抹掉了而已,循环了10次,有10个不同的立即执行函数] [立即执行函数虽然销毁,但最终把里面的函数弄了出来,其和立即执行函数形成了闭包,j自身没有就从立即执行函数中查找] 【里面函数与立即执行函数形成:1对1的闭包】

6.立即执行函数(也称为函数的自执行):该函数执行过后就销毁便不存在了;[js中提供立即执行函数来处理那些只执行一次的函数,有些函数内容巨长会占用内存][所谓的销毁并不是直接把代码抹掉了,而是把函数的引用抹掉]

[无论如何有的函数只执行一次(只被执行一次,我们想要其执行一次,或者执行一次返回结果的函数),我们称之为初始化函数或初始化功能,例如开发中的数据进行初始化等,初始化后该函数就没用了]

常规用法:[立即执行函数没有函数名,函数名没什么用处,写上倒也不会报错]

拓展(深挖底层原理):只有表达式才能被执行符号执行,执行后基本就是立即执行函数了; [function test(){}();//报错; test();不报错,test代表着函数体,为什么两者结果不同? 原因:test也是表达式,例如123是数字表达式];

var test = function(){ console.log('a');}();//表达式加上执行符号,执行过后就销毁console.log(test);//undefined;

回归到最初所提到的:(function (){} ()); (function (){})();这两种立即执行函数形式,可以理解为外面的括号将函数声明变为了表达式,然后被执行符号执行,变为立即执行函数;

升华例子:括号首先把函数声明变为了表达式,然后执行if条件判断;(开发中也没有人这么写)

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

推荐阅读更多精彩内容

  •   函数表达式是 JavaScript 中的一个既强大有容易令人困惑的特性。定义函数的的方式有两种: 函数声明; ...
    霜天晓阅读 810评论 0 1
  • 来源:仗剑走天涯! 关于javascript的作用域的一些总结,主要参考以上文章,加上自己的整理的理解。 近日对j...
    Michael_林阅读 919评论 0 1
  • 原文:http://dmitrysoshnikov.com/ecmascript/javascript-the-c...
    jaysoul阅读 472评论 0 0
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,096评论 0 21
  • 定义函数的方式有两种:函数声明和函数表达式。 函数声明的一个重要特征就是函数声明提升,意思是在执行代码前会先读取函...
    oWSQo阅读 660评论 0 0