call & apply & bind 与柯理化

call & apply & bind

前言

原型&原型链,函数的3种角色:

普通函数:闭包和作用域

构造函数:类。原型和原型链,类和对象有关

对象

每个函数都是Function这个类的实例,那每个函数都可以调用Function.prototype原型上的方法

// 一道闭包的开胃题
function fun(n,o){
    console.log(o)
    return {
        fun:function(m){
            return fun(m,n)
        }
    }
}
var c=fun(0).fun(1);
c.fun(2);
c.fun(3);

this问题

  1. 给元素绑定事件, 那么事件回调中的this就是dom元素本身

  2. 普通函数执行,前面有点的,点前面是谁执行主体就是谁,this就是那个执行体,没有则是window(严格模式下是undefined)。自执行函数中的this默认是window

  3. 构造函数中的this就是创建的那个实例

原型上常用的方法:call apply bind

每个函数都可以调用这3个方法,这3个方法都是用来改变this指向的

我们从 实例.方法这个角度去看call

fn.call(); 这里执行的是call方法(此时你还不知道call是干什么用的,哈哈)

这部分的理解很重要

call

第一个参数为this的值,不传为window(严格模式下为undefined)

window.name='WINDOW'
let obj={
    name:'OBJ',
}
function fn(){
    console.log(this.name);
}
fn();
fn.call(obj);
obj.fn=fn;
obj.fn.call();

语法:

call函数执行做了以下几件事:

  1. 让函数中的this变成context,把后面的每一项参数未来传给函数

  2. 会让这个函数执行

ac3fd87b0e622346497322a2a11afe4e.pngthis的第4种情况:基于call改变

Object.prototype.toString()
Object.prototype.toString.call(100)

原理实现

不固定个数的实参,怎么在函数中接收?arguments。内置实参集合(类数组)。

526b4d29b2a3e02dfe0a79776f945a44.png

function call(context,...arg){
    let result=null;
    console.log(arg);
    // 不传默认为window
    context = context || window;
    context.$fn=this;
    return context.$fn(...arg);
    delete context.$fn;
    return result;
}
Function.prototype.call=call;
let obj={
    name:'OBJ',
}
function sum(){
    console.log(this);
}
sum.call(obj,10,20,30);

这里的实现是有缺陷的:

但是通过这个锻炼出来的能力:

怎么重编写一个方法,能够基于面向对象、基于原型链自己扩展方法,怎么来实现自己的功能

充分理解call的语法

阿里面试题

function fn1(){ console.log(1) }
function fn2(){ console.log(2) }
let obj={}
fn1.call(fn2);
fn1.call.call(fn2);
// fn2.fn1();
Function.prototype.call(fn1);
// fn1.call(obj);
Function.prototype.call.call(fn1);

解题思路:

首先第一题 fn1.call(fn2);

fn1.call(fn2);

  1. 首先call执行

  2. 按源码,context是fn2,this是fn1,相当于给fn2加上$fn属性,值为fn1

  3. 然后再执行fn2.$fn,即fn1

从代码和语义都是fn1执行

第二题 fn1.call.call(fn2);

fn1.call.call(fn2);

永远都从最后一个 .call 开始算

fn1.call.call(fn2); 前面画一条中划线,上面写个call

  1. 执行的是最后一个call方法

  2. xxx

9ddba2c2c666fb6962cab48733c07eb1.png

第三题 Function.prototype.call(fn1);

Function.prototype.call(fn1);

Function.prototype是一个匿名空函数

总结:

1个call,让点号左边的执行;

n个call,让括号里面的执行;

js面试题:fn.call.call.call.call(fn2) 解析_嘿嘿-CSDN博客

apply & bind

apply

let obj={
    name:'OBJ'
}
function fn(n,m){
    console.log(this.name);
    console.log(n+m);
}
fn.call(obj,10,20);
fn.apply(obj,[10,20]);

bind

  1. 先执行一个匿名函数

  2. 在执行匿名函数的时候去执行fn,相当于点击的时候执行了2个函数,先执行匿名函数;再执行fn。而执行fn的时候再去改变this

用bind

把fn中的this预先改变为obj,但是fn并没有执行

这种“预先做啥事情”的思想叫做“柯理化函数”

6957b4b696d7e36a22bdff16e1c9db7f.png

let obj={
    name:'OBJ'
}
function fn(n,m){
    console.log(this.name);
}
window.document.body.onclick=fn.bind(obj);

获取数组中的最大值和最小值

  1. 先排序

  1. Math.max/min

传的时候是数组,但是相当于一个个传 => apply

  1. 假设法

let arr=[23,45,12,4,5,6,98,109];
// arr.sort((a,b)=> a-b);
let max=arr[0];
for(let i=1;i<arr.length;i++){
    if(arr[i]>max) max=arr[i];
}
console.log(max);

*柯理化

预先把fn中的this处理成obj,

  1. 用bind是完全可以搞定的。把预先给fn的参数已经有了,fn中的this也改为了obj

  1. 自己先绑一个匿名函数

bind原理:

  1. myBind方法执行形成一个闭包,把实参信息分别用私有变量存储起来,返回的函数被以外的占用了;

  2. 返回的函数真正执行的时候,很多变量都是往上级作用域闭包里面找;

就相当于,myBind执行形成闭包,把一些信息预先存储起来,当以后返回的小函数用到这些信息的时候,直接从闭包里面拿来用。

总结柯理化:

形成闭包存点东西,以后能够供里面的小函数使用,供里面的子集作用域使用

let obj={
    name:'OBJ'
}
function fn(...arg){
    console.log(this);
    console.log(arg);
}
function clickFn(...arg){
    fn.call(obj,100,200,...arg);

}
// document.body.onclick=clickFn;
// document.body.onclick=fn.bind(obj,100,200);
Function.prototype.myBind=function(context,...arg){
    let _this=this;
    return function(...arg1){
        _this.call(context,...arg,...arg1)
    }
}
document.body.onclick=fn.myBind(obj,100,200);

题目

面试题

柯里化的递归嵌套,闭包套闭包

fn.bind(null, ...arg) 用bind把之前的实参预先存起来

解题代码:

function add(n1,n2,n3){
    return n1+n2+n3;
}
function currying(fn,length){
    length = length || fn.length;
    return function(...args){
        if(args.length>=length){
            return fn(...args)
        }
        return currying(fn.bind(null,...args),length-args.length);
    }
}
add = currying(add,3);

console.log(add(1,2,3));
console.log(add(1)(2)(3));
console.log(add(1,2)(3));

function fn(a1,a2,a3){
    return a1+a2+a3;
}
let f1=fn.bind(null,1)
let f2=f1.bind(null,2)
console.log('f2')
// 通过bind把之前的实参都保存起来
console.log(f2(3));
console.log(f1(2));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值