Maison > interface Web > js tutoriel > Partagez 5 techniques avancées pour les fonctions JS

Partagez 5 techniques avancées pour les fonctions JS

青灯夜游
Libérer: 2020-10-26 17:57:24
avant
1697 Les gens l'ont consulté

Partagez 5 techniques avancées pour les fonctions JS

Une fonction est un bloc de code événementiel ou réutilisable qui s'exécute lorsqu'elle est appelée. Les fonctions sont un concept central pour tout langage, notamment en JavaScript. Cet article fournira une introduction approfondie à 5 techniques avancées pour les fonctions.

Constructeur scope-safe

Le constructeur est en fait une fonction appelée à l'aide de l'opérateur new

function Person(name,age,job){
    this.name=name;    
    this.age=age;    
    this.job=job;
}
var person=new Person('match',28,'Software Engineer');
console.log(person.name);//match
Copier après la connexion

Si l'opérateur new n'est pas utilisé, les trois les attributs initialement destinés à l'objet Person ont été ajoutés à l'objet window

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}
var person = Person('match', 28, 'Software Engineer');
console.log(person); //undefinedconsole.log(window.name);//match
Copier après la connexion

L'attribut name de window est utilisé pour identifier la cible du lien et le cadre. L'écrasement accidentel de cet attribut peut provoquer d'autres erreurs sur la page. La solution à ce problème consiste à créer un constructeur scope-safe.

function Person(name, age, job) {
    if (this instanceof Person) {
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name, age, job);
    }
}
var person = Person('match', 28, 'Software Engineer');
console.log(window.name); // ""
console.log(person.name); //'match'
var person= new Person('match',28,'Software Engineer');
console.log(window.name); // ""
console.log(person.name); //'match'
Copier après la connexion

Cependant, l'héritage du modèle de vol du constructeur entraînera des effets secondaires. En effet, dans le code suivant, l'objet this n'est pas une instance de l'objet Polygon, donc le constructeur Polygon() créera et renverra une nouvelle instance.

function Polygon(sides) {
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function() {
            return 0;
        }
    } else {
        return new Polygon(sides);
    }
}
function Rectangle(wifth, height) {
    Polygon.call(this, 2);
    this.width = this.width;
    this.height = height;
    this.getArea = function() {
        return this.width * this.height;
    };
}
var rect = new Rectangle(5, 10);
console.log(rect.sides); //undefined
Copier après la connexion

Si vous souhaitez utiliser le constructeur scope-safe pour voler le modèle, vous devez combiner l'héritage de la chaîne de prototypes et réécrire l'attribut prototype de Rectangle afin que son instance devienne également une instance de Polygon.

function Polygon(sides) {
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function() {
            return 0;
        }
    } else {
        return new Polygon(sides);
    }
}
function Rectangle(wifth, height) {
    Polygon.call(this, 2);
    this.width = this.width;
    this.height = height;
    this.getArea = function() {
        return this.width * this.height;
    };
}
Rectangle.prototype = new Polygo
Copier après la connexion

Fonction de chargement paresseux

En raison des différences de comportement entre les navigateurs, nous incluons souvent un grand nombre d'instructions if dans la fonction pour vérifier les fonctionnalités du navigateur de navigation à résoudre problèmes de compatibilité avec différents navigateurs. Par exemple, notre fonction la plus courante pour ajouter des événements aux nœuds dom

function addEvent(type, element, fun) {
    if (element.addEventListener) {
        element.addEventListener(type, fun, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + type, fun);
    } else {
        element['on' + type] = fun;
    }
}
Copier après la connexion

Chaque fois que la fonction addEvent est appelée, elle vérifie les fonctionnalités prises en charge par le navigateur. Tout d'abord, elle vérifie si la méthode addEventListener est prise en charge. S'il est pris en charge, vérifiez si la méthode attachEvent est prise en charge. Si elle n'est pas encore prise en charge, utilisez la méthode de niveau dom0 pour ajouter l'événement.

Ce processus doit être effectué à chaque appel de la fonction addEvent. En fait, si le navigateur prend en charge l'une des méthodes, alors il la prendra toujours en charge et il n'est pas nécessaire d'effectuer d'autres branches. Détecté. En d’autres termes, l’instruction if ne doit pas être exécutée à chaque fois et le code peut s’exécuter plus rapidement.

La solution est le chargement paresseux. Le soi-disant chargement paresseux signifie que la branche d'exécution de la fonction ne se produira qu'une seule fois. Il existe deux façons d'implémenter le chargement paresseux

1 La première consiste à traiter la fonction lorsque la fonction est appelée. Lorsque la fonction est appelée pour la première fois, la fonction sera écrasée comme une autre fonction exécutée de manière appropriée, de sorte que tout appel à la fonction d'origine n'ait pas besoin de passer par la branche d'exécution

Nous pouvons utiliser la méthode suivante Utiliser le chargement paresseux pour réécrire addEvent()

function addEvent(type, element, fun) {
    if (element.addEventListener) {
        addEvent = function(type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    } else if (element.attachEvent) {
        addEvent = function(type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    } else {
        addEvent = function(type, element, fun) {
            element['on' + type] = fun;
        }
    }
    return addEvent(type, element, fun);
}
Copier après la connexion

Dans ce chargement paresseux addEvent(), chaque branche de l'instruction if attribue une valeur à la variable addEvent, couvrant efficacement la fonction d'origine. La dernière étape consiste à appeler la nouvelle fonction d'affectation. La prochaine fois que addEvent() sera appelée, la fonction nouvellement affectée sera appelée directement, il n'est donc pas nécessaire d'exécuter l'instruction if.

Cependant, cette méthode présente un inconvénient. Si le nom de la fonction change, sa modification sera plus difficile.

2. La seconde consiste à spécifier la fonction appropriée lors de la déclaration de la fonction. De cette façon, il n’y aura aucune perte de performances lorsque la fonction est appelée pour la première fois, mais seulement une légère perte de performances lors du chargement du code. Ce qui suit est addEvent() réécrit selon cette idée. Le code suivant crée une fonction anonyme auto-exécutable qui passe par différentes fonctions pour déterminer quelle implémentation de fonction doit être utilisée.

var addEvent = (function() {
    if (document.addEventListener) {
        return function(type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    } else if (document.attachEvent) {
        return function(type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    } else {
        return function(type, element, fun) {
            element['on' + type] = fun;
        }
    }
})();
Copier après la connexion

Liaison de fonction

Dans javascript interagissant avec DOM, vous devez souvent utiliser la liaison de fonction pour définir une fonction, puis la lier à un élément ou une collection DOM spécifique. Sur un programme déclencheur d'événements, les fonctions de liaison sont souvent utilisées avec des fonctions de rappel et des gestionnaires d'événements pour transmettre des fonctions sous forme de variables tout en conservant l'environnement d'exécution du code.

<button id="btn">按钮</button>
<script>var handler = {
    message: "Event handled.",
    handlerFun: function() {
      alert(this.message);
    }
  };
  btn.onclick = handler.handlerFun;</script>
Copier après la connexion

Le code ci-dessus crée un objet appelé handler. La méthode handler.handlerFun() est affectée en tant que gestionnaire d'événements pour un bouton DOM. Lorsque le bouton est enfoncé, cette fonction est appelée et une boîte d'alerte s'affiche.

Bien qu'il semble que la boîte d'avertissement doive afficher Événement géré, elle affiche en fait undefiend. Le problème est que l'environnement de handler.handleClick() n'est pas enregistré, donc cet objet finit par pointer vers le bouton DOM au lieu du gestionnaire.

Ce problème peut être résolu à l'aide de fermetures

<button id="btn">按钮</button>
<script>var handler = {
    message: "Event handled.",
    handlerFun: function() {
      alert(this.message);
    }
  };
  btn.onclick = function() {
    handler.handlerFun();
  }</script>
Copier après la connexion

Bien sûr, il s'agit d'une solution spécifique à ce scénario, créer plusieurs fermetures peut rendre le code difficile à comprendre et à déboguer. Une meilleure approche consiste à utiliser la liaison de fonctions.

Une simple fonction de liaison bind() accepte une fonction et un environnement et renvoie une fonction qui appelle la fonction donnée dans l'environnement donné, en transmettant tous les paramètres inchangés.

function bind(fn, context) {
    return function() {
        return fn.apply(context, arguments);
    }
}
Copier après la connexion

Cette fonction semble simple, mais sa fonction est très puissante. Une fermeture est créée dans bind(). La fermeture utilise apply() pour appeler la fonction entrante et transmet l'objet contextuel et les paramètres à apply(). Lorsque la fonction renvoyée est appelée, elle exécute la fonction passée dans l'environnement donné et donne tous les arguments.

<button id="btn">按钮</button>
<script>function bind(fn, context) {
    return function() {
      return fn.apply(context, arguments);
    }
  }
  var handler = {
    message: "Event handled.",
    handlerFun: function() {
      alert(this.message);
    }
  };
  btn.onclick = bind(handler.handlerFun, handler);</script>
Copier après la connexion

ECMAScript5 définit une méthode native bind() pour toutes les fonctions, simplifiant encore davantage les opérations.

只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就突显出来了。它们主要用于事件处理程序以及setTimeout()和setInterval()。

然而,被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用。

函数柯里化

与函数绑定紧密相关的主题是函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。

function add(num1, num2) {
    return num1 + num2;
}
function curriedAdd(num2) {
    return add(5, num2);
}
console.log(add(2, 3)); //5
console.log(curriedAdd(3));//8
Copier après la connexion

这段代码定义了两个函数:add()和curriedAdd()。后者本质上是在任何情况下第一个参数为5的add()版本。尽管从技术来说curriedAdd()并非柯里化的函数,但它很好地展示了其概念。

柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。

下面是创建柯里化函数的通用方式:

function curry(fn) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments),
        finalArgs = args.concat(innerArgs);
        return fn.apply(null, finalArgs);
    };
}
Copier après la connexion

curry()函数的主要工作就是将被返回函数的参数进行排序。curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。

为了获取第一个参数之后的所有参数,在arguments对象上调用了slice()方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数。然后args数组包含了来自外部函数的参数。在内部函数中,创建了innerArgs数组用来存放所有传入的参数(又一次用到了slice())。

有了存放来自外部函数和内部函数的参数数组后,就可以使用concat()方法将它们组合为finalArgs,然后使用apply()将结果传递给函数。注意这个函数并没有考虑到执行环境,所以调用apply()时第一个参数是null。curry()函数可以按以下方式应用。

function add(num1, num2) {
    return num1 + num2;
}
var curriedAdd = curry(add, 5);
alert(curriedAdd(3)); //8
Copier après la connexion

在这个例子中,创建了第一个参数绑定为5的add()的柯里化版本。当调用cuurriedAdd()并传入3时,3会成为add()的第二个参数,同时第一个参数依然是5,最后结果便是和8。也可以像下例这样给出所有的函数参数:

function add(num1, num2) {
    return num1 + num2;
}
var curriedAdd2 = curry(add, 5, 12);
alert(curriedAdd2()); //17
Copier après la connexion

在这里,柯里化的add()函数两个参数都提供了,所以以后就无需再传递给它们了,函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind()函数。

function bind(fn, context) {
    var args = Array.prototype.slice.call(arguments, 2);
    return function() {
        var innerArgs = Array.prototype.slice.call(arguments),
        finalArgs = args.concat(innerArgs);
        return fn.apply(context, finalArgs);
    };
}
Copier après la connexion

对curry()函数的主要更改在于传入的参数个数,以及它如何影响代码的结果。curry()仅仅接受一个要包裹的函数作为参数,而bind()同时接受函数和一个object对象。

这表示给被绑定的函数的参数是从第三个开始而不是第二个,这就要更改slice()的第一处调用。另一处更改是在倒数第3行将object对象传给apply()。当使用bind()时,它会返回绑定到给定环境的函数,并且可能它其中某些函数参数已经被设好。

要想除了event对象再额外给事件处理程序传递参数时,这非常有用。

var handler = {
    message: "Event handled",
    handleClick: function(name, event){
        alert(this.message + ":" + name + ":" + event.type);
    }
};var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
Copier après la connexion

handler.handleClick()方法接受了两个参数:要处理的元素的名字和event对象。作为第三个参数传递给bind()函数的名字,又被传递给了handler.handleClick(),而handler.handleClick()也会同时接收到event对象。

ECMAScript5的bind()方法也实现函数柯里化,只要在this的值之后再传入另一个参数即可。

var handler = {
    message: "Event handled",
    handleClick: function(name, event) {
        alert(this.message + ":" + name + ":" + event.type);
    }
};
var btn = document.getElementById("my-btn");
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn"));
Copier après la connexion

javaScript中的柯里化函数和绑定函数提供了强大的动态函数创建功能。使用bind()还是curry()要根据是否需要object对象响应来决定。它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外的开销。

函数重写

由于一个函数可以返回另一个函数,因此可以用新的函数来覆盖旧的函数。

function a(){
    console.log(&#39;a&#39;);
    a = function(){
        console.log(&#39;b&#39;);
    }
}
Copier après la connexion

这样一来,当我们第一次调用该函数时会console.log('a')会被执行;全局变量a被重定义,并被赋予新的函数

当该函数再次被调用时, console.log('b')会被执行。

再复杂一点的情况如下所示:

var a = (function() {
    function someSetup() {
        var setup = &#39;done&#39;;
    }
    function actualWork() {
        console.log(&#39;work&#39;);
    }
    someSetup();
    return actualWork;
})()
Copier après la connexion

我们使用了私有函数someSetup()和actualWork(),当函数a()第一次被调用时,它会调用someSetup(),并返回函数actualWork()的引用。

相关免费学习推荐:js视频教程

更多编程相关知识,请访问:编程入门!!

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!

Étiquettes associées:
source:51cto.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal