函数本质是对象。每一个函数都是 Function 类型的实例。
javascript
定义函数有两种方式:函数声明、函数表达式。
java
使用 "function" 关键字 (会变量提高,但不建议这样作),代码以下:
编程
// 变量提高
console.log(fn); // [Function: fn]
// "function
function fn() {
// 函数内容...
}复制代码
使用 "var" 或 "let" 关键字 ("var"会致使声明提高,一样不建议这样作),代码以下:数组
// 声明提高
console.log(fn1); // undefined
var fn1 = function () {
// 处理代码...
}
// "let" 声明不会被提高
console.log(fn1); // 报错!!!
let fn2 = function () {
// 处理代码...
}复制代码
其实函数表达式是先定义了匿名函数(也叫拉姆达函数),再将匿名函数赋值给变量。
promise
function plus(num1, num2) {
if (!num1) {
return 0
}
if (num2) {
return num1 + num2
}
return num1
}
console.log(plus()); // 0
console.log(plus(11)); // 11
console.log(plus(2, 1)); // 3复制代码
Funtion()
和new Funtion()
建立函数let fn1 = new Function('return 1');
let fn2 = Function('return 2');
console.log(fn1(), fn2()); // 1 2复制代码
虽然以上代码无语法错误,但和eval()
函数同样,可能会被浏览器阻止。
浏览器
参数定义时,能够为其提供默认值。这部份内容和解构赋值有关,点击连接看另外一篇文章。下面是简单的例子:闭包
// 参数的默认值
function fn1(x, y = 2) {
console.log(x, y)
}
// 参数的解构赋值
function fn2({ x, y }) {
console.log(x, y)
}
// 参数声明时使用解构赋值
function fn3({x, y = 5} = {}) {
console.log(x, y);
}
// 使用展开运算符和解构赋值
function fn4(...value) {
console.log(value)
}
fn1(10); // 10 2
fn2({ y: 22, x: 11 }); // 11 22
fn3(); // undefined 5
fn4(1, 2, 3); // [1,2,3]
复制代码
length 属性能够获取没有指定默认值的参数个数。看下面例子:app
function fn1(x, y) { }
function fn2(x, y = 1) { }
console.log(fn1.length); // 2
console.log(fn2.length); // 1复制代码
箭头函数的最大特色就是"绑定" this 的指向,使其指向定义时所在的对象。基本语法以下:异步
// 箭头后的圆括号表示直接返回的值
let addOne = (x, y) => (value + 1)
// 能够像普通匿名函数同样,处理和返回值 (单个参数能够省略箭头左边的括号)
let addTen = value => {
value += 10;
return value
}
console.log(addOne(1, 2)); // 3
console.log(addTen(1)); // 11复制代码
使用注意点:async
new
运算符。函数的 name 属性,返回该函数的函数名。
function foo() {}
foo.name // "foo"
// 函数赋值,但name仍返回原来函数的名字
baz = foo;
baz.name // "foo"
// 绑定的函数会被加上"bound "
foo.bind({}).name // "bound foo"
// 匿名函数,返回空字符串
(function(){}).name // ""
复制代码
关于Promise
的介绍在另外一篇文章。
"async function" 的本质是Promise
对象,是Promise
的语法糖。因此,一样可使用then()
和catch()
。其内部能使用(也只能在其内部使用)的一个关键字await
,表示等待该行代码执行结束,而后再继续后面的代码。与Promise
不一样,"async function" 就是函数,只有被调用时才会执行,而非定义后立刻执行。下面给出简单例子:
// 2s 以后返回双倍的值
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
} )
}
// 定义async函数
async function testResult() {
console.log('---2s后打印resolve---');
let result = await doubleAfter2seconds(30);
setTimeout(() => {
console.log('再过1.5s后打印"await"后语句')
}, 1500)
console.log(`result:${result}`);
}
testResult(30);
/* 打印结果: ---2s后打印resolve--- result:60 再过1.5s后打印"await"后语句 */复制代码
为了加深理解,能够试试下面的一道题:
async function async1() {
console.log( 'async1 start' );
await async2();
console.log( 'async1 end' );
}
async function async2() {
console.log( 'async2' );
}
console.log( 'script start' );
setTimeout( function () {
console.log( 'setTimeout' );
}, 0);
async1();
new Promise(function (resolve) {
console.log( 'promise1' );
resolve();
}).then(function () {
console.log( 'promise2' );
});
console.log( 'script end' );复制代码
结果以下:
script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout复制代码
迭代器是一种特殊对象,它具备一些专门为迭代过程设计的专有接口,全部的迭代器对象都有一个next()
方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value
,表示下一个将要返回的值;另外一个是done
,它是一个布尔类型的值,当没有更多可返回数据时返回true
。迭代器还会保存一个内部指针,用来指向当前集合中值的位置,每调用一次next()
方法,都会返回下一个可用的值。若是在最后一个值返回后再调用next()
方法,那么返回的对象中属性done
的值为true
,属性value
若未定义,则为undefined
。
以为复杂的话,想一想数组对象,它就是内置迭代器,循环遍历时(如for
),就是调用迭代器,每一回的循环都.next().value
获取值,直到结束。
下面以Array
对象的values()
方法获取一个迭代器:
let arr = ['a', 4, { a: 1 }];
let myIterator = arr.values();
console.log(myIterator.next().value); // 'a'
console.log(myIterator.next().value); // 4
console.log(myIterator.next().value); // { a: 1 }
console.log(myIterator.next().done); // true复制代码
生成器是生成迭代器的函数,经过function *
或function*
定义,函数中的特有关键字yield
,表示当前迭代器返回的值,直到迭代器使用next()
,则跳到下一个yield
停顿,有点相似于return
。下面给出简单例子:
const arr = ['blue', 'green', 'red'];
function *creatIterator(arr) {
for (let i = 0; i < arr.length; i++) {
// 表示每次调用都返回arr[i]的值,与return相似
yield arr[i];
}
}
const item = creatIterator(arr);
console.log('第一次调用:' + item.next().value);
console.log('第二次调用:' + item.next().value);
console.log('第三次调用:' + item.next().value);
console.log('第四次调用:' + item.next().done);
/* 打印结果: 第一次调用:blue 第二次调用:green 第三次调用:red 第四次调用:true */复制代码
函数被定义时,则自动建立两个属性:arguments
和this
,且只能在函数内部使用。函数执行时会建立caller
属性,指向调用本函数的外部函数。注意,这三个属性都在严格模式下('use strict'
)没法使用。
arguments 是一个对应于传递给函数的参数的类数组对象。不是数组,但能够转化成数组。函数内部一定存在的一个对象。看下面一个例子:
function fn () {
console.log(arguments);
}
fn('a', '', 'c'); // { '0': 'a', '1': '', '2': 'c' }复制代码
能够看出 arguments 依次包含 3 个参数。
arguments 对象内部还有 3 个不可遍历的属性:
通常来讲,this 指向执行本函数的上下文,但能够经过 "apply" 、 "call" 和 "bind" 方法改变指向。更多详细描述请查看另外一篇博文。
当本函数执行时,caller
属性指向调用本函数的外部函数;当本函数执行结束后,caller
会被赋值为null
。看以下例子:
function foo() {
function fn() {
console.log('fn内部:',fn.caller);
}
function baz() {
let obj = {}
obj.myFn = fn;
let globalFn = fn;
obj.myFn();
globalFn();
}
baz();
console.dir('fn外部:',fn.caller);
}
foo();
// fn内部: function baz()
// fn内部: function baz()
// fn外部: null复制代码
递归的定义是函数内部调用函数自身。上面提到能够用 "arguments.callee" 获取函数自己,但严格模式下是不容许的,看如下例子:
"use strict";
function fn(num) {
console.log(num--)
if (num > 0) {
// 严格模式下会报错
// arguments.callee(num);
// 建议使用
fn(num);
}
}复制代码
当一门编程语言的函数能够被看成变量同样用时,则称这门语言拥有头等函数。例如,在这门语言中,函数能够被看成参数传递给其余函数,能够做为另外一个函数的返回值,还能够被赋值给一个变量。如下示例都证实 JavaScript 是拥有头等函数。
// 函数复制给变量
let foo = function () { }
// 函数返回函数
function baz() {
return function () { }
}
// 函数做为参数
function foz(foo) { }复制代码
高阶函数是一个接受其余函数做为参数或将函数做为返回值返回的函数。如下给出两种高阶函数的例子。
function fn() { }
// 接受函数做为参数的高阶函数
function hof1(fn) { }
// 以函数作为返回值的高阶函数
function hof2() {
return function () { }
}复制代码
一元函数是一个只接受一个参数的函数。与之对应的是多元函数 (multivariate functions)。
function uf(value) { }复制代码
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数且返回结果的新函数的技术。
// 未柯里化的函数
function sum(a, b, c, d) {
return a + b * c - d
}
// 柯里化后的函数
function curryingSum1(a) {
return function (b) {
return function (c) {
return function (d) {
return a + b * c - d
}
}
}
}
// 箭头函数版的柯里化函数
let curryingSum2 = a => b => c => d => a + b * c - d
// 使用柯里化函数
curryingSum2(1)(2)(3)(4); // 3复制代码
上面例子能够看得出,箭头函数比较适合写柯里化的函数。
函数反作用是指函数在正常工做任务以外对外部环境所施加的影响,函数不只仅只是返回了一个值,并且还作了其余的事情。以下例子:
let myObject = { a: 1 }
function fn() {
myObject.a = 11
return true
}复制代码
函数反作用的函数也称为非纯函数 (Impure Function)。
输入输出数据流全是显式(Explicit)的,意思是,函数与外界交换数据只有一个惟一渠道 —— 参数和返回值。函数从函数外部接受的全部输入信息都经过参数传递到该函数内部。函数输出到函数外部的全部信息都经过返回值传递到该函数外部。简单说就是没有反作用的函数。
function add1(a) {
return a + 1
}复制代码
闭包函数指的是有权访问另外一个函数做用域中的变量的函数。这部分是比较大的内容,能够点击连接查看详细。
本篇不做详述,另开篇幅整理此类容,请点击连接。