this硬绑定

一、this显示绑定this显示绑定 , 顾名思义 , 它有别于this的隐式绑定,而隐式绑定必须要求一个对象内部包含一个指向某个函数的属性(或者某个对象或者上下文包含一个函数调用位置),并通过这个属性间接调用这个函数,从而把this简介/隐式绑定到这个对象上 。但是this的隐式绑定存在一个绑定对象丢失问题,如下面代码所示:
function afun() {console.log(this.a);}var obj = {a: 1,afun: afun};var a = "hello";setTimeout(obj.afun, 100);//"hello"出人意料的是,控制台打印出来的结果是全局变量a的结果,而不是拥有指向函数属性的对象的a的值,这就是隐式绑定的对象丢失,回调函数丢失绑定对象是非常常见的 。
但是 , 显示绑定仍然不能解决绑定对象丢失的问题,但是显示绑定的一个变种,即硬绑定可以解决绑定对象丢失 。
在此之前,我们先来看看什么是显示绑定 。
我们可以通过使用函数的call()和apply()方法实现this显示绑定,绝大多数内置函数和自定义函数都可以调用这两种方法 。他们均接收两个参数

  • 参数:
    • thisArg: 要绑定到调用者this的对象 。
      • arg1, arg2, ...(call):指定的参数列表 , 即要传给调用者的参数 。比如a.call(obj, args)/a.apply(obj, args),args为参数传给调用者函数a作为该函数的实参 。
      • argsArray(apply):一个数组或者类数组对象,如果该参数的值为 nullundefined,则表示不需要传入任何参数 。
    • 返回值:调用者函数若有返回值则返回该值,没有返回值则返回undefined 。
来看下面的代码:
function bfun() {return this.a;}var obj1 = {a: "hello"};console.log(bfun.apply(obj1), bfun.call(obj1));//"hello" "hello"若传入的第一个参数时一个原始值呢?来看下面的代码:
function bfun() {return this;}console.log(bfun.apply(3));//[Number: 2]没错,原始值被转换成了它的对象形式 , 也就是new Number(),这被成为"装箱" 。
但是 , 我们前面提到过,this显示绑定虽然强大,但是仍然不能解决this绑定丢失问题 。下面我们来解释硬绑定,即显示绑定的一个变种,它能完美解决this绑定丢失问题 。
二、硬绑定先来看看下面的代码:
function cfun() {console.log(this.a)//"hello"return this.a;}var obj2 = {a: "hello"};var fn = function() {return cfun.apply(obj2);};console.log(fn());//"hello"setTimeout(fn, 100);//"hello"可以看到 , 硬绑定确实解决了this绑定丢失,但值得注意的是,通过apply()绑定的this对象,无法二次更改绑定对象:
function f() {console.log( this.a );}var obj = {a:2};var b = function() {f.call( obj );};b(); // 2b.call( window ); // 2硬绑定的一个典型应用场景是创建一个包裹函数,传入所有的参数给调用者函数并返回接收到的所有值 。
function dfun(v) {return (v[0] + this.a);}var obj3 = {a: 10};var fn1 = function() {return dfun.call(obj3, arguments);}var result = fn1(6);console.log(result);//16对于arguments而言,call()和apply()的不同之处在于他们的参数类型不同:
function efun(v) {console.log(..v)//6 10 11return (v);}var obj4 = {a: 10};var fn1 = function() {return efun.apply(obj3, arguments);}var result = fn1([6, 10, 11]);console.log(result);//[6, 10, 11]也就是说call()的参数类型是Object,它传入的参数列表会被转换为键为'0'(随传入参数数量递增),值为传入参数的对象;而apply()的参数类型则是数组或者类数组 。
function dfun(v) {return (v);}var obj3 = {a: 10};var fn1 = function() {return dfun.call(obj3, arguments);}var result = fn1(6, 19, 1, 1);console.log(result);//[Arguments]{'0':6,'1':19,2':1,3':1}console.log(typeof result);//'object'最强大的一种方法是将包裹函数创建为可以重复使用的辅助函数 , 封装可重复使用的硬绑定 。
function ffun(v) {return this.a * v;}var obj5 = {a: 5,};var fn2 = function(fn, obj) {return function() {return fn.apply(obj, arguments);}}var bind = fn2(ffun, obj5);console.log(bind(10));//50由于硬绑定十分常用,但通过包裹函数创建可重复使用的硬绑定比较麻烦,所以ES5提供了一个实现相同功能的方法bind() 。用法如下:
function hfun(v) {return this.a * v;}var obj6 = {a: 6};var bind = hfun.bind(obj6);console.log(bind(4));//24可以看到,我们再无需构建一个包裹函数来手动调用call或apply方法,只需要提供调用者和绑定到调用者this的对象即可 。

推荐阅读