美文网首页
javaScript 基础巩固之闭包来实例化对象

javaScript 基础巩固之闭包来实例化对象

作者: 人话博客 | 来源:发表于2020-07-02 23:36 被阅读0次

    javaScript 基础巩固之闭包来实例化对象

    在 JavaScript 中,我们可以很简单的创建两个完全独立的对象,相互之间并且互不影响.

    方式一

    使用对象字面量

    const obj1 = {
        name: '李四'
    }
    
    const obj2 = {
        name: '王五'
    
    }
    
    obj1.name = '李四大爷'
    obj2.name = '王五大娘'
    
    

    obj1obj2 之间毫无关系,互不影响. 这点很好理解.


    方式二

    使用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 = '李四大娘'
    
    

    p1p2 之前也毫无影响. 以为它俩是独立的对象.

    我在这里用 new 调用,很简单,函数是我写的,我知道我写的是个构造函数,所以我就用new去调用了. 这函数要不是我写的,我很可能用普通函数模式 Person() 去调用,但也没设么关系,起码在这一行上程序也不会报错.


    方式三

    使用 闭包 模式.

    上述的方式一方式二都很方便,也很好用,很简单的创建两个独立的对象,并相互之间并不影响.

    但它们都有一个问题(严不严重看实际情况): 在外部,我都可以很简单的修改它们之前所定义的值

    p1.name  从 张三 -----> 张三大爷
    p2.name  从 李四 -----> 李四大娘
    
    

    有办法解决吗?

    答案是:

    可以用闭包模式,来完成独立的对象"实例化",并且私有化对象 属性.

    至于什么是闭包? 我不打算解释过多,但是可以稍微说一下,如果完全不了解啥是闭包的,建议先去看看闭包相关的文章.

    1. 首先闭包,一定是函数内部包含函数.(也就是函数定义的时候,有层级关系,至少是两层)
    2. 内部函数可以访问外部函数的变量.(函数的作用域链)
    3. 正常情况下,函数调用时,都有堆栈信息,里面存放了函数的参数,本地变量等信息(函数执行时创建的执行上下文 activeObject).当函数调用完毕时,这些个信息也一并都被销毁了.
    4. 但是如果存在闭包,且内层函数被外层引用,那么此函数的外层函数的参数,本地变量,都会被内部函数连着(装逼一点说法叫劫持),除非外部接受的函数被销毁,否则这些数据将一直存在.

    书接上文.

    对于JavaScript函数我们都知道的事情大致有:

    1. JavaScript 这门语言中只有函数作用域的.
    2. 函数在调用的过程中,都会创建一个执行上下文对象(如果以前不知道,那么现在就知道了)
    3. 这个执行上下文对象,就挂载了这个函数在执行过程中的入参, 本地变量等.(如果以前不知道,那么现在就知道了)
    4. 函数本身执行完毕之后,起执行上下文也会一并被销毁.

    来一段代码说一下:

    
    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() 
    
    

    除了 abcf 函数,外部肯本无法访问到p1,p2,p3,local1,loca2.

    1. 所以,闭包完成了属性的私有化(这个属性是函数执行上下文对象的属性)

    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 - 美编部
    
    

    我们使用闭包的模式:

    1. 创建了 p1p2 两个完全独立的对象.
    2. 私有化了对象的 name , age , address , salary, department 的属性

    最后总结:

    1. 函数在执行时,会创建一个上下文对象(activeObject)--(对象的独立性),这个对象挂载了和此函数相关的入参以及本地变量.
    2. 内部函数,可以访问外部函数的言外之音就是,内部函数可以访问外部函数由于调用所产生的执行上下文对象(函数的作用域链).(这样,就也产生了一个闭包)
    3. 函数本身作为返回值,在调用完毕之后,它自己的执行上下文就被销毁了,但是如果它本身一直存在(let f = ''''),那么它父亲(abc函数)的执行上下文就会一直存在,除非它自己也没了。
    4. 所以,我们可以利用内部返回这个函数,(如果函数较多)将内部函数挂载成对象的属性上,这样,只要挂载的这个内部函数对象不释放,我们就能无限制的访问闭包函数里作用于链上的所有数据(对内部读写,对外部只读,对象属性的私有化)

    内部函数只要能接收,能一直调用,那么它父亲的执行上下文在此期间就会一直存在!!

    相关文章

      网友评论

          本文标题:javaScript 基础巩固之闭包来实例化对象

          本文链接:https://www.haomeiwen.com/subject/izydqktx.html