Maison  >  Article  >  interface Web  >  Une ligne intéressante de code JS

Une ligne intéressante de code JS

coldplay.xixi
coldplay.xixiavant
2020-10-29 17:07:422145parcourir

La colonne

javascript présente une phrase de code en détail.

Une ligne intéressante de code JS

Nous voyons souvent un code similaire au suivant dans le code source au niveau du framework, tel que :

var toStr1 = Function.prototype.call.bind(Object.prototype.toString);复制代码

Les deux sont utilisés dans ce code La méthode call utilise également la méthode bind À première vue, cela semble un peu déroutant ! Qu'est-ce que tu veux faire ?

Pas de problème, appelons-le et essayons de passer différents types. L'effet est le suivant :

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]"复制代码

D'après les résultats, vous pouvez voir que la fonction principale de cette méthode est de détecter le type de l'objet. Mais généralement, pour la détection de type, nous pouvons voir plus souvent l'implémentation de code suivante :

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]"复制代码

Les étudiants qui sont familiers avec bind et call doivent savoir que les deux méthodes sont essentiellement les mêmes et que la deuxième méthode est plus concise, Nous pouvons simplement obtenir la fonction souhaitée en un seul appel, et la logique du code est claire et plus facile à comprendre. Mais parmi de nombreux frameworks, pourquoi le premier est-il utilisé plus souvent ?

En fait, la raison principale est 防止原型污染 Par exemple, si nous remplaçons la méthode Object.prototype.toString dans le code métier, la deuxième façon d'écrire n'obtiendra pas le résultat correct, mais la première façon de le faire. l'écriture fonctionnera toujours. Essayons-le avec le code :

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

Une ligne intéressante de code JS

Une ligne intéressante de code JS

Le résultat est évident. La première méthode donne toujours un résultat correct, mais pas la seconde ! Alors pourquoi cela se produit-il ? Nous savons que le résultat renvoyé par la fonction bind est une fonction. Cette fonction est une fonction à l'intérieur de la fonction et son exécution sera retardée. Il est donc naturel de penser qu'il peut y avoir une fermeture ici ! 因为闭包可以保持内部函数执行时的上下文状态. Cependant, dans les navigateurs modernes, call et bind ont été implémentés en interne par le moteur js, et nous n'avons aucun moyen de déboguer ! Cependant, nous pouvons comprendre la logique interne du moteur grâce au code source d'implémentation approximatif fourni par polly-fill. Voici la démo déboguée dans cet article. Vous pouvez l'essayer :

.
// 模拟实现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]"复制代码

Une ligne intéressante de code JS

上述的实现略去一些健壮性的代码,仅保留核心逻辑,具体的实现细节这里不做解释,有兴趣的可以自己研究,从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(视频)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer