首頁  >  文章  >  web前端  >  一行有趣的JS程式碼

一行有趣的JS程式碼

coldplay.xixi
coldplay.xixi轉載
2020-10-29 17:07:422056瀏覽

##javascript 欄目的詳細介紹一段程式碼。

一行有趣的JS程式碼

##我們常在框架層級的原始碼中看到類似下面的一段程式碼,例如:

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方法被覆盖!"复制代码

一行有趣的JS程式碼

一行有趣的JS程式碼##結果很明顯。第一種方法仍然能正確得到結果,而第二種則不行!那麼為什麼會這樣我們知道bind函數返回結果是一個函數,這個函數是函數內部的函數,會被延遲執行,那麼很自然聯想到這裡可能存在閉包!

閉包因為保持內部函數執行時的上下文可以狀態

。不過在現代版瀏覽器中call和bind都已經被js引擎內部實現了,我們沒有辦法調試!但是下面我們可以通過polly-fill提供的近似實現的源碼來理解內部引擎的邏輯,是論文調試的demo,大家可以嘗試下:<pre class="brush:php;toolbar:false">// 模拟实现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 !== &quot;function&quot;) {    throw new Error(&quot;请使用函数对象调用我,谢谢!&quot;);   }  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({}));      // &quot;[object Object]&quot;console.log(toStr1([]));      // &quot;[object Array]&quot;console.log(toStr1(123));     // &quot;[object Number]&quot;console.log(toStr1(&quot;abc&quot;));   // &quot;[object String]&quot;console.log(toStr1(new Date));// &quot;[object Date]&quot;复制代码</pre> <p><img alt="一行有趣的JS程式碼" class="lazyload" src="https://img.php.cn/upload/article/000/000/052/c343a2d26aa9eb372375cf7ffd9fc424-2.png" data- style="max-width:90%" data- style="max-width:90%"></p> <p>上述的实现略去一些健壮性的代码,仅保留核心逻辑,具体的实现细节这里不做解释,有兴趣的可以自己研究,从devtools我们看到<code>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(视频)

以上是一行有趣的JS程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除