zhaobinglong / myBlog

https://zhaobinglong.github.io/myBlog/
MIT License
7 stars 0 forks source link

js概念之函数 #28

Open zhaobinglong opened 4 years ago

zhaobinglong commented 4 years ago

一句话

函数是JavaScript这门语言中的一等公民

构造函数和普通函数的区别

• 构造函数首字母习惯大写 • 构造函数需要new才能使用,普通函数不需要

参考

https://zhuanlan.zhihu.com/p/78513598

zhaobinglong commented 4 years ago

纯函数

纯函数,这里有一个非常严格的定义:

特点

实例

function add(a,b) {
  return a+b
}
zhaobinglong commented 4 years ago

高阶函数(High-Order Function)

当我们讨论高阶函数时,通常包括以下几点:

// 自己实现一个map的高阶函数
function myMap(arr, fn) {
    let newArr = [];
    for(let i in arr) {
        newArr.push(
            fn(arr[i])
        )
    }
    return newArr;
}

// 用myMap函数实现一下上面的需求
myMap(persons, function(item){return item.age}); // [10, 18, 49]

// 试着把写进Array.prototype原型上
Array.prototype.myMap = function(fn){
    let array = this;
    let len = array.length;
    let newArr = [];
    for(let i=0; i<len; i++){
        newArr.push(fn(array[i], i, array))
    }
    return newArr;
}
zhaobinglong commented 4 years ago

函数柯里化Currying

柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

一句话

将一个带有多个参数的函数转换为一次只传递部分参数的过程。每次调用函数时,它只接受一个或者多个参数,并返回一个函数,直到传递所有参数为止。

优点:

不定长参数柯里化

// 实现一个工具函数,将普通函数柯里化
function curry(fn) {
  // 保存预置参数
  const presetArgs = [].slice.call(arguments, 1)
  // 返回一个新函数
  function curried () {
    // 新函数调用时会继续传参
    const restArgs = [].slice.call(arguments)
    const allArgs = [...presetArgs, ...restArgs]
    return curry.call(null, fn, ...allArgs)
  }
  // 重写toString
  curried.toString = function() {
    return fn.apply(null, presetArgs)
  }
  return curried;
}

function dynamicAdd() {
  return [...arguments].reduce((prev, curr) => {
    return prev + curr
  }, 0)
}
var add = curry(dynamicAdd);
add(1)(2)(3)(4) // 10
add(1, 2)(3, 4)(5, 6) // 21

作用1: 参数复用

// 正常正则验证字符串 reg.test(txt)

// 函数封装后
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false

作用2: 提前确认

//换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了
var on = function(isSupport, element, event, handler) {
    isSupport = isSupport || document.addEventListener;
    if (isSupport) {
        return element.addEventListener(event, handler, false);
    } else {
        return element.attachEvent('on' + event, handler);
    }
}

作用3: 延迟执行

// 像我们js中经常使用的bind,实现的机制就是Currying.
Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)

    return function() {
        return _this.apply(context, args)
    }
}

改写函数

// 实现一个add函数,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    let sum = 0;
    sum = Array.prototype.slice.call(arguments).reduce((a,b) => {return a+b;},sum);
    var curried = function () {
        if (arguments.length == 0) {
            return sum;
        }else{
            sum = Array.prototype.slice.call(arguments).reduce((a,b) => {return a+b;},sum);
            return curried;
        }
    };
    curried.toString = function () {
        return sum;
    }
    return curried;
}

console.log(add(1)(2)(3)(4));//10
console.log(add(1,2)(3,4)(1));//10
console.log(add(1,2)(3,4));//10

参考

https://www.jianshu.com/p/2975c25e4d71 https://www.zhangxinxu.com/wordpress/2013/02/js-currying/ https://zhuanlan.zhihu.com/p/31271179 https://juejin.im/post/6864378349512065038#heading-27

zhaobinglong commented 4 years ago

ES6新增箭头函数

[].map(item => {
   // 
})

箭头函数使用注意

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

zhaobinglong commented 4 years ago

立即执行函数

zhaobinglong commented 4 years ago

高阶函数实现AOP

AOP(面向切面编程)的主要作用就是把一些和核心业务逻辑模块无关的功能抽取出来,然后再通过“动态织入”的方式掺到业务模块种。这些功能一般包括日志统计,安全控制,异常处理等。AOP是Java Spring架构的核心。下面我们就来探索一下再Javascript种如何实现AOP 在JavaScript种实现AOP,都是指把一个函数“动态织入”到另外一个函数中,具体实现的技术有很多,我们使用Function.prototype来做到这一点

/**
* 织入执行前函数
* @param {*} fn 
*/
Function.prototype.aopBefore = function(fn){
  // 第一步:保存原函数的引用
  const _this = this
  // 第四步:返回包括原函数和新函数的“代理”函数
  return function() {
    // 第二步:执行新函数,修正this
    fn.apply(this, arguments)
    // 第三步 执行原函数
    return _this.apply(this, arguments)
  }
}
/**
* 织入执行后函数
* @param {*} fn 
*/
Function.prototype.aopAfter = function (fn) {
  const _this = this
  return function () {
    let current = _this.apply(this,arguments)// 先保存原函数
    fn.apply(this, arguments) // 先执行新函数
    return current
  }
}
/**
* 使用函数
*/
let aopFunc = function() {
  console.log('aop')
}
// 注册切面
aopFunc = aopFunc.aopBefore(() => {
  console.log('aop before')
}).aopAfter(() => {
  console.log('aop after')
})
// 真正调用
aopFunc()
zhaobinglong commented 4 years ago

偏函数

zhaobinglong commented 4 years ago

惰性函数

有时候我们会写一些函数,这些函数是处理业务逻辑的,函数中有很多判断,你会发现函数每次被调用,这些判断都要被执行一次,有没有可能函数只有在第一次调用的时候会执行一次判断,后续就不再执行这个判断呢?实现这种方案的函数就是惰性函数

非惰性函数实例

// 给执行元素点击事件,每次执行addEvent,if都会被执行一次
function addEvent(element, type, handler) {
  if (window.addEventListener) {
    element.addEventListener(type, handler, false);
  } else if (window.attachEvent){
    element.attachEvent('on' + type, handler);
  } else {
    element['on' + type] = handler;
  }
}

惰性函数实例

// if只会被执行一次
function addEvent(element, type, handler) {
  if (window.addEventListener) {
    addEvent = function(element, type, handler) {
      element.addEventListener(type, handler, false);
    }
  } else if (window.attachEvent){
    addEvent = function(element, type, handler) {
      element.attachEvent('on' + type, handler);
    }
  } else {
    addEvent = function(element, type, handler) {
      element['on' + type] = handler;
    }
  }
  // 有点闭包的味道了
  addEvent(element, type, handler);
}