Maison >interface Web >js tutoriel >fermeture avant-arrière
En JavaScript, les fermetures sont souvent considérées comme un art mystérieux. Une fois que vous les maîtrisez, vous pouvez écrire du JavaScript vraiment étonnant. Cet article vous donnera une introduction rapide à la magie des fermetures JavaScript.
L'un des faits clés de JavaScript est que tout est un objet. Bien entendu, cela inclut également les fonctionnalités.
Une fermeture n'est rien d'autre qu'un objet fonction avec une portée associée dans laquelle les variables de la fonction sont résolues.
Les fermetures tirent leur nom de la façon dont leur contenu est fermé. Considérez le code JavaScript suivant :
topping = "anchovi"; function pizzaParty(numSlices) { var topping = "pepperoni", innerFunction = function() { var topping = "ham"; console.log(" .....But put " + topping + " on " + numSlices + " slices"); }; console.log("This pizza is all about the " + topping); innerFunction(); } pizzaParty(3);
Si vous allumez votre console préférée et lancez ce mauvais garçon, vous recevrez un délicieux message du type "Cette pizza est entièrement composée de pepperoni... mais mettez le jambon sur trois tranches". Les exemples illustrent certains concepts clés de ". JavaScript indispensable à la maîtrise des fermetures.
Combien d’objets fonction y a-t-il dans le code ci-dessus ? Eh bien... nous avons pizzaParty
函数,并且嵌套在该函数中的是 innerFunction
。数学并不总是我的强项,但在我的书中 1 + 1 = 2
. Chaque objet fonction possède son propre ensemble de variables, qui sont résolues dans le scope de chaque fonction.
Les fermetures ne peuvent pas être pleinement comprises sans une base solide. Le mécanisme de cadrage de JavaScript permet à chaque fonction d'avoir sa propre variable topping
, sans laquelle nous pourrions nous retrouver avec trop de pepperoni, trop peu de jambon, ou *halètement*... des anchois lors d'une soirée pizza. Utilisons un exemple simple pour mieux illustrer cette idée.
Les fonctions sont exécutées en utilisant la portée en vigueur lorsque la fonction est définie. Cela n'a rien à voir avec la portée valide lorsque la fonction est appelée.
Les flèches vertes indiquent l'accessibilité de l'extérieur vers l'intérieur. Les variables définies dans la portée en dehors d’une fonction sont accessibles depuis l’intérieur de la fonction.
Si nous omettons la variable pizzaParty
函数中省略 topping
变量,那么我们会收到类似“This Pizza is all about the anchovi”的消息,但由于 pizzaParty
在其自己的范围内有一个 topping
dans la fonction pizzaParty
, alors nous obtiendrons un message du type "Cette pizza est entièrement consacrée aux anchois", mais comme pizzaParty
est dans son propre champ d'application Il y a une
innerFunction
内部访问 numSlices
参数,因为它是在上面的范围中定义的 - 在本例中是 pizzaParty
De même, on peut aller de
La flèche rouge indique que les variables contenues dans la portée d'une fonction ne sont jamais accessibles en dehors de cette fonction. Cela n'arrivera que si la variable remplit l'une des conditions suivantes :
var
utilise le mot-clé
var
关键字将导致 JavaScript 在外部函数中设置最接近的命名变量,一直到全局范围。因此,使用我们的示例,无法从 pizzaParty
访问 innerFunction
中的火腿 topping
,并且无法在 anchovi 所在的全局范围内访问 pizzaParty
中的意大利辣香肠 topping
Si vous omettez le mot-clé
innerFunction
n'est pas accessible depuis pizzaParty
et n'est pas accessible dans pizzaParty
dans la portée globale où se trouve l'anchovi. de pepperoni JavaScript utilise une portée lexicale
La portée lexicale signifie que la fonction est exécutée en utilisant la portée de la variable qui était en vigueur lorsque la fonction a été définie. Cela n'a rien à voir avec la portée valide lorsque la fonction est appelée. Ce fait est crucial pour libérer le pouvoir des fermetures.
Utilisez des fermetures pour protéger la vie privée
Les fermetures sont unmoyen de cacher le code au public. Les fermetures permettent d'avoir facilement des membres privés isolés du monde extérieur :
(function(exports){ function myPrivateMultiplyFunction(num,num2) { return num * num2; } //equivalent to window.multiply = function(num1,num2) { ... exports.multiply = function(num1,num2) { console.log(myPrivateMultiplyFunction(num1,num2)); } })(window);
Les fermetures permettent d'avoir facilement des membres privés isolés du monde extérieur.
window
),这样我们就可以“导出”一个公共函数,但隐藏其他所有函数。因为函数 myPrivateMultiplyFunction
Décomposons-le. Notre objet fonction de niveau supérieur est une fonction anonyme :
(function(exports){ })(window);
Nous appelons immédiatement cette fonction anonyme. Nous lui transmettons le contexte global (dans ce cas myPrivateMultiplyFunction
qui est une fonction imbriquée et elle n'existe que dans notre portée de fermeture ; nous pouvons donc l'utiliser n'importe où dans cette portée et uniquement dans cette portée Inside.
multiply(2,6) // => 12 myPrivateMultiplyFunction(2,6) // => ReferenceError: myPrivateMultiplyFunction is not defined
闭包允许我们定义一个供私人使用的函数,同时仍然允许我们控制世界其他地方所看到的内容。闭包还能做什么?
在生成代码时,闭包非常方便。厌倦了记住键盘事件的所有那些烦人的键代码?一种常见的技术是使用键映射:
var KeyMap = { "Enter":13, "Shift":16, "Tab":9, "LeftArrow":37 };
然后,在键盘事件中,我们要检查是否按下了某个键:
var txtInput = document.getElementById('myTextInput'); txtInput.onkeypress = function(e) { var code = e.keyCode || e.which //usual fare for getting the pressed key if (code === KeyMap.Enter) { console.log(txtInput.value); } }
上面的例子并不是最糟糕的,但是我们可以使用元编程和闭包来做出更好的解决方案。使用我们现有的 KeyMap
对象,我们可以生成一些有用的函数:
for (var key in KeyMap) { //access object with array accessor to set "dyanamic" function name KeyMap["is" + key] = (function(compare) { return function(ev) { var code = ev.keyCode || ev.which; return code === compare; } })(KeyMap[key]); }
闭包非常强大,因为它们可以捕获定义它们的函数的局部变量和参数绑定。
此循环为 KeyMap
中的每个键生成一个 is
函数,并且我们的 txtInput.onkeypress
函数变得更具可读性:
var txtInput = document.getElementById('myTextInput'); txtInput.onkeypress = function(e) { if(KeyMap.isEnter(e)) { console.log(txtInput.value); } }
魔法从这里开始:
KeyMap["is" + key] = (function(compare){ })(KeyMap[key]); //invoke immediately and pass the current value at KeyMap[key]
当我们循环 KeyMap
中的键时,我们将该键引用的值传递给匿名外部函数并立即调用它。这将该值绑定到该函数的 compare
参数。
我们感兴趣的闭包是我们从匿名函数内部返回的闭包:
return function(ev) { var code = ev.keyCode || ev.which; return code === compare; }
请记住,函数是在定义函数时的作用域内执行的。 compare
参数绑定到循环迭代期间到位的 KeyMap
值,因此我们的嵌套闭包能够捕获它。我们及时拍摄当时有效范围的快照。
我们创建的函数允许我们在每次想要检查关键代码时跳过设置 code
变量,现在我们可以使用方便、可读的函数。
至此,应该相对容易看出闭包对于编写一流的 JavaScript 至关重要。让我们应用我们对闭包的了解来增强 JavaScript 的一种原生类型(惊呼!)。我们将重点放在函数对象上,让我们增强本机 Function
类型:
Function.prototype.cached = function() { var self = this, //"this" refers to the original function cache = {}; //our local, lexically scoped cache storage return function(args) { if(args in cache) return cache[args]; return cache[args] = self(args); }; };
这个小宝石允许任何函数创建其自身的缓存版本。您可以看到该函数返回一个函数本身,因此可以像这样应用和使用此增强功能:
Math.sin = Math.sin.cached(); Math.sin(1) // => 0.8414709848078965 Math.sin(1) // => 0.8414709848078965 this time pulled from cache
注意发挥作用的结束技巧。我们有一个本地 cache
变量,该变量保持私有并与外界屏蔽。这将防止任何可能使我们的缓存失效的篡改。
返回的闭包可以访问外部函数的绑定,这意味着我们能够返回一个可以完全访问内部缓存以及原始函数的函数!这个小函数可以为性能带来奇迹。这个特定的扩展被设置为处理一个参数,但我很想看到您对多参数缓存函数的尝试。
作为额外的好处,让我们看一下闭包的一些实际用途。
有时,著名的 jQuery $
工厂不可用(例如 WordPress),而我们希望以通常的方式使用它。我们可以使用闭包来允许内部函数访问我们的 $
参数绑定,而不是使用 jQuery.noConflict
。
(function($){ $(document).ready(function(){ //business as usual.... }); })(jQuery);
在大型 Backbone.js 项目中,将应用程序模型设为私有,然后在主应用程序视图上公开一个公共 API 可能会更有利。使用闭包,您可以轻松实现此隐私。
(function(exports){ var Product = Backbone.Model.extend({ urlRoot: '/products', }); var ProductList = Backbone.Collection.extend({ url: '/products', model: Product }); var Products = new ProductList; var ShoppingCartView = Backbone.View.extend({ addProduct: function (product, opts) { return CartItems.create(product, opts); }, removeProduct: function (product, opts) { Products.remove(product, opts); }, getProduct: function (productId) { return Products.get(productId); }, getProducts: function () { return Products.models; } }); //export the main application view only exports.ShoppingCart = new ShoppingCartView; })(window);
快速回顾一下我们所学到的知识:
非常感谢您的阅读!随意问任何问题。现在让我们享受披萨派对吧!
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!