讲清楚之 javascript 函数

函数

以前几节中围绕着函数梳理了 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

argumentsthis同样是函数提供给函数内部使用的一个属性(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属性外,不能使用pushpop等方法。若是确实要进行修改操做,能够将其转换为真正的数组:函数

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"

完全理解闭包的最好途径就是理解闭包的由来,闭包的产生和做用域链有密切关系。在做用域链一节已经梳理过闭包的原理。

函数式编程

未完成

相关文章
相关标签/搜索