js—— call、bind 和 apply 、 真数组和伪数组互相转换及函数柯里化

apply 和 call 方法的作用:

这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值

格式:

call(对象,参数1,参数2,...)
apply(对象,[数组]) 注:数组也可以是arguments

  • 实例一
function sum(num1, num2){
    return num1 + num2;
}

function callSum1(num1, num2){
    // 传入arguments对象
    return sum.apply(this, arguments);
}

function callSum2(num1, num2){
    // 传入数组
    return sum.apply(this, [num1, num2]);
}

//20
alert(callSum1(10,10));
//40
alert(callSum2(20,20));
  • 实例二
function fun1(...args) {
    console.log(1)
    console.log(...args)
}

function fun2() {
    console.log(2)
    fun1.call(this, 2, 3)
    fun1.apply(this, [4, 5])
}

fun2()
/*
2
1
2 3
1
4 5
*/
  • 实例三
// 它的目的不在于添加传递参数,真正强大的地方是能够扩充函数依赖以运行的作用域
window.color = 'red'
let obj = { color: "blue" }

function sayColor() {
    console.log(this.color)
}

sayColor() // red
sayColor.call(this) // red
sayColor.call(window) // red
sayColor.call(obj) // blue

bind

bind 函数会创建一个新绑定函数(bound function,BF)。绑定函数是一个exotic function object(怪异函数对象,ECMAScript 2015中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。

bind 方法创建一个新的函数,在调用时设置 this 关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。

语法
function.bind(thisArg[, arg1[, arg2[, ...]]])

  • thisArg
    调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg。
  • arg1, arg2, …
    当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。
let writen = document.write

writen('hello')// error
writen.bind(document)('hello')// right

创建绑定函数

var module = {
  x: 42,
  getX: function() {
    return this.x;
  }
}

var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42

包装 Array.prototype.slice 方法

function fun() {
    console.log(Array.prototype.slice.call(arguments)) // (3) [1, 2, 3]

    let slice = arg => Array.prototype.slice.apply(arg)
    console.log(slice(arguments)) // (3) [1, 2, 3]

    // 利用 bind 并不先执行,可以包装 Array.prototype.slice 方法
    let unboundSlice = Array.prototype.slice;
    let slice1 = Function.prototype.apply.bind(unboundSlice);
    console.log(slice1(arguments)) // (3) [1, 2, 3]
}

fun(1, 2, 3)

更多实例参见:bind

// 手写 bind
Function.prototype.myBind = function (oThis, ...args) {
  if (typeof oThis !== 'object') return
  let [_self, func] = [this, function () {}]

  let fBind = function () {
    return _self.apply(this instanceof _self ? this : oThis, [...args, ...arguments])
  }

  if(_self.prototype){ // 维护原型关系
     func.prototype = _self.prototype
     fBind.prototype = new func()
  }

  return fBind
}

function add (...args) {
  console.log(this.join(' '), args.reduce((a, b) => a + b))
}
let obj = ['hello', 'world']
let fun = add.myBind(obj, 1, 2, 3, 4)
fun(5) // hello 15
  // vue 提供重写 bind
  function polyfillBind(fn, ctx) {
    function boundFn(a) {
      var l = arguments.length;
      /**
       * 如果参数不存在,直接绑定作用域调用该函数fn.call(ctx)
       * 如果存在且只有一个,那么调用fn.call(ctx, a), a是入参
       * 如果存在且不止一个,那么调用fn.apply(ctx, arguments)
       */
      return l
        ? l > 1
          ? fn.apply(ctx, arguments)
          : fn.call(ctx, a)
        : fn.call(ctx)
    }

    boundFn._length = fn.length;
    return boundFn
  }

  function nativeBind(fn, ctx) {
    return fn.bind(ctx)
  }

  var bind = Function.prototype.bind
    ? nativeBind
    : polyfillBind;

bind、apply 和 call 三者区别

apply 和 call 的区别就是传参形式不同:

  • apply 仅接收两个参数,目标对象和数组,数组就是该函数的传参
  • call 接收多个参数,第一个为目标对象,后面的数值都是函数的传参

bind 与前两者的区别:

  • bind 方法会返回执行上下文被改变的函数而不会立即执行,而前两者是直接执行该函数。他的参数形式和 call 相同。
function fun (...args) {
  console.log(args)
}

let obj = {}

fun.call(obj, 1, 2, 3)// (3) [1, 2, 3]
fun.apply(obj, [4, 5, 6])// (3) [4, 5, 6]
let save = fun.bind(obj, 7, 8, 9)// 在这里并不会立即执行 fun 函数
save()// (3) [7, 8, 9]

真数组和伪数组互相转换

真数组:var arr=[1,2,3,4];
伪数组:var obj={0:'onj',1:'hello',length:2}(注:最后的length不可少)

/*真数组转换伪数组方法*/
    	var ar=[1,3,4,5,6];
    	var ob={};
    	/*
    	1、通过[].push找到数组中的push方法
    	2、通过apply(ob)将找到的push方法内部的this修改为自定义的对象
    	3、将传入数组中的元素依次取出,传递给形参ob
     	*/
    	[].push.apply(ob,ar);//推荐
    	console.log(ob);/*{0: 1, 1: 3, 2: 4, 3: 5, 4: 6, length: 5}*/

/*伪数组转换真数组方法*/
        //系统自带伪数组
        var res=document.querySelectorAll('div');
        //自定义伪数组
        var obj={0:'lng',1:'af',length:2};

        //伪数组转真数组
        var arr=[].slice.call(obj);//推荐
        var arr1=[].slice.call(res);
        /*
         var arr=[];
         [].push.apply(arr,res);//也可以 但不兼容IE8及以下
         */
         
        console.log(arr1);//(3) [div, div, div]
        console.log(arr);//(2) ["lng", "af"]
        var arr3=[1,2,3,4];
        console.log(arr3);//(4) [1, 2, 3, 4]
  // vue 中伪数组转真数组
  function toArray(list, start) {
    start = start || 0;
    var i = list.length - start;
    var ret = new Array(i);
    while (i--) {
      ret[i] = list[i + start];
    }
    return ret
  }

函数柯里化

function curry(fn) {
    let args = Array.prototype.slice.call(arguments, 1)
    return function () {
        let innerArgs = Array.prototype.slice.call(arguments)
        let finalArgs = args.concat(innerArgs)
        return fn.apply(null, finalArgs)
    }
}

function add(num1, num2) {
    return num1 + num2
}

console.log(curry(add, 5)(4)); // 9
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值