函数是js中既强大又容易令人困惑的特性,首先定义函数的方法有两种:函数声明和函数表达式
许多浏览器给函数定义了一个非标准的name属性,console.log(functionName.name) =》 “functionName”,匿名函数的name属性则是一个空的字符串
函数声明的重要特征就是函数声明提升,可以在声明函数之前调用
在函数内部使用函数声明定义函数,只能创建局部函数
递归
递归函数是一个函数通过名字调用自身的情况下构成的
arguments.callee是一个指向正在执行的函数的指针,在这里等同于factorial(num-1)
闭包
闭包就是指有权访问另一个函数内部变量的函数,通常是一个函数包含一个函数的形式,这句话大多数前端er都知道,但如何去理解闭包呢
首先要理解作用域和作用域链的问题,当某个函数被调用时,会创建一个执行环境以及相应的作用域链,然后,使用arguments和其他命名参数来初始化函数的活动对象,在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,以此类推,直至全局对象,也就是作用域链的终点:全局执行环境,举个🌰
后台的每个执行环境都有一个表示变量的对象——变量对象,全局环境的变量始终存在(window),而像createComparisonFunction()函数这样的局部环境的变量对象,只在函数执行时存在,在创建createComparisonFunction()函数时,会预先创建一个包含全局变量对象的作用域链,被保存在内部的[[Scope]]属性中,这个对象对应的是一个对象的列表,列表中的对象仅能javascript内部访问,没法通过语法访问。
当调用createComparisonFunction()函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象,构建起执行环境的作用域链。作用域链的本质是一个指向变量对象的指针列表,只引用但不实际包含的变量对象。
通常函数执行完成,就是销毁局部活动对象,但上面的🌰中的匿名函数的作用域链包含了外部函数的活动对象,所以即使外部函数执行完毕,其局部活动对象也不会销毁,直到匿名函数被销毁。
闭包与变量
由于作用域链的配置机制是一个引用,所以闭包只能获取包含函数中任何变量的最终值。🌰
由于这里没有参数传递,这里的i是作用域链中引用的对象,即使它的值是基本类型,所以这里输出10个10。
想要获得0-9这样的结果,可以将i作为实参传递给匿名函数的形参,中间是有一个复制的操作,或者使用ES6的let,详情参考这里
this对象
this对象是在运行时基于函数的执行环境进行绑定的,全局环境中指向window,当函数被作为某个对象调用时,指向调用的对象。this始终指向直接调用它的对象。
每个函数在被调用时,会自动取得两个特殊的变量this和arguments,内部函数搜索这两个变量时,只会在活动对象里搜索,并不会向上查找。可以通过变量赋值的方法实现访问。
内存泄漏
众所周知,js在函数执行完毕后会通过垃圾回收机制将函数内部的活动对象销毁,而闭包函数由于作用域链一直引用对象,所以不能销毁,这样就很容易造成内存泄漏问题,所以使用的时候也要记得手动销毁。
模仿块级作用域
js是没有块级作用域的概念的,所以在块语句中定义的变量,实际是包含在函数中的变量,而非语句中的变量。
私有变量
任何函数中定义的变量,都可以认为是私有变量,因为不能从外部访问到。
有权访问私有变量和私有函数的公有方法叫特权方法
利用私有和特权成员,可以隐藏那些不想被直接修改的数据。🌰
静态私有变量
通过在私有作用域中定义私有变量和函数,创建特权方法。🌰
这个🌰中的Person构造函数和setName()和getName()方法一样,都有权访问私有变量name,这种模式下,变量name就变成一个静态的,由所有实例共享的属性。
模块模式
模块模式是为单例(只有一个实例)创建私有变量和特权方法。🌰
这里的单例就是application,私有对象components数组,返回对象的getComponentCount()和registerComponent()方法都是有权访问components对象的特权方法。
增强的模块模式
在返回对象之前加入对其增强的代码,这种模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其增强情况。🌰
小结
本文仅是个人的看法,如有错误和补充,欢迎指正和交流。
参考红皮书的一些学习所得