javaScript 基础巩固之闭包来实例化对象
在 JavaScript 中,我们可以很简单的创建两个完全独立的对象,相互之间并且互不影响.
方式一
使用对象字面量
const obj1 = {
name: '李四'
}
const obj2 = {
name: '王五'
}
obj1.name = '李四大爷'
obj2.name = '王五大娘'
obj1
和 obj2
之间毫无关系,互不影响. 这点很好理解.
方式二
使用javascript
提供的构造函数.
补充: 在 JavaScript 中,函数可以理解成有两种模式.
第一种: 普通函数调用, someFunc() 就完事(
即使你里面写了什么 this.name=name这样的语句)
第二种: 构造函数调用, new SomeFunc () 就完事 (
即使你里面压根没有写什么鬼 this.name=name 这样的语句)
也就是说, JavaScript
的函数,你用 new
调用了,它就是构造函数. 你没用 new
调用,它就是普通函数.
怎么去用随你的便, javaScript
引擎才不管你内部函数写了些什么乱七八糟的玩意.
更多时候,你在里面写了 this.name = name
,你知道这是个构造函数,所以你就去用new
调用了,仅此而已
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.showInfo = function () {
console.log(this.name , this.age)
}
let p1 = new Person('张三', 22)
let p2 = new Person('李四', 33)
p1.name = '张三大爷'
p2.name = '李四大娘'
p1
和 p2
之前也毫无影响. 以为它俩是独立的对象.
我在这里用
new
调用,很简单,函数是我写的,我知道我写的是个构造函数,所以我就用new
去调用了. 这函数要不是我写的,我很可能用普通函数模式Person()
去调用,但也没设么关系,起码在这一行上程序也不会报错.
方式三
使用 闭包
模式.
上述的方式一
和方式二
都很方便,也很好用,很简单的创建两个独立的对象,并相互之间并不影响.
但它们都有一个问题(严不严重看实际情况): 在外部,我都可以很简单的修改它们之前所定义的值
p1.name 从 张三 -----> 张三大爷
p2.name 从 李四 -----> 李四大娘
有办法解决吗?
答案是: 有
可以用闭包模式,来完成独立的对象"实例化",并且私有化对象 属性.
至于什么是闭包? 我不打算解释过多,但是可以稍微说一下,如果完全不了解啥是闭包的,建议先去看看闭包相关的文章.
- 首先闭包,一定是函数内部包含函数.(也就是函数定义的时候,有层级关系,至少是两层)
- 内部函数可以访问外部函数的变量.(函数的作用域链)
- 正常情况下,函数调用时,都有堆栈信息,里面存放了函数的参数,本地变量等信息(函数执行时创建的执行上下文 activeObject).当函数调用完毕时,这些个信息也一并都被销毁了.
- 但是如果存在闭包,且内层函数被外层引用,那么此函数的外层函数的参数,本地变量,都会被内部函数连着(装逼一点说法叫劫持),除非外部接受的函数被销毁,否则这些数据将一直存在.
书接上文.
对于JavaScript函数我们都知道的事情大致有:
- JavaScript 这门语言中只有函数作用域的.
- 函数在调用的过程中,都会创建一个执行上下文对象(如果以前不知道,那么现在就知道了)
- 这个执行上下文对象,就挂载了这个函数在执行过程中的入参, 本地变量等.(如果以前不知道,那么现在就知道了)
- 函数本身执行完毕之后,起执行上下文也会一并被销毁.
来一段代码说一下:
function abc (p1, p2, p3) {
let local1 = 1, local2 = 2
// ... 其他逻辑
}
当我们调用时:
abc(1,2,3)
==> 函数在执行时会创建一个执行上下文
==> abcActiveObject {}
==> 执行上下文对象会挂载和当前函数有关的入参,本地变量
==> abcActiveObject {
p1:1,
p2:2,
p3:3,
local1:1,
local2:2
}
根据 javaScript
函数定义第一条: 只有函数作用域
, 表明了,这些个 p1,p2,p3,local1,local2
属性只能是函数 abc
自己来使用.
根据闭包定义第二条: 内部函数可以访问外部函数的数据
, 我们就可以知道了,c除了abc
函数自己使用外,如果在 abc
内部还有一个函数的话,那么这个内部函数是可以访问这些个 p1,p2,p3,local1,local2
了.
function abc (p1, p2, p3) {
let local1 = 1, local2 = 2
// ... 其他逻辑
function def () {
// 内部函数访问外部函数的变量
p1 = 10
p2 = 11
p3 = 22
local1 = 100
local2 = 44
}
}
看到了吗?
很自然的,这样就行成了一个闭包.
根据闭包定义的第四条,关键字 连着(劫持), 将代码修改为:
function abc (p1, p2, p3) {
let local1 = 1, local2 = 2
// ... 其他逻辑
return function def () {
// 内部函数访问外部函数的变量
p1 = 10
p2 = 11
p3 = 22
local1 = 100
local2 = 44
}
}
在调用
let f = abc(1,2,3)
f()
除了 abc
和 f
函数,外部肯本无法访问到p1,p2,p3,local1,loca2
.
- 所以,闭包完成了属性的私有化(这个属性是函数执行上下文对象的属性)
2.每一次函数的调用,都会产生一个独立的执行上下文(闭包完成了对象的独立性)(这个属性是函数执行上下文对象的属性)
根据JavaScript
函数大致定义第四条,上述代码虽然产生了闭包,但当f()
执行完毕之后, 但由于 f
函数一直存在,所以,它的父级 abc
函数的执行上下文也会一直存在。除非 f
函数本身不存在了。
只返回一个函数,我们很好接受,如果返回多个函数呢?
我们可以在内部,以对象属性的方式,才多个函数挂载成返回对象的属性。
大致结构如下:
连接f函数的玩意 ---> f 函数 (执行上下文) --> abc 函数(执行上下文)
所以,可以将代码修改为:
function abc (p1, p2, p3) {
let local1 = 1, local2 = 2
// ... 其他逻辑
//return function def () {
// 内部函数访问外部函数的变量
// p1 = 10
// p2 = 11
// p3 = 22
// local1 = 100
// local2 = 44
//}
function def () {
// 内部函数访问外部函数的变量
p1 = 10
p2 = 11
p3 = 22
local1 = 100
local2 = 44
}
return {
def,
//... 其他函数
}
}
我返回一个对象,让对象去挂载函数.只要挂载def
函数为属性的对象不会释放,那么f
函数作为它的属性就会一直存在了 ---> abc
的执行上下文也就会一直存在了 --> 对象也就一直存在了 (基于 abc
函数的执行上下文)
有时候,去控制一个对象本身的生命周期,比控制闭包内函数的声明周期要方便的多,也直观的多。
来个简单点的直白点的,像普通写构造函数那样数据的例子吧.
function outter(name, age, address) {
// 函数内部变量
let salary = 3000, department = '客服部'
function setName(namep) {
// 闭包,内部函数可以访问和修改外部函数的数据
name = namep
}
function setAge(agep) {
age = agep
}
function setAddress(addressp) {
address = addressp
}
function setSalary(salaryP) {
salary = salaryP
}
function setDepartment(departmentP) {
department = departmentP
}
function __showInfo() {
console.log(`${name} - ${age} - ${address} - ${salary} - ${department}`)
}
return {
setAge,
setAddress,
setName,
setSalary,
setDepartment,
__showInfo
}
}
// "实例化" 第一个对象
let p1 = outter('李四', 22, '湖北省武汉市洪山区') // ouuter 一执行,就创建了一个执行上下文对象 --> 对应独立性
p1.setName('李四大爷') // 除了内部函数,外部无法访问这个执行上下文的属性 ---> 闭包完成了 私有化
p1.setAge(25)
p1.setAddress('湖北省武汉市洪山区关山大道金桥名居1111号')
p1.setSalary(10000)
p1.setDepartment('开发部')
p1.__showInfo()
let p2 = outter('王五', 25, '湖北省武汉市洪山区')
p2.setName('王五真美')
p2.setAge(25)
p2.setAddress('湖北省武汉市洪山区关山大道金桥名居2222号')
p2.setSalary(3333)
p2.setDepartment('美编部')
p2.__showInfo()
运行结果:
李四大爷 - 30 - 湖北省武汉市洪山区关山大道金桥名居1111号 - 10000 - 开发部
王五真美 - 30 - 湖北省武汉市洪山区关山大道金桥名居2222号 - 3333 - 美编部
我们使用闭包的模式:
- 创建了
p1
和p2
两个完全独立的对象. - 私有化了对象的
name
,age
,address
,salary
,department
的属性
最后总结:
- 函数在执行时,会创建一个上下文对象(activeObject)--(对象的独立性),这个对象挂载了和此函数相关的入参以及本地变量.
- 内部函数,可以访问外部函数的言外之音就是,内部函数可以访问外部函数由于调用所产生的执行上下文对象(函数的作用域链).(这样,就也产生了一个闭包)
- 函数本身作为返回值,在调用完毕之后,它自己的执行上下文就被销毁了,但是如果它本身一直存在(
let f = ''''
),那么它父亲(abc
函数)的执行上下文就会一直存在,除非它自己也没了。 - 所以,我们可以利用内部返回这个函数,(如果函数较多)将内部函数挂载成对象的属性上,这样,只要挂载的这个内部函数对象不释放,我们就能无限制的访问闭包函数里作用于链上的所有数据(对内部读写,对外部只读,对象属性的私有化)
内部函数只要能接收,能一直调用,那么它父亲的执行上下文在此期间就会一直存在!!
网友评论