4.1 修改THIS指向 call、apply、bind

每一个函数(普通函数/构造函数/内置类)都是Function这个内置类的实例,所以:函数._proto_===Function.prototype,函数可以直接>调取Function原型上的方法;

call / apply / bind

  • 原型上提供的三个公有属性方法
  • 每一个函数都可以调用这个方法执行
  • 这些方法都是用来改变函数中的THIS指向的
function fn(){}
fn.call();   //=>fn函数基于原型链找到Function.prototype上的call方法,并且让其执行(执行的是call方法:方法中的this是fn)
fn.call.call();   //=>fn.call就是Function.prototype上的call方法,也是一个函数,只要是函数就能用原型上的方法,所以可以继续调用call来执行;
Function.prototype.call = function $1(){
   //...
}
fn.call => $1
fn.call() => $1()  this:fn
fn.call.call() => $1.call() => 继续让call执行,this:$1

实例.方法():都是找到原型上的内置方法,让内置方法先执行(只不过执行的时候做了一些事情会对实例产生改变,而这也是这些内置方法的作用),内置方法中的THIS一般都是当前操作的实例

call

语法: 函数.call([context],[params1],...)
函数基于原型链找到Function.prototype.call这个方法,并且把它执行,在call方法执行的时候完成了一些功能;

1、让当前函数执行;
2、把函数中的THIS指向改为第一个传递给call的实参;
3、把传递给call其余的实参当做参数信息传递给当前函数;
如果执行call一个实参都没有传递,非严格模式下是让函数中的this执行window,严格模式下指向undefined;

//需求: 让FN执行的时候,方法中的THIS指向obj;
window.name = 'window';
let obj = {
    name:'obj'
}
let fn = function(n,m){
    console.log(this.name);
}

fn.call(obj);  // obj
fn.call();   //一个实参都没有传递,非严格模式下是让函数中的this执行window,严格模式下指向undefined;
fn.call(null) //=> this:window   严格下是NULL(第一个参数传递的是null/undefined/不传,非严格模式下this指向window,严格模式下传递的是谁this就是谁,不传this是undefined;
fn.call(obj,1,2)  //this指向obj,u=1,m=2;
fn()    //window

// fn();  //window
// obj.fn();   // Uncaught TypeError: obj.fn is not a function
//因为obj中没有fn这个属性;
obj.fn = fn;
obj.fn();     // => obj
delete obj.fn;
console.log(obj)

练习题

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

apply方法

和call方法一样,都是把函数执行,并且改变里面的this关键字的,唯一的区别就是传递给函数参数的方式不同

  • call是一个个传参
  • apply是按照数组传参
let obj={name:'OBJ'};
let fn=function(n,m){
    console.log(this.name);
}
//=>让fn方法执行,让方法中的this变为obj,并且传递10/20
fn.call(obj,10,20);
fn.apply(obj,[10,20]);

bind方法

和call/apply一样,也是用来改变函数中的this关键字的,只不过基于bind改变this,当前方法并没有被执行,类似于预先改变this;

let obj={name:'OBJ'};
function fn(){
    console.log(this.name);
}
document.body.onclick=fn; //=>当事件触发,fn中的this:BODY

//=>点击BODY,让FN中的THIS指向OBJ
//document.body.onclick=fn.call(obj); //=>基于call/apply这样处理,不是把fn绑定给事件,而是把fn执行后的结果绑定给事件
document.body.onclick=function(){
    //this:BODY
    fn.call(obj);
}
document.body.onclick=fn.bind(obj); //=>bind的好处是:通过bind方法只是预先把fn中的this修改为obj,此时fn并没有执行呢,当点击事件触发才会执行fn(call/apply都是改变this的同时立即把方法执行) =>在IE6~8中不支持bind方法  预先做啥事情的思想被称为“柯理化函数”

内置call/apply/bind实现的办法

~ function () {

    /*生成随机函数名:时间戳的方式*/
    function queryRandomName() {
        let time = new Date().getTime();
        return '$ran' + time;
    };

    /*
     * call:改变函数中的THIS指向 
     *   @params
     *      context 可以不传递,传递必须是引用类型值(因为后面要给它加$fn的属性)  
     */
    
    
    /*模拟CALL方法改变函数中的THIS*/
    function changeThisCall(context = window, ...ary) {
        let _this = this,
            result = null,
            ran = queryRandomName();
        context[ran] = _this;
        result = context[ran](...ary);
        delete context[ran];
        return result;
    };

    //APPLY方法
    function changeThisApply(context, arg = []) {
        let _this = this,
            result = null,
            ran = queryRandomName();
        context[ran] = _this;
        result = context[ran](...arg);
        delete context[ran];
        return result;
    } 

    // =>bind方法
    //=>bind方法在IE6~8中不兼容,接下来我们自己基于原生JS实现这个方法
    function myBind(context=window,...arg) {
        let _this = this,
            outerArg = arg;
        return function anonymous(...arg) {
            let innerArg = arg;
            _this.apply(context, outerArg.concat(innerArg));
        }
    };

    // 把方法挂在函数的原型上;
    ["changeThisCall", "changeThisApply","myBind"].forEach(item => {
        Function.prototype[item] = eval(item);
    });
}();

call练习题解析


内置CALL方法的实现2.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容