call、apply、bind原理

一、箭头函数

var n = 100;
 let obj = {
     id:'11',
     name:'邱顺俊',
     n:0,
     say(){
         setTimeout(()=>{
             this.n++;
             console.log(this.n);
         }, 1000);
     }
 };
 obj.say(); //==>1

箭头函数没有自己的this,所用到的是当前所处上下文中的this
在Function.prototype上的三个方法:call/apply/bind 都是用来改变this指向的

二、 CALL/APPLY的用法

func.call(context, arg1, arg2, …)
func.apply(context, [argsArray])
CALL/APPLY
① 立即执行函数
② 改变函数中的this为:context参数
③ 给函数传递参数信息:
=> CALL 接收的是参数列表
=> APPLY 接收的是数组

这里,以call为例

function fn(x,y){
     console.log(this, x+y);
 }
 let obj = {
     id:'11',
     name:'邱顺俊',
 };
 fn.call(obj, 10, 20);  

输出的结果是:

fn.call(obj, 10, 20)
①这句代码的意思是:
fn实例基于__proto_ 找到Function.prototype原型上的call方法,然后把call方法执行
=> call方法中的this:fn (因为fn.call,call的this是fn)
=> context存储的是参数:obj

② call方法内部功能是:
=> 把fn函数立即执行,即:call方法中的this执行
=> 把fn中的this改变为第一个参数context:obj
=> 其余的参数依次传递给fn
即:把fn执行,并让fn中的this变为obj,并且把除了obj外的剩余参数,依次传递给fn

三、重写内置的CALL

原理:实现的思路:把需要执行的函数fn和需要改变的this指向obj关联起来-> obj.xxx=fn,
此时我们只需要obj.xxx(),就相当于把fn执行,而且函数中的this就是obj

实现代码:

 自己封装的call方法
 优化:
 1.临时添加的属性不能和原始对象中的属性冲突
 2.参数的处理,传入的context如果为空或者传入的为undefined或者null,以及传入的context不是引用数据类型的需要进行转化,因为基本数据类型无法添加属性
Function.prototype._call = function _call(context, ...parms) {
     // 下面执行_call方法时,点前面是fn,所以this是fn (17行)
context == null ? (context = window) : null; //如果为空或者什么都不传就把context赋值为window,否则就什么都不做
 /^(object|function)$/.test(typeof context) ? null: (context = Object(context)); //如果传入的context不是引用类型,转换为引用类型
 let xxx = Symbol(); //保证添加的属性的唯一性
 context[xxx] = this; //给对象添加一个属性,指向需要执行的函数,此刻的this是fn
 let result = context[xxx](...parms); //执行函数,传入剩余参数
 delete context[xxx]; //删除添加的属性,不改变原始数据结构
 return result; //把结果返回出去
       };
let res = fn._call(null, 30, 10);

四、bind的用法

fn.bind(context[, arg1[, arg2[, …]]])
bind:函数不会立即执行,预先处理函数中的this和参数,返回的是一个函数体

 function fn(x,y){
     console.log(this, x, y);
 }
 let obj = {
     id:'11',
     name:'邱顺俊',
 };
 document.body.onclick = fn;
 //=> this:document.body

需求:当点击BODY时,让fn执行,并且this->obj,传递值10->x、20->y

 document.body.onclick = function (ev){
    //=> 当触发BODY的点击事件时
    //=> 执行的是这个匿名函数
    //=> 这里 this:body;
    //=> 然后,实现上面的需求:
    fn.call(obj, 10, 20, ev);
};
//=> 或者直接用bind,也能实现相同的效果
document.body.onclick = fn.bind(obj, 10, 20);

△ bind不会让函数立即执行的,执行点击事件执行

五、重写内置的BIND

 //实现bind方法,返回的是一个函数
       Function.prototype._bind = function _bind(context, ...parms) {
           let that = this;
           return function (...arg) {
               context == null ? context = window : null;
               /^(function|object)$/.test(context) ? null : context = Object(context);
               let xxx = Symbol();
               parms=parms.concat(arg);
               context[xxx] = that;
               let result = context[xxx](...parms);
               delete context[xxx];
               return result;
           }
       }

或者

Function.prototype.bind = function (context, ...parms){
   let self = this;
   return function (...arg){
       return self.call(context, ...parms.concat(arg));
   };
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值