在JavaScript里,函数被称为一等公民。Javascript函数涉及到的内容有很多,其中包含函数声明、函数调用、赋值、传参、构造函数、立即执行等。
JS编程特点
函数式编程和面向对象编程的混编语言,可扩展性强
优点:编程灵活、易学
缺点:不可控
采用三种不同的方法来实现计算的函数
1、普通函数
function add(a, b) {
return a + b
}
function minus(a, b) {
return a - b
}
fucntion compute(a, b, fn) {
return fn(a, b)
}
compute(1, 2, add)
2、构造函数
function Compute(a, b) {
this.a = a
this.b = b
this.add = function() {
return this.a + this.b
}
this.minus = function() {
return this.a - this.b
}
}
var compute = new Compute(1, 2)
compute.add()
3、立即执行函数
var test = (
function () {
function Compute(a, b) {
this.a = a
this.b = b
this.add = function() {
return this.a + this.b
}
this.minus = function() {
return this.a - this.b
}
}
return new Compute(1, 2)
}
)()
test.add()
函数式编程
函数式第一类对象,不依赖任何其他对象独立存在
纯函数
相同的输入得到相同的输出,不依赖且不影响外部环境也不产生任何副作用;输出完全取决于输入。 test函数就是纯函数,行参取决于全局变量a。
var a = 1
function test(num) {
console.log(num)
}
test(a)
凡事产生了副作用的函数就不是纯函数。只要和函数外部环境发生了交互就是副作用。 常见的副作用主要包括:发送数据请求、改变数据、console.log、 DOM操作、数据存储等等。
函数组合
若干个纯函数、偏函数、柯里化函数组合成一个新的函数、形成数据传递、并实现一种有序的执行效果。
function toUpperCase(str) {
return str.toUpperCase()
}
function exclaim(str) {
return str + '!'
}
function compose(f, g) {
return function(x) {
return f(g(x))
}
}
var fn = compose(exclaim, toUpperCase)
fn('hello')
exclaim(toUpperCase('hello'))
组合函数的参数不定,采用循环的方式来封装compose,遍历的项由参数的最后一项自右向左,每次执行的结果作为下一次调用的参数。
function compose() {
var args = [].slice.call(arguments)
len = args.length - 1
return function(x) {
var res = args[len](x)
while(len--) {
res = args[len](res)
}
return res
}
}
可以采用数组的reduceRight方法,将上面的compose进行一步改造。
function compose() {
var args = [].slice.call(arguments)
return function(x) {
return args.reduceRight(function(res, cb) {
return cb(res)
}, x)
}
}
高阶函数
一个函数接受另一个函数作为参数变量
function test(a, b, pow) {
return pow(a, 2) + pow(b, 2)
}
function pow(x, n) {
return Math.pow(x, n)
}
var res = test(2, 3, pow)
柯里化
函数式编程的思想,将一个多参数的函数转成多个单参数的函数,将n元函数转换成n个一元函数。主要作用:1、简化代码;2、提高维护性;3、功能单一化
function add(a, b, c, d) {
return a + b + c + d
}
function curry(fn) {
var _arg = [].slice.call(arguments, 1)
return function() {
var newArgs = _arg.concat([].slice.call(arguments))
return fn.apply(this. newArgs)
}
}
var add2 = curry(add, 1, 2)
var res = add2(3, 4)
简版的柯里化大体封装,但是有个弊端,参数的个数只能在有限次数凑齐,如果在调用add2时传入的参数只有一个将会出错。
将柯里化进一步优化封装,当调用时传入的参数小于fn的形参时,递归调用柯里化函数本身。
function curry(fn, len) {
var len = len || fn.length
var func = function(fn) {
var _arg = [].slice.call(arguments, 1)
return function() {
var newArgs = _arg.concat([].slice.call(arguments))
return fn.apply(this, newArgs)
}
}
return function() {
var argLen = arguments.length
if (argLen < len) {
var formatedArr = [fn].concat([].slice.call(arguments))
return curry(func.apply(this, formatedArr), len - argLen)
} else {
return func.apply(this, arguments)
}
}
}
偏函数
英文:partial Application,将n元函数转换成n-x元的函数
Function.prototype.partial = function() {
var _self = this
_args = [].slice.call(arguments)
return function() {
var newArgs = _args.concat([].slice.call(arguments))
return _self.apply(null, newArgs)
}
}
函数记忆
var time = 0
var cache = []
function factorial(n) {
time++
if (cache[n]) {
return cache[n]
}
if (n === 0 || n === 1) {
cache[0] = 1
cache[1] = 1
return 1
}
return cache[n] = n * factorial(n - 1)
}
function memorize(fn) {
var cache = {}
return fucntion() {
var key = [].join.call(arguments, ',')
return cache[key] = cache[key] || fn.apply(this, arguments)
}
}
var f = memorize(factorial)
防抖
1.是否首次延迟执行
2.n秒之内 触发事件不执行事件处理函数(n秒之内频繁触发事件,计时器会频繁重新开始计时)
function debounce(fn, time, triggleNow) {
var t = null
var debounced = function() {
var _self = this
args = arguments
if (t) {
clearTimeout(t)
}
if (triggerNow) {
var exec = !t
t = setTimeout(function() {
t = null
}, 1000)
if (exec) {
fn.apply(_self, args)
}
} else {
t = setTimeout(function() {
fn.apply(_self, args)
}, time)
}
}
debounced.remove = function() {
clearTimeout(t)
t = null
}
return debounced
}
节流
事件被触发,n秒之内只执行一次事件处理函数
function throttle(fn, delay) {
var t = null
begin = new Date().getTime()
return function() {
var self = this
args = arguments
cur = new Date().getTime()
clearTimeout(t)
if (cur - begin >= delay) {
fn.apply(this, _args)
begin = cur
} else {
t = setTimeout(function() {
fn.apply(_self, args)
}, delay)
}
}
}
数组扁平
多维数组变为一维数组
function flatten(arr) {
var _arr = arr || []
fArr = []
len = _arr.length
item
for(var i = 0; i < len; i++) {
item = _arr[i]
if (_isArr(item)) {
fArr = fArr.concat(flatten(item))
} else {
fArr.push(item)
}
}
function _isArr(obj) {
return {}.toString.call(obj) === '[object Array]'
}
return fArr
}