6 函数
约 1971 字大约 7 分钟
2025-06-18
一个函数访问了此函数的父级及父级以上的作用域变量
简单来说,当一个嵌套的函数引用了其外部函数中的变量,并且该嵌套函数被返回或者以其他方式在外部使用时,就形成了闭包。闭包使得这些被引用的变量不会被垃圾回收机制清理掉,即使外部函数已经执行结束。
闭包可以在一个内层函数中访问到其外层函数的作用域,用于:
- 创建私有变量
- 延长变量的生命周期
优点:
- 减少全局变量的定义,减少全局污染
- 可以私有化内部变量,使其只能通过特定的方式(内层函数)来访问
- 延长变量的生命周期 缺点:
- 内存泄露
- 性能影响
闭包的基本特性
- 保持状态:闭包可以“记住”并访问创建它的那个函数的作用域。
- 封装数据:通过闭包,你可以隐藏某些数据,防止外部直接访问。
- 延长变量生命周期:由于闭包持有对外部变量的引用,这些变量的生命期会延长至与闭包相同。
闭包的应用场景
- 数据隐私:利用闭包可以实现私有变量和方法,防止外部直接访问或修改。
- 回调函数:在事件处理、异步编程(如 AJAX 请求)等场景下经常需要用到闭包来保存状态。
- 函数工厂:可以通过闭包创建具有特定配置的函数实例。
- 模块模式:结合立即执行函数表达式 (IIFE),可以用来模拟类的概念,实现模块化开发。
尽管闭包非常有用,但如果不谨慎使用也可能带来问题:
- 内存泄漏:由于闭包会持有对外部变量的引用,如果这些引用不再需要但仍被闭包持有,则可能导致内存无法释放。
- 性能影响:过多地使用闭包可能会导致不必要的性能开销。 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)
IIFE
立即执行函数
let a=2
(function IIFE(){
console.log(a)
})()
高阶函数
参数或返回值为函数的函数
常用的高阶函数
- map
- filter
- reduce
- bind
- setTimeout
- addEventListener
函数式编程(编程范式之一)
- 面向过程编程:一步一步的实现功能,开发的基础阶段
- 面向对象编程:现实世界中事物抽象成类和对象,采用封装、继承、多态来演示客观世界的联系
- 函数式编程:现实世界中事物和事物之间的联系抽象到程序世界(对运算过程进行抽象)
在函数式编程中把每一个函数看作是一个运算 高阶函数表示的是缺失与运算 在运算缺失的时候需要函数作为参数,对运算需要延续的时候使用函数作为返回值
缺失 以map为例:在对数组中的元素进行运算时有一个运算规则的缺失,需要传入一个运算规则(函数)根据不同的规则对数组中的元素进行不同的运算 延续 以bind为例:bind接收对象和参数,并将绑定好的函数返回,整个过程中并不调用需要执行的参数,在需要调用的地方调用bind返回的函数
函数柯里化
是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数, 并且返回接受余下的参数的新函数的技术。
应用了闭包和高阶函数的概念
优点:
- 参数复用(利用了闭包和高阶函数的特性)
- 延迟运行(例如js中的bind方法,实现的机制就是Curying)
创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上 其实在大部分应用中,主要的性能瓶颈是在操作DOM节点上, 这js的性能损耗基本是可以忽略不计的,所以cury是可以直接放心的使用。
示例: 返回url的函数
function getUrl(protocol,hostname,pathname){
reutrn `${protocol}://${hostname}/${pathname}`
}
console.log(getUrl('https','baidu.com','passport')) // https://baidu.com/passport
console.log(getUrl('https','tencent.com','404')) // https://tencent.com/404
可以看到有一些参数是可以复用的,本例中的为协议字段,可以将函数进一步封装,根据不同协议生成不同协议的url函数
部分柯里化
function getUrl(protocol){
return (hostname,pathname){
reutrn `${protocol}://${hostname}/${pathname}`
}
}
const url=getUrl('https')
console.log(url('baidu.com','passport')) // https://baidu.com/passport
console.log(url('tencent.com','404')) // https://tencent.com/404
完全柯里化
function getUrl(protocol){
return (hostname){
return(pathname){
reutrn `${protocol}://${hostname}/${pathname}`
}
}
}
const httpsUrl=getUrl('https')
const baiduUrl=httpsUrl('baidu.com')
console.log(badiduUrl('passport')) // https://baidu.com/passport
console.log(badiduUrl('404')) // https://baidu.com/404
实现一个能够将任何函数都转换成柯里化的函数的函数
function curry(fn,...args){
if(arg.length >= fn.length){
return fn(...args)
}
return(...rest)=>{
return curry(fn,...args,...rest)
}
}
call、apply、bind
方法/特征 | call | apply | bind |
---|---|---|---|
方法参数 | 多个 | 单个数组 | 多个 |
方法功能 | 函数调用改变this | 函数调用改变this | 函数调用改变this |
返回结果 | 直接执行 | 直接执行 | 返回待执行函数 |
底层实现 | 通过eval | 通过eval | 间接调用apply |
call
call
方法允许调用一个函数,并且显式地指定this
值以及以参数列表的形式传入其他参数。其语法如下:
thisArg
: 在函数体内使用的this
值。arg1, arg2, ...
: 传递给被调用函数的参数。
function.call(thisArg, arg1, arg2, ...)
例如:
const obj = { num: 42 };
function add(a, b) {
return this.num + a + b;
}
console.log(add.call(obj, 1, 2)); // 输出 45
apply
apply
与call
非常相似,主要区别在于传递参数的方式:apply
接受一个数组或类数组对象作为参数列表。其语法如下:
thisArg
: 在函数体内使用的this
值。[argsArray]
: 包含传递给函数的参数的数组或类数组对象。
function.apply(thisArg, [argsArray])
例如:
const obj = { num: 42 };
function add(a, b) {
return this.num + a + b;
}
console.log(add.apply(obj, [1, 2])); // 输出 45
bind
bind
方法不会立即调用函数,而是返回一个新的函数,这个新函数在被调用时会将指定的this
值绑定到它内部。此外,还可以预先设置一些默认参数。其语法如下:
thisArg
: 在新函数内使用的this
值。arg1, arg2, ...
: 可选参数,这些参数会被预设为新函数的默认参数。
const newFunction = function.bind(thisArg, arg1, arg2, ...)
例如:
const obj = { num: 42 };
function add(a, b) {
return this.num + a + b;
}
const boundAdd = add.bind(obj, 1);
console.log(boundAdd(2)); // 输出 45
箭头函数和function的区别
箭头函数与传统函数function
有以下几个主要区别:
this绑定
- 箭头函数没有自己的
this
,继承自外层作用域的this
。 - 传统函数的
this
根据调用方式动态绑定。 构造函数 - 箭头函数不能作为构造函数使用,不能使用
new
关键字。 - 传统函数可以作为构造函数,创建对象实例。 arguments对象
- 箭头函数没有自己的
arguments
对象,可以使用剩余参数...args
代替。 - 传统函数有
arguments
对象,包含所有传入的参数。 原型对象 - 箭头函数没有
prototype
属性。 - 传统函数有
prototype
属性,用于创建对象实例的原型。 call、apply、bind - 箭头函数的
this
无法通过call
、apply
、bind
方法改变。 - 传统函数的
this
可以通过这些方法修改。
贡献者
版权所有
版权归属:PinkDopeyBug