JavaScript中闭包与原型的区别及闭包使用原因探讨
闭包 vs 原型:区别、关联与闭包的价值
Great question! 你写的两个标签生成函数确实能实现类似效果,但背后的逻辑完全不同——一个靠闭包锁住状态,另一个靠原型链共享方法。咱们结合你的代码一步步拆解:
先把你的代码格式化方便参考:
// Closure function function createLI(tags) { return function(...args) { if (args.length > 1) { return args.map((item) => { return "<" + tags + ">" + item + "</" + tags + ">" }) } else { return "<" + tags + ">" + args + "</" + tags + ">" } } } var tag = createLI('li'); console.log(tag('test')); // Prototype function let Newfunction = function(x) { this.content = x; } Newfunction.prototype.aswrapper = function(...args) { if (args.length > 1) { return args.map((item) => { return "<" + this.content + ">" + item + "</" + this.content + ">" }) } else { return "<" + this.content + ">" + args + "</" + this.content + ">" } } let heading = new Newfunction('h1'); console.log(heading.aswrapper('sometextprint'));
一、核心区别
1. 状态存储的本质
- 闭包:
createLI返回的内部函数,通过闭包“记住”了外部作用域的tags参数。这个状态是绑定在返回的函数实例上的——每个通过createLI创建的函数(比如tag)都有自己独立的tags值,互不干扰。
比如你再创建一个生成<p>标签的函数:const tagP = createLI('p'); console.log(tagP('test')); // <p>test</p> // tag和tagP的tags完全独立 - 原型:
Newfunction把状态(content)存在实例的this上,而aswrapper方法是挂在Newfunction.prototype上的——所有Newfunction的实例都会共享同一个原型方法,状态则是每个实例自己的。
比如:const headingH2 = new Newfunction('h2'); console.log(headingH2.aswrapper('text')); // <h2>text</h2> // heading和headingH2的aswrapper是同一个函数 console.log(heading.aswrapper === headingH2.aswrapper); // true
2. 内存与复用效率
- 闭包:每个闭包函数都会携带自己的外部作用域,创建大量闭包时会占用更多内存(因为每个都有独立的作用域链)。但好处是不用实例化对象,直接调用函数就行,写法更轻量化。
- 原型:原型上的方法是所有实例共享的,内存占用更高效——不管创建多少实例,方法只存在一份。但必须通过
new创建实例,更偏向面向对象的写法。
3. 语法风格与适用场景
- 闭包:属于函数式编程风格,适合创建“预配置”的函数(比如你的标签生成器),或者封装私有变量(外部无法直接修改
tags,只能通过返回的函数操作)。 - 原型:是JS面向对象编程的核心,适合创建多个具有相同行为但不同状态的对象,比如类的继承就是基于原型链实现的。
二、两者的关联
虽然看起来是不同机制,但在JS里它们是相辅相成的:
- 原型方法可以结合闭包:比如你可以在原型方法里创建闭包,用来保存一些不想暴露给外部的私有状态。
- 闭包可以模拟原型特性:比如用闭包创建多个共享逻辑的函数,但这样不如原型高效,因为每个函数都有独立的逻辑副本。
- 都依赖JS的作用域规则:闭包靠词法作用域查找变量,原型靠原型链查找方法——本质上都是JS查找标识符的规则延伸。
三、为什么要使用闭包?
闭包是JS里非常强大的特性,主要有这些核心用途:
- 封装私有变量:比如
createLI里的tags,外部代码无法直接修改它,只能通过返回的函数来使用,避免了全局污染和意外修改。 - 创建函数工厂:就像你写的标签生成器,通过闭包可以快速批量创建预配置好的函数(生成li、p、h1等标签),代码更简洁。
- 保存执行上下文状态:比如在定时器、事件监听里,闭包可以记住回调函数创建时的环境变量,避免变量被覆盖的经典问题。
- 实现模块化:在ES6模块出现之前,很多前端模块都是用闭包封装私有逻辑,只暴露需要的接口。
举个封装私有变量的经典例子:
function createCounter() { let count = 0; // 外部无法直接访问的私有变量 return { increment: () => count++, getCount: () => count } } const counter = createCounter(); counter.increment(); console.log(counter.getCount()); // 1 // 你没法直接修改count,只能通过提供的方法操作
内容的提问来源于stack exchange,提问作者pixellab
相关产品推荐
相关产品推荐

