本文摘自PHP中文网,作者coldplay.xixi,侵删。
javascript栏目详细介绍一句代码。

我们经常在框架级的源码中看到类似如下的一句代码,比如:
1 | var toStr1 = Function.prototype.call.bind(Object.prototype.toString);复制代码
|
在这一句代码中既使用call方法,同时也使用bind方法,乍看之下,有点晕!这到底是想干嘛?
无妨,我们调用看看,传入不同的类型试试,效果如下:
从结果中可以看到该方法的主要功能是用于检测对象的类型。但通常类型检测,我们可能更多地看到如下代码实现:
1 | var toStr2 = obj => Object.prototype.toString.call(obj);console.log(toStr2({}));
|
熟悉bind和call的同学应该知道,两种方法本质上是相同的,而第二种方法更简洁,仅仅使用一次call就能获得我们想要的功能,且代码逻辑清晰,理解起来更加容易,可在众多框架中为何更多使用第一种呢?
其实主要的原因是防止原型污染
,比如我们在业务代码中覆写了Object.prototype.toString
方法,第二种写法将得不到正确的结果,而第一种写法仍然可以。我们用代码来来试试:
1 2 | var toStr1 = Function.prototype.call.bind(Object.prototype.toString); var toStr2 = obj => Object.prototype.toString.call(obj);Object.prototype.toString = function (){ return 'toString方法被覆盖!' ;
}
|


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

上述的实现略去一些健壮性的代码,仅保留核心逻辑,具体的实现细节这里不做解释,有兴趣的可以自己研究,从devtools我们看到mybind
形成的闭包确实在函数对象toStr1的作用域上!
当然如果你对原型链有深刻理解的话,其实这句有趣的代码还可以写成如下方式:
1 | 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);
|
-END-
相关免费学习推荐:javascript(视频)
以上就是一行有趣的JS代码的详细内容,更多文章请关注木庄网络博客!
相关阅读 >>
javascript有多少种输出
javascript的三大组成部分是什么
javascript怎么判断数组是否存在指定元素
html怎样格式化输出json数据
vue访问元素和组件的方法(附示例)
javascript捕获异常的方法有哪些
javascript如何修改文件内容
javascript声明变量的4种方法是什么
手把手带你弄懂javascript中的异步编程
javascript函数式编程中代码组合(compose)的理解
更多相关阅读请进入《javascript》频道 >>
人民邮电出版社
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者
转载请注明出处:木庄网络博客 » 一行有趣的JS代码