《JavaScript设计模式与开发实践》——JavaScript中的call和apply

几点基础知识:

  • Function.prototype.callFunction.prototype.apply 方法用于改变内部函数 this 的指向
  • func.apply(thisArg[, argsArray]) 接受两个参数,第一个参数指定了 func 函数运行时的this,第二个参数是一个数组或者类数组对象
  • func.call(thisArg, arg1, arg2, ...) 方法的作用和 apply() 方法类似,只有一个区别,就是 call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组

举个栗子:
var hero = {
    name:'奥特曼'
};
function beatMonster() {
    console.log(this.name);
}
// call把this由window指向hero对象,并执行了beatMonster方法
beatMonster.call(hero); // 输出:奥特曼
模拟实现call

思路:
1. 将函数设置为对象的属性
2. 执行该函数
3. 删除对象中的该函数

Function.prototype.myCall = function(context) { 
    // context即为hero
    // this为被调用的方法  因为myCall在beatMonster的上下文中执行,所以this指向beatMonster
    context.fn = this; 
    context.fn();
    delete context.fn;
}
beatMonster.myCall(hero); // 输出:奥特曼 没毛病(・。・)

// 接下来考虑有参数的情况
function beatMonster(weapon, level) {
    console.log(this.name);
    console.log(weapon);
    console.log(level);
}
beatMonster.call(hero,'动感光波',10); // 输出:奥特曼 动感光波 10

// 因为参数的实际情况不明,所以使用函数内部的arguments(类数组对象)来获取实参,先凭本能搞一波
Function.prototype.myCall = function(context) {
    // 借用 Array.prototype的方法拿到实参数组
    var args = [].slice.apply(arguments,[1]);
    context.fn = this; 
    context.fn.apply(context, args);
    //这里可以用es6的...操作符 context.fn(...args);但call是es3的方法
    delete context.fn;
}
beatMonster.myCall(hero,'巴拉拉能量',8); // 输出:奥特曼 巴拉拉能量 8 
// 以上的实现从结果来看没毛病,但是感觉怪怪的,模拟call的时候用他兄弟apply,那还模拟个啥
// 改下改下 用个eval方法
Function.prototype.myCall = function(context) {
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args[i-1] = "arguments["+i+"]";
    }
    context.fn = this;
    // 这里 args 会自动调用 Array.toString() 这个方法
    eval('context.fn('+ args +')');
    delete context.fn;
}

看起来好像成功了 (´ω`★)
然而 还有一种情况 beatMonster.myCall(null); 报错 Cannot set property 'fn' of null
再加上两点必须注意:

  • 凡是方法中使用到上下文语境的,一定要考虑默认的上下文(this)
  • 凡是编码对象是function的,一定要考虑返回值

于是代码改为:

Function.prototype.myCall = function(context) {
    context = context || window;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args[i-1] = "arguments["+i+"]";
    }
    context.fn = this;
    // 这里 args 会自动调用 Array.toString() 这个方法
    var result = eval('context.fn('+ args +')');
    delete context.fn;
    return result;
}

(๑•̀ㅂ•́)و✧

模拟实现apply

直接上代码:

Function.prototype.myApply = function(context, arr) {
    context = context || window;
    context.fn = this;
    var result;
    if(!arr) {
        result = context.fn();
    } else {
        var args = [];
        for(var i = 0, len = arr.length; i < len; i++) {
            args[i] = "arr["+i+"]";
        }
        result = eval('context.fn('+ args +')');
    }
    delete context.fn;
    return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值