前言
JS的函数学习笔记
定义函数
通过函数声明定义函数
无返回值
1 2 3
| function 函数名(形参列表) { ... }
|
有返回值
1 2 3 4
| function 函数名(形参列表) { ... return 返回值; }
|
通过函数表达式定义函数
- 通过函数表达式定义的函数,在调用时,使用变量名当作函数名进行调用
1 2 3
| var 变量名 = function (形参列表) { ... }
|
箭头函数(ES6)
- 箭头函数是函数表达式定义函数的语法糖
- 箭头函数不绑定this,箭头函数体中调用this会向上层作用于查找
- 箭头函数不绑定arguments,箭头函数体中调用arguments会向上层作用于查找
1 2 3
| var 变量名 = (形参列表) => { ... }
|
通过构造函数创建函数
1
| var 变量名 = new Function("形参1", "形参2", "return 返回值");
|
调用函数
- 函数的实参可以是任意的数据类型,调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则对参数进行类型的检查
- 调用函数时解析器不会检查实参的数量,多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined
无返回值
有返回值
构造函数
1 2 3 4
| var 变量名 = function (属性1, 属性2) { this.属性1 = 属性1; this.属性2 = 属性2; }
|
通过类语法定义构造函数
传送门
1 2 3 4 5 6
| class 函数名 { constructor(属性1, 属性2) { this.属性1 = 属性1; this.属性2 = 属性2; } }
|
函数声明定义函数与函数表达式定义函数的区别
- 函数声明定义函数时,可以将调用函数的代码写在函数定义之前
- 函数表达式定义函数时,必须将调用函数的代码写在函数定义之后
1 2 3
| function 函数名() {}
函数名();
|
1 2 3
| 函数名();
function 函数名() {}
|
1 2 3
| var 变量名 = function () {}
变量名();
|
头等函数
- 头等函数(first-class function)又称为第一级函数,是指在JS中函数被当作头等公民
- JS支持使用头等公民的方式编程,这种编程方式被称为函数式编程
头等函数的特点
函数可以赋值给变量
1 2 3
| var 变量名 = function () {}
变量名();
|
函数可以在变量之间来回传递
1 2 3 4 5
| var 变量名 = function () {}
var 变量名2 = 变量名;
变量名2();
|
函数可以作为函数的参数进行传递
1 2 3 4 5
| var 函数名 = function () {}
function 函数名2(参数) {}
函数名2(函数名);
|
函数可以作为函数的返回值
1 2 3 4 5 6 7
| function 函数名(参数) { return function () {} }
var 变量名 = 函数名();
变量名();
|
函数可以作为对象的属性值
1 2 3 4 5
| var 对象名 = { 属性名: function () {} }
对象名.属性名();
|
匿名函数
- 如果一个函数没有定义函数名,则这个函数就是匿名函数
使用括号包裹匿名函数
1 2 3
| (function (形参列表) { ... })(实参列表);
|
1 2 3
| (function (形参列表) { ... }(实参列表));
|
使用运算符作为前缀
1 2 3
| +function (形参列表) { ... }(实参列表);
|
1 2 3
| -function (形参列表) { ... }(实参列表);
|
1 2 3
| !function (形参列表) { ... }(实参列表);
|
回调函数
- 将函数作为参数传递给其他函数,如果这个函数在其他函数中被执行,这个函数就被称为回调函数
- 传递回调函数时通常传递匿名函数
1 2 3 4 5 6 7
| function 函数名(函数形参) { 函数形参(); }
函数名(function () { });
|
闭包
- 闭包(Closure):在支持头等函数的语言中,实现词法绑定的一种技术
- 闭包在实现上是一个结构体,它存储了一个函数和一个关联环境
- 闭包跟函数最大的区别在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能照常运行
1 2 3 4 5 6 7
| function 函数名() { return function () { } }
var 变量名 = 函数名();
|
小闭包
1 2 3
| (function (形参) { })(实参);
|
高阶函数
- 如果一个函数满足以下两个条件的任意条件,则该函数就是一个高阶函数
- 如果一个函数的实参传递的是一个函数(这个实参就是回调函数)
- 如果一个函数的返回值是一个函数(这个返回值就是闭包)
递归函数
1 2 3
| function 函数名() { return 函数名(); }
|
- 如果没有定义跳出条件,则会产生无限调用,报错:
Maximum call stack size exceeded
函数的参数
剩余参数(ES6)
- 剩余参数(Rest Parameter):将不定数量的实参放到一个数组作为形参
1 2 3
| function 函数名(...形参) {}
函数名(实参1, 实参2);
|
- 如果函数有除了剩余参数以外的形参,剩余参数必须放在末尾
1 2 3
| function 函数名(形参1, ...形参2) {}
函数名(实参1, 实参2, 实参3);
|
展开运算符(ES6)
1 2 3 4 5 6
| function fn(形参1, 形参2) { ... }
var 实参列表 = [实参1, 实参2]; fn(...实参列表);
|
参数默认值(ES6)
1
| function 函数名(形参 = 默认值) {}
|
函数对象
属性
函数名
形参总数
- 对于剩余参数,
length不会计算
- 对于传递了默认值的形参,
length不会计算
this指向
this绑定的分类
默认绑定
- 函数没有绑定到对象上,则默认绑定到
window对象上
隐式绑定
显式绑定
- 通过
bind()、call()、apply()函数改变this指向
new绑定
- 通过
new关键字创建对象时,第三个内置操作会进行this绑定
this绑定的优先级
- 优先级从高到底
- new绑定
- 显示绑定通过
bind()绑定
- 显示绑定通过
apply()、call()绑定
- 隐式绑定
- 默认绑定
bind函数
改变函数的this指向
bind()不会立即执行函数,只会改变函数的this指向
- 返回的是原函数的拷贝
1 2 3 4
| function 函数名() {} let 对象名 = {};
let 新函数 = 函数名.bind(对象名);
|
- 使用
bind()函数传递的实参将作为新函数参数的默认值
1 2 3 4
| function 函数名(形参) {} let 对象名 = {};
let 新函数 = 函数名.bind(对象名, 实参);
|
call函数
调用函数
1 2 3 4 5
| function 函数名() { ... }
函数名.call();
|
改变函数的this指向
call()会立即执行函数,并改变函数的this指向
1 2 3 4
| function 函数名() {} let 对象名 = {};
函数名.call(对象名);
|
1 2 3 4
| function 函数名(形参) {} let 对象名 = {};
函数名.call(对象名, 实参);
|
apply函数
apply()可以调用需要传递参数的函数,向调用的函数传递参数时只能是数组类型数据
调用函数
1 2 3
| function 函数名() {}
函数名.apply(null);
|
1 2 3
| function 函数名(形参) {}
函数名.apply(null, [实参]);
|
改变函数的this指向
apply()会立即执行函数,并改变函数的this指向
- 如果将
null、undefined作为第一个参数,则函数的this指向为window对象
1 2 3 4
| function 函数名() {} let 对象名 = {};
函数名.apply(对象名);
|
1 2 3 4
| function 函数名(形参) {} let 对象名 = {};
函数名.apply(对象名, [实参]);
|
特殊的this指向
- 将对象2的函数赋值给对象1并直接调用,则函数的this指向为
window对象
1 2 3 4 5 6
| var 对象名1 = {}; var 对象名2 = { 函数名: function () {} };
(对象名1.函数名 = 对象名2.函数名)();
|
传递this的指向
通过上层变量传递(ES5)
1 2 3 4 5 6
| function 函数名() { var _this = this; function 函数名() { console.log(_this); } }
|
通过箭头函数传递(ES6)
- 由于箭头函数不会绑定this指向,所以可以用作传递this的指向
1 2 3 4 5
| function 函数名() { var 函数名 = () => { console.log(this); } }
|
arguments对象
- 在函数中,可以通过可迭代对象
arguments,获取实参列表中的任意参数,不论形参定义的参数个数如何,都能正确拿到所有实参
通过索引访问运算符获取指定实参
遍历实参列表
1 2 3 4 5
| function 函数名() { for (var i = 0; i < arguments.length; i++) { console.log(arguments[i]); } }
|
1 2 3 4 5
| function 函数名() { for (var arg of arguments) { console.log(arg); } }
|
arguments转数组
可迭代对象转数组(ES6)
1
| var arr = Array.from(arguments);
|
展开运算符(ES6)
1
| var arr = [...arguments];
|
切片
1
| var arr = [].slice.apply(arguments);
|
1
| var arr = Array.prototype.slice.apply(arguments);
|
纯函数(Pure)
柯里化函数
1 2 3 4 5 6 7 8 9
| function fn(x) { return function (y) { return function (z) { return x + y + z; }; } }
fn(1)(2);
|
简写
1 2 3
| var fn = x => y => z => x + y + z;
fn(1)(2)(3);
|
with扩展代码快的作用域链
1 2 3 4 5 6 7 8 9
| var obj = { key: value, fn: function () {}, }
with(obj) { console.log(key); fn(); }
|
eval将字符串作为函数执行
- 最后一个语句的返回值会作为
eval()函数的返回值
1
| var result = eval('function fn() {return 1}; fn();');
|
完成
参考文献
哔哩哔哩——Python小清风
哔哩哔哩——黑马前端