javascript的案例分析
创始人
2024-11-17 22:03:24
0次

我们经常在框架级的源码中看到类似如下的一句代码,比如:

var toStr1 = Function.prototype.call.bind(Object.prototype.toString);

在这一句代码中既使用call方法,同时也使用bind方法,乍看之下,有点晕!这到底是想干嘛?

无妨,我们调用看看,传入不同的类型试试,效果如下:

console.log(toStr1({}));      // "[object Object]"console.log(toStr1([]));      // "[object Array]"console.log(toStr1(123));     // "[object Number]"console.log(toStr1("abc"));   // "[object String]"console.log(toStr1("abc"));   // "[object String]"console.log(toStr1(new Date));// "[object Date]"复制代码

从结果中可以看到该方法的主要功能是用于检测对象的类型。但通常类型检测,我们可能更多地看到如下代码实现:

var toStr2 = obj => Object.prototype.toString.call(obj);console.log(toStr2({}));      // "[object Object]"console.log(toStr2([]));      // "[object Array]"console.log(toStr2(123));     // "[object Number]"console.log(toStr2("abc"));   // "[object String]"console.log(toStr2("abc"));   // "[object String]"console.log(toStr2(new Date));// "[object Date]"复制代码

熟悉bind和call的同学应该知道,两种方法本质上是相同的,而第二种方法更简洁,仅仅使用一次call就能获得我们想要的功能,且代码逻辑清晰,理解起来更加容易,可在众多框架中为何更多使用第一种呢?

其实主要的原因是防止原型污染,比如我们在业务代码中覆写了Object.prototype.toString方法,第二种写法将得不到正确的结果,而第一种写法仍然可以。我们用代码来来试试:

var toStr1 = Function.prototype.call.bind(Object.prototype.toString);var toStr2 = obj => Object.prototype.toString.call(obj);Object.prototype.toString = function(){  return'toString方法被覆盖!';
}// 接着我们再调用上述方法// toStr1调用结果如下:console.log(toStr1({}));      // "[object Object]"console.log(toStr1([]));      // "[object Array]"console.log(toStr1(123));     // "[object Number]"console.log(toStr1("abc"));   // "[object String]"console.log(toStr1("abc"));   // "[object String]"console.log(toStr1(new Date));// "[object Date]"// toStr2调用结果如下:console.log(toStr2({}));      // "toString方法被覆盖!"console.log(toStr2([]));      // "toString方法被覆盖!"console.log(toStr2(123));     // "toString方法被覆盖!"console.log(toStr2("abc"));   // "toString方法被覆盖!"console.log(toStr2("abc"));   // "toString方法被覆盖!"console.log(toStr2(new Date));// "toString方法被覆盖!"复制代码

javascript的案例分析

javascript的案例分析

结果很明显。第一种方法仍然能正确得到结果,而第二种则不行!那么为什么会这样呢?我们知道bind函数返回结果是一个函数,这个函数是函数内部的函数,会被延迟执行,那么很自然联想到这里可能存在闭包!因为闭包可以保持内部函数执行时的上下文状态。不过在现代版浏览器中call和bind都已经被js引擎内部实现了,我们没有办法调试!但是我们可以通过polly-fill提供的近似实现的源码来理解引擎内部的逻辑,下面是本文调试的demo,大家可以尝试下:

// 模拟实现call// ES6实现Function.prototype.mycall = function (context) {
  context = context ? Object(context) : window;  var fn = Symbol();
  context[fn] = this;  let args = [...arguments].slice(1);  let result = context[fn](...args);  delete context[fn]  return result;
}// 模拟实现bindFunction.prototype.mybind = function (context) {  if (typeof this !== "function") {    throw new Error("请使用函数对象调用我,谢谢!");
  }  var self = this;  var args = Array.prototype.slice.call(arguments, 1);  var fNOP = function () { };  var fBound = function () {    var bindArgs = Array.prototype.slice.call(arguments);    return self.myapply(this instanceof fNOP ? this : context, args.concat(bindArgs));
  }

  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();  return fBound;
}// 模拟实现apply// ES6实现Function.prototype.myapply = function (context, arr) {
    context = context ? Object(context) : window;    var fn = Symbol();
    context[fn] = this;    let result;    if (!arr) {
        result = context[fn]();
    } else {
        result = context[fn](...arr);
    }    delete context[fn]    return result;
}var toStr1 = Function.prototype.mycall.mybind(Object.prototype.toString);console.log(toStr1({}));      // "[object Object]"console.log(toStr1([]));      // "[object Array]"console.log(toStr1(123));     // "[object Number]"console.log(toStr1("abc"));   // "[object String]"console.log(toStr1(new Date));// "[object Date]"复制代码

javascript的案例分析

上述的实现略去一些健壮性的代码,仅保留核心逻辑,具体的实现细节这里不做解释,有兴趣的可以自己研究,从devtools我们看到mybind形成的闭包确实在函数对象toStr1的作用域上!

当然如果你对原型链有深刻理解的话,其实这句有趣的代码还可以写成如下方式:

var toStr3 = Function.call.bind(Object.prototype.toString);var toStr4 = Function.call.call.bind(Object.prototype.toString);var toStr5 = Function.call.call.call.bind(Object.prototype.toString);// 甚至可以这么写。。。var toStr6 = (()=>{}).call.bind(Object.prototype.toString);复制代码

-END-

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...