以前几节中围绕着函数梳理了 this、原型链、做用域链、闭包等内容,这一节梳理一下函数自己的一些特色。javascript
javascript 中函数是一等公民。 而且函数也是对象,由于它们能够像任何其余对象同样具备属性和方法。它们与其余对象的区别在于函数能够被调用。简而言之,它们是 Function 对象。java
一个函数是能够经过外部代码调用的一个“子程序”,函数内部包含了执行语句或表达式。每一个自定义函数都是Function
的实例,并继承Function
的全部属性和方法,而Function
是语言自己提供的编程接口。编程
function foo (name) { console.log(`hello, ${name}`) } var res = foo(`小明`) // 'hello, 小明' console.log(res)
调用函数时,传递给函数的值被称为函数的实参(值传递),对应位置的函数参数名叫做形参。若是实参是一个包含原始值(数字,字符串,布尔值)的变量,则就算函数在内部改变了对应形参的值,返回后,该实参变量的值也不会改变。若是实参是一个对象引用,则对应形参会和该实参指向同一个对象。假如函数在内部改变了对应形参的值,返回后,实参指向的对象的值也会改.(欢迎查看参数传值一节)设计模式
若是函数内部没有经过return
返回一个值,则函数会默认返回undefined
.数组
函数在实际开发中承担着代码分块、 功能封装等任务。浏览器
// 函数申明 foo() let a = 1 function foo () { console.log(a) } // 函数表达式 (函数变量) let too = function (){ console.log('hello') } too() // Function 构造函数实例化一个函数 let bar = new Function('console.log("hello")') bar() // 箭头函数 let fns = () => { console.log('hello') } fns()
须要注意的是函数申明
和函数表达式
在变量提高时的区别,强烈建议阅读变量对象
相关内容: 详情。同时函数申明的函数名称
不能被改变,而函数表达式赋值给的变量能够被从新赋值。
函数的各类定义方式就不深刻,这里须要注意的是经过使用构造函数(new Function()
)方式建立的函数,在每次调用的时候都会解析一次。因此不推荐使用 Function 构造函数建立函数, 由于它须要的函数体做为字符串可能会阻止一些 JS 引擎优化,也会引发浏览器资源回收等问题。闭包
arguments
同this
同样是函数提供给函数内部使用的一个属性(ES6中箭头函数没有)。经过arguments
咱们能够获取调用函数时传递进来的实参, 该属性是一个类数组对象,咱们能够像数组同样进行数值的读取。函数式编程
function foo () { let a = arguments[0] // 获取函数的第一个参数 let b = arguments[1] // 获取函数的第二个餐宿 // 遍历函数的全部参数 for (let arg in arguments) { console.log(`${arg}: ${arguments[arg]}`) } arguments[2] = 1122 } foo(1, 2, 3)
虽然arguments
是相似于数组,可是除了经过索引获取元素和length
属性外,不能使用push
、pop
等方法。若是确实要进行修改操做,能够将其转换为真正的数组:函数
let args = Array.prototype.slice.call(arguments) let args = [].slice.call(arguments) // ES2015 let args = Array.from(arguments)
把函数定义和函数执行结合到一块儿就是当即执行函数,也叫自执行函数。优化
在官方术语中叫作 IIFE( 当即调用函数表达式),是在定义时就会当即执行的函数。被称为自执行匿名函数
的设计模式,主要包括两部分:
()
里的一个匿名函数,这个匿名函数拥有独立的词法做用域。这不只避免了外界访问此 IIFE 中的变量,并且又不会污染全局做用域。借用这个特性咱们能够对局部功能进行封装,只暴露不多的方法给外部环境,这也是不少第三方库的封装方法之一。()
建立了一个当即执行函数表达式,JavaScript 引擎到此将直接执行函数。当函数变成当即执行的函数表达式
时,表达式中的变量不能从外部访问:
(function () { var a = 1 })() console.log(a) // a is not defined
将 IIFE 分配给一个变量,不是存储 IIFE 自己,而是存储 IIFE 执行后返回的结果:
let bar = (function (){ let a = 123 return a })() console.log(bar) // 123
别外自执行函数还有一些其余使用方式:
// 下面2个括弧()都会当即执行 (function(){ /* code */ }()); (function(){ /* code */ })(); // 因为括弧()和JS的&&,异或,逗号等操做符是在函数表达式和函数声明上消除歧义的 // 因此一旦解析器知道其中一个已是表达式了,其它的也都默认为表达式了 var i = function(){ /* code */ }(); true && function(){ /* code */ }(); 0, function(){ /* code */ }(); // 若是你不在乎返回值,或者不怕难以阅读 // 你甚至能够在function前面加一元操做符号 !function(){ /* code */ }(); ~function(){ /* code */ }(); -function(){ /* code */ }(); +function(){ /* code */ }(); // 上面这种使用一元表达式这种方式实际上是不太常见的 // 并且有时候确定在一些场景下存在一些弊端,由于一元表达式会有一个不为undefined的返回值 // 要想返回值为undefined,那么最保险的就是使用void关键字 void function(){/* code */}();
()
左边必须是一个函数表达式,而不是一个函数申明。自执行函数也会用在模块封装的场景下。
函数和函数声明时的词法做用域造成闭包。咱们可使用闭包来保存变量、封装不须要暴露的私有属性和方法。
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); foo(); // "local scope"
完全理解闭包的最好途径就是理解闭包的由来,闭包的产生和做用域链有密切关系。在做用域链一节已经梳理过闭包的原理。
未完成