javascript栏目详细介绍一句代码。
我们经常在框架级的源码中看到类似如下的一句代码,比如:
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方法被覆盖!"复制代码
结果很明显。第一种方法仍然能正确得到结果,而第二种则不行!那么为什么会这样呢?我们知道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]"复制代码
上述的实现略去一些健壮性的代码,仅保留核心逻辑,具体的实现细节这里不做解释,有兴趣的可以自己研究,从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-
相关免费学习推荐:javascript(视频)
The above is the detailed content of An interesting line of JS code. For more information, please follow other related articles on the PHP Chinese website!

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

SublimeText3 Linux new version
SublimeText3 Linux latest version

Notepad++7.3.1
Easy-to-use and free code editor

Atom editor mac version download
The most popular open source editor

WebStorm Mac version
Useful JavaScript development tools

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment
