Rumah > hujung hadapan web > tutorial js > penutupan depan ke belakang

penutupan depan ke belakang

PHPz
Lepaskan: 2023-09-02 16:09:03
asal
1198 orang telah melayarinya

Dalam JavaScript, penutupan sering dianggap sebagai seni misteri. Sebaik sahaja anda menguasainya, anda boleh menulis JavaScript yang benar-benar menakjubkan. Artikel ini akan memberi anda pengenalan pantas kepada keajaiban penutupan JavaScript.


Apakah itu penutupan?

Salah satu fakta penting JavaScript ialah semuanya ialah objek. Sudah tentu, ini juga termasuk fungsi.

Penutupan hanyalah objek fungsi dengan skop yang berkaitan di mana pembolehubah fungsi diselesaikan.

Penutupan mendapat namanya daripada cara kandungannya ditutup. Pertimbangkan kod JavaScript berikut:

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);
Salin selepas log masuk

Jika anda menghidupkan konsol kegemaran anda dan menjalankan budak jahat itu, anda akan mendapat mesej yang lazat dengan kesan "Pizza ini semuanya pepperoni...tetapi letakkan ham pada tiga keping Ini Contoh menggambarkan beberapa konsep utama." JavaScript yang penting untuk menguasai penutupan.

Penutupan ialah objek fungsi

Berapakah bilangan objek fungsi yang terdapat dalam kod di atas? Nah...kami mempunyai fungsi pizzaParty dan bersarang dalam fungsi itu ialah innerFunction. Matematik tidak selalu menjadi titik kuat saya, tetapi dalam buku saya 1 + 1 = 2 . Setiap objek fungsi mempunyai set pembolehubahnya sendiri, yang diselesaikan dalam pizzaParty 函数,并且嵌套在该函数中的是 innerFunction。数学并不总是我的强项,但在我的书中 1 + 1 = 2 。每个函数对象都有自己的一组变量,这些变量在每个函数的范围中解析。

闭包有其自己的范围

如果没有坚实的范围基础,就无法完全理解闭包。 JavaScript 的作用域机制允许每个函数拥有自己的 topping 变量,如果没有它,我们可能会在披萨派对上吃到太多的意大利辣香肠、太少的火腿或 *gasp* ... 一些凤尾鱼。让我们用一个简单的例子来更好地说明这个想法。

penutupan depan ke belakang

函数是使用定义函数时有效的作用域来执行的。它与调用函数时的有效范围无关。

变量辅助功能由外而内工作

绿色箭头表明可访问性是从外到内的。在函数外部范围内定义的变量可以从函数内部访问。

如果我们在 pizzaParty 函数中省略 topping 变量,那么我们会收到类似“This Pizza is all about the anchovi”的消息,但由于 pizzaParty 在其自己的范围内有一个 topping 变量;那些咸味的傻瓜永远不会靠近我们的披萨派对。

同样,可以从 innerFunction 内部访问 numSlices 参数,因为它是在上面的范围中定义的 - 在本例中是 pizzaParty 的范围。

变量可访问性无法从内到外发挥作用

红色箭头表明函数范围内的变量永远无法在该函数之外访问。仅当变量满足以下条件之一时才会出现这种情况:

  1. 正在使用 var 关键字。
  2. 变量是函数或外部函数的参数。
  3. 该变量是一个嵌套函数。

设置变量时省略 var 关键字将导致 JavaScript 在外部函数中设置最接近的命名变量,一直到全局范围。因此,使用我们的示例,无法从 pizzaParty 访问 innerFunction 中的火腿 topping,并且无法在 anchovi 所在的全局范围内访问 pizzaParty 中的意大利辣香肠 topping

JavaScript 使用词法作用域

词法作用域意味着函数是使用定义函数时有效的变量作用域来执行的。它与调用函数时的有效范围无关。这一事实对于释放闭包的力量至关重要。

现在我们了解了闭包是什么以及闭包的范围意味着什么,让我们深入研究一些经典用例。


使用闭包保护隐私

闭包是向公众隐藏代码的方式。通过闭包,您可以轻松地拥有与外界隔离的私有成员:

(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);
Salin selepas log masuk

通过闭包,您可以轻松拥有与外界隔离的私人成员。

让我们来分解一下。我们的顶级函数对象是一个匿名函数:

(function(exports){
	
})(window);
Salin selepas log masuk

我们立即调用这个匿名函数。我们将全局上下文传递给它(在本例中为 window),这样我们就可以“导出”一个公共函数,但隐藏其他所有函数。因为函数 myPrivateMultiplyFunction 是一个嵌套函数,它只存在于我们的闭包范围内;所以我们可以在这个范围内的任何地方使用它,并且只能在这个范围内。

JavaScript 将保留对我们的私有函数的引用,以便在乘法函数内部使用,但无法在闭包外部访问 myPrivateMultiplyFunctionskop

setiap fungsi. 🎜 🎜Penutupan mempunyai skop tersendiri🎜 🎜 Penutupan tidak dapat difahami sepenuhnya tanpa asas yang kukuh dalam skop. Mekanisme skop JavaScript membenarkan setiap fungsi mempunyai pembolehubah toppingnya sendiri, tanpanya kita mungkin akan mendapat terlalu banyak pepperoni, terlalu sedikit ham atau *terkejut* di pesta piza kami ... beberapa ikan bilis. Mari kita gunakan contoh mudah untuk menggambarkan idea ini dengan lebih baik. 🎜 🎜Penutupan depan ke belakang🎜
🎜 Fungsi dilaksanakan menggunakan skop yang berkuat kuasa apabila fungsi ditakrifkan. Ia tiada kaitan dengan skop yang sah apabila fungsi dipanggil. 🎜 🎜 🎜Pembantu boleh ubah bekerja dari luar dalam🎜 🎜 Anak panah hijau menunjukkan kebolehcapaian dari luar ke dalam. Pembolehubah yang ditakrifkan dalam skop di luar fungsi boleh diakses dari dalam fungsi. 🎜 🎜 Jika kita meninggalkan pembolehubah topping dalam fungsi pizzaParty, maka kita akan mendapat mesej seperti "Pizza ini adalah mengenai ikan bilis", tetapi kerana pizzaParty code> mempunyai pembolehubah topping dalam skopnya sendiri; 🎜 🎜 Begitu juga, parameter numSlices boleh diakses dari dalam innerFunction kerana ia ditakrifkan dalam skop di atas - dalam kes ini skop pizzaParty . 🎜 🎜Kebolehaksesan boleh ubah tidak berfungsi dari dalam ke luar🎜 🎜 Anak panah merah menunjukkan bahawa pembolehubah dalam skop fungsi tidak boleh diakses di luar fungsi tersebut. Ini hanya akan berlaku jika pembolehubah memenuhi salah satu daripada syarat berikut: 🎜
  1. Kata kunci var sedang digunakan.
  2. Pembolehubah ialah parameter fungsi atau fungsi luaran.
  3. Pembolehubah ini ialah fungsi bersarang.
🎜 Meninggalkan kata kunci var semasa menetapkan pembolehubah akan menyebabkan JavaScript menetapkan pembolehubah bernama yang paling hampir dalam fungsi luaran, sehingga ke skop global. Jadi, menggunakan contoh kami, ham topping dalam innerFunction tidak boleh diakses daripada pizzaParty dan tidak boleh diakses dalam skop global tempat ikan bilis berada . Pepperoni topping dalam >pizzaParty. 🎜 🎜JavaScript menggunakan skop leksikal🎜 🎜 Skop leksikal bermaksud bahawa fungsi dilaksanakan menggunakan skop pembolehubah yang berkuat kuasa apabila fungsi itu ditakrifkan. Ia tiada kaitan dengan skop yang sah apabila fungsi dipanggil. Fakta ini penting untuk membuka kunci kuasa penutupan. 🎜 🎜 Sekarang setelah kita memahami maksud penutupan dan maksud skop penutupan, mari kita selami beberapa kes penggunaan klasik. 🎜 🎜 🎜Gunakan penutupan untuk melindungi privasi🎜 🎜 Penutupan ialah cara 🎜🎜 menyembunyikan kod daripada orang ramai. Penutupan membolehkan anda dengan mudah mempunyai ahli persendirian yang terasing dari dunia luar: 🎜
multiply(2,6) // => 12
myPrivateMultiplyFunction(2,6) // => ReferenceError: myPrivateMultiplyFunction is not defined
Salin selepas log masuk
Salin selepas log masuk
🎜 🎜 Penutupan memudahkan untuk mempunyai ahli persendirian yang terasing dari dunia luar. 🎜 🎜 🎜 Mari kita pecahkannya. Objek fungsi peringkat teratas kami ialah fungsi tanpa nama: 🎜
var KeyMap = {
	"Enter":13,
	"Shift":16,
	"Tab":9,
	"LeftArrow":37
};
Salin selepas log masuk
Salin selepas log masuk
🎜 Kami memanggil fungsi tanpa nama ini dengan segera. Kami menyerahkan konteks global kepadanya (window dalam kes ini) supaya kami boleh "mengeksport" fungsi awam tetapi menyembunyikan semua yang lain. Oleh kerana fungsi myPrivateMultiplyFunction ialah fungsi bersarang, ia hanya wujud dalam skop penutupan kami supaya kami boleh menggunakannya di mana-mana sahaja dalam skop ini dan hanya dalam skop ini. 🎜 🎜 JavaScript akan menyimpan rujukan kepada fungsi peribadi kami untuk digunakan di dalam fungsi pengganda, tetapi ia tidak boleh diakses di luar penutupan myPrivateMultiplyFunction . Jom cuba ini: 🎜
multiply(2,6) // => 12
myPrivateMultiplyFunction(2,6) // => ReferenceError: myPrivateMultiplyFunction is not defined
Salin selepas log masuk
Salin selepas log masuk

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


使用闭包进行元编程

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

var KeyMap = {
	"Enter":13,
	"Shift":16,
	"Tab":9,
	"LeftArrow":37
};
Salin selepas log masuk
Salin selepas log masuk

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

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);
	}
}
Salin selepas log masuk

捕捉瞬间

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

}
Salin selepas log masuk

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

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

var txtInput = document.getElementById('myTextInput');
txtInput.onkeypress = function(e) {
	if(KeyMap.isEnter(e)) {
		console.log(txtInput.value);
	}
}
Salin selepas log masuk

魔法从这里开始:

KeyMap["is" + key] = (function(compare){
	
})(KeyMap[key]); //invoke immediately and pass the current value at KeyMap[key]
Salin selepas log masuk

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

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

return function(ev) {
	var code = ev.keyCode || ev.which;
	return code === compare;
}
Salin selepas log masuk

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

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

Math.sin = Math.sin.cached();
Math.sin(1) // => 0.8414709848078965
Math.sin(1) // => 0.8414709848078965 this time pulled from cache
Salin selepas log masuk

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

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


野外关闭

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

jQuery

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

(function($){
	$(document).ready(function(){
		//business as usual....
	});
})(jQuery);
Salin selepas log masuk

骨干.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);
Salin selepas log masuk

结论

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

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

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

Atas ialah kandungan terperinci penutupan depan ke belakang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan