Home > Web Front-end > JS Tutorial > front-to-back closure

front-to-back closure

PHPz
Release: 2023-09-02 16:09:03
Original
1198 people have browsed it

In the field of JavaScript, closures are often regarded as a mysterious art. Once you master them, you can write some truly amazing JavaScript. This article will give you a quick introduction to the magic of JavaScript closures.


What is closure?

One of the key facts of JavaScript is that everything is an object. Of course, this also includes functionality.

A closure is nothing more than a function object with an associated scope in which the function's variables are resolved.

Closures are named after the way they are closed by their contents. Consider the following JavaScript code:

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);
Copy after login

If you fire up your favorite console and run that bad boy, you'll get a delicious message to the effect of "This pizza is all pepperoni...but put the ham on three slices." This The examples illustrate some key concepts in JavaScript that are essential for mastering closures.

Closure is a function object

How many function objects are there in the above code? Well...we have the pizzaParty function, and nested within that function is the innerFunction. Math isn't always my strong point, but in my book 1 1 = 2 . Each function object has its own set of variables, which are resolved in the scope of each function.

A closure has its own scope

Closures cannot be fully understood without a solid foundation in scope. JavaScript's scoping mechanism allows each function to have its own topping variable, without which we might end up with too much pepperoni, too little ham, or *gasp* at our pizza party. .. some anchovies. Let's use a simple example to better illustrate this idea.

front-to-back closure

Functions are executed using the scope in effect when the function is defined. It has nothing to do with the valid scope when the function is called.

Variable helper functions work from the outside in

Green arrows indicate accessibility from outside to inside. Variables defined in the scope outside a function can be accessed from within the function.

If we omit the topping variable in the pizzaParty function, then we will get a message like "This Pizza is all about the anchovi", but since pizzaParty is in It has a topping variable in its own scope; those salty fools never come near our pizza party.

Likewise, the numSlices parameter is accessible from inside innerFunction because it is defined in the scope above - in this case the scope of pizzaParty.

Variable accessibility does not work from the inside out

The red arrow indicates that variables within a function's scope can never be accessed outside that function. This occurs only if the variable meets one of the following conditions:

  1. is using the var keyword.
  2. Variables are parameters of functions or external functions.
  3. This variable is a nested function.

Omitting the var keyword when setting a variable will cause JavaScript to set the closest named variable in the external function, all the way up to the global scope. So, using our example, ham topping in innerFunction cannot be accessed from pizzaParty, and pizzaParty# cannot be accessed in the global scope where anchovi is located. Pepperoni in topping.

JavaScript uses lexical scope

Lexical scope means that the function is executed using the variable scope that was in effect when the function was defined. It has nothing to do with the valid scope when the function is called. This fact is crucial to unlocking the power of closures.

Now that we understand what a closure is and what the scope of a closure means, let's dive into some classic use cases.


Use closures to protect privacy

Closures are a

way of hiding code from the public. Closures allow you to easily have private members that are isolated from the outside world:

(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);
Copy after login

Closures make it easy to have private members that are isolated from the outside world.

Let's break it down. Our top-level function object is an anonymous function:

(function(exports){
	
})(window);
Copy after login

We call this anonymous function immediately. We pass it the global context (

window in this case) so that we can "export" a public function but hide all others. Because function myPrivateMultiplyFunction is a nested function, it only exists within our closure scope; so we can use it anywhere within this scope, and only within this scope.

JavaScript will keep a reference to our private function for use inside the multiplier function, but it will not be accessible outside the closure

myPrivateMultiplyFunction . Let's try this:

multiply(2,6) // => 12
myPrivateMultiplyFunction(2,6) // => ReferenceError: myPrivateMultiplyFunction is not defined
Copy after login

闭包允许我们定义一个供私人使用的函数,同时仍然允许我们控制世界其他地方所看到的内容。闭包还能做什么?


使用闭包进行元编程

在生成代码时,闭包非常方便。厌倦了记住键盘事件的所有那些烦人的键代码?一种常见的技术是使用键映射:

var KeyMap = {
	"Enter":13,
	"Shift":16,
	"Tab":9,
	"LeftArrow":37
};
Copy after login

然后,在键盘事件中,我们要检查是否按下了某个键:

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);
	}
}
Copy after login

捕捉瞬间

上面的例子并不是最糟糕的,但是我们可以使用元编程和闭包来做出更好的解决方案。使用我们现有的 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]);

}
Copy after login

闭包非常强大,因为它们可以捕获定义它们的函数的局部变量和参数绑定。

此循环为 KeyMap 中的每个键生成一个 is 函数,并且我们的 txtInput.onkeypress 函数变得更具可读性:

var txtInput = document.getElementById('myTextInput');
txtInput.onkeypress = function(e) {
	if(KeyMap.isEnter(e)) {
		console.log(txtInput.value);
	}
}
Copy after login

魔法从这里开始:

KeyMap["is" + key] = (function(compare){
	
})(KeyMap[key]); //invoke immediately and pass the current value at KeyMap[key]
Copy after login

当我们循环 KeyMap 中的键时,我们将该键引用的值传递给匿名外部函数并立即调用它。这将该值绑定到该函数的 compare 参数。

我们感兴趣的闭包是我们从匿名函数内部返回的闭包:

return function(ev) {
	var code = ev.keyCode || ev.which;
	return code === compare;
}
Copy after login

请记住,函数是在定义函数时的作用域内执行的。 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);
	};
};
Copy after login

这个小宝石允许任何函数创建其自身的缓存版本。您可以看到该函数返回一个函数本身,因此可以像这样应用和使用此增强功能:

Math.sin = Math.sin.cached();
Math.sin(1) // => 0.8414709848078965
Math.sin(1) // => 0.8414709848078965 this time pulled from cache
Copy after login

注意发挥作用的结束技巧。我们有一个本地 cache 变量,该变量保持私有并与外界屏蔽。这将防止任何可能使我们的缓存失效的篡改。

返回的闭包可以访问外部函数的绑定,这意味着我们能够返回一个可以完全访问内部缓存以及原始函数的函数!这个小函数可以为性能带来奇迹。这个特定的扩展被设置为处理一个参数,但我很想看到您对多参数缓存函数的尝试。


野外关闭

作为额外的好处,让我们看一下闭包的一些实际用途。

jQuery

有时,著名的 jQuery $ 工厂不可用(例如 WordPress),而我们希望以通常的方式使用它。我们可以使用闭包来允许内部函数访问我们的 $ 参数绑定,而不是使用 jQuery.noConflict

(function($){
	$(document).ready(function(){
		//business as usual....
	});
})(jQuery);
Copy after login

骨干.js

在大型 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);
Copy after login

结论

快速回顾一下我们所学到的知识:

  • 闭包只不过是一个具有作用域的函数对象。
  • 闭包因其“关闭”其内容的方式而得名。
  • 闭包在 JavaScript 的词法范围上带来了巨大的收益。
  • 闭包是 JavaScript 中实现隐私的方式。
  • 闭包能够捕获外部函数的局部变量和参数绑定。
  • JavaScript 可以通过一些闭包魔法进行强大的扩展。
  • 闭包可以与许多您喜爱的库一起使用,让它们变得更酷!

非常感谢您的阅读!随意问任何问题。现在让我们享受披萨派对吧!

The above is the detailed content of front-to-back closure. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template