(javascript)(基础知识+实例) 21.proxy方法,闭包

判断对象是否为空

判断数组为空

判断一个对象里面是否有某个key

  • 可以直接访问这个key 如果得到的值为undefined代表没有 如果得到的值不为undefined代表有
  • in属性
    • 键名 in 对象名
    • 有这个键返回true
    • 没有这个键返回false
  • hasOwnProperty
    • 对象名.hasOwnProperty(键名)
    • 有这个键返回true
    • 没有这个键返回false

Object.defineProperty方法

  • 元编程:对于原生js里面的功能进行编程
  • 对于对象的功能进行编程(拦截)
  • Object.defineProperty(对象, 键名, 修饰属性)
  • 缺点:
    1. 如果数组或者对象里面的数据比较多的时候,需要遍历取给每一项数据添加get和set方法(性能差)
    2. 数组和对象后来添加的数据无法识别 不会触发set和get方法(无法支持后面新增的数据)

Proxy方法

  • 是以面向对象方式封装的
    代理的数据 = new Proxy(源数据, {
        get(){
            // 获取数据时候触发
        },
        set(){
            // 设置数据的时候触发
        }
    })

闭包

  • 什么是闭包

  • 闭包在开发中的使用

  • 闭包有哪些优缺点

  • 函数

    1. 含义:将一段代码以命名的方式存储到一个盒子里面,在要执行这段代码的时候,需要根据这个名称找到这个盒子,将代码取出来运行
    2. 两个阶段
      • 定义阶段
      • 调用阶段
    • 函数在定义阶段做的事件

          function 函数名(){
              // 一段代码
              var a = 1
              console.log(1)
              console.log(2)
          }
      
      • 当代码读取到function的时候会在内存里面开辟一块空间,得到一个空间地址
      • 当我们写一个函数名的时候,会把这个空间地址赋值给函数名
      • 当我们在花括号里面写一段代码,会把这一段代码转换成字符串存储到上面开辟的空间里面
      • 注意事项
        1. 函数在定义的时候函数体内的代码不会被执行
        2. 函数名存储的是一个地址,两个一模一样的函数肯定不相等除非函数名一样
    • 函数的调用阶段

      • 函数名()
      • 当js读取到函数的时候,会根据函数名存储的地址找到存的代码
      • 当js读取到小括号,会将存储的字符串代码转换成js,然后执行
    • 函数的执行空间

      • 函数在调用的时候会开辟一块空间 用于存储函数内部的定义的属性和方法,这个开辟的空间被称为函数的执行空间
      • 函数调用结束之后,执行空间就会被自动销毁
  • 闭包

    • 含义:访问一个函数内部的局部变量
    • 创建一个不销毁的执行空间?
      1. 当函数调用返回一个引用的数据类型
      2. 函数外部有一个变量使用着函数的返回值
    • 实现
      1. 一个函数A里面有一个变量a,在函数A里面返回一个函数B
      2. 在函数A的外面有一个变量data使用着函数A的返回值,这个data其实就是函数B
      3. 调用data其实就是调用函数B,B里面用了一个变量a

实例

(以下代码均为课程实例)

(1)判断数组是否为空

    <script>
        var arr = [1,2,3]
        var arr01 = [,,]
        var arr02 = new Array(10)
        var arr03 = []
        console.log(arr)
        console.log(arr03)
        console.log(arr01 === [])
        console.log(arr03 === []) // 因为他们不是同一个地址
        // 判断数组为空一般根据数组的长度
        console.log(arr01.length === 0)
        console.log(arr03.length === 0)
    </script>

(2)判断对象是否为空

    <script>
        var obj = {
            a:1,
            b:2,
            c:3
        }
        var obj02 = {}
        var obj03 = new Object()
        // 对象里面没有length属性
        console.log(obj)
        console.log(obj02)
        console.log(obj02 === {}) // 不是同一个地址 即使为空也是不相等
        console.log(obj.length === 0)
        console.log(obj02.length === 0)

        // 把对象转换成json字符串
        console.log(JSON.stringify(obj) === '{}')
        console.log(JSON.stringify(obj02) === '{}')
        console.log(JSON.stringify(obj03) === '{}') 
        // console.log('{}' == '{}')
        // 利用for..in
        for(let key in obj){
            // 如果对象有键值对 它至少会进来一次
            console.log(1)
        }
        for(let key in obj02){
            // 如果是一个空对象 一次都不会进来
            console.log(2)
        }
        /* 
            封装一个判断对象是否为空的方法
        */
       function isEmptyObject(data){
            let isEmpty = true // 默认为true
            for(let key in data){
                isEmpty = false
            }
            // 需要返回值
            return isEmpty
       }
       console.log(isEmptyObject(obj)) // false
       console.log(isEmptyObject(obj02)) // true
       // 利用Object.keys()  es6
       console.log(Object.keys(obj))
       console.log(Object.keys(obj02))
       function isEmptyObject02(data){
           return Object.keys(data).length === 0
       }
       console.log(isEmptyObject02(obj)) // false
       console.log(isEmptyObject02(obj02)) // true
       // 利用Object.getOwnPropertyNames() es5
       console.log(Object.getOwnPropertyNames(obj))
       console.log(Object.getOwnPropertyNames(obj02))
    </script>

(3)判断对象里面是否含有某个键值对

    <script>
        var person = {
            username: 'jack',
            age: 18,
            gender: '男'
        }
        var person02 = {
            username1: 'jack',
            age: 18,
            gender: '男'
        }
        var person03 = {
            username2: 'jack',
            age: 18,
            gender: '男',
            score: undefined
        }
        /* 
            封装一个方法,判断对象里面是否有username
            访问对象里面有这个key 返回的就是对应的value
            访问对象里面一个不存在的key 返回的是undefined
        */
       console.log(person.username)
       console.log(person02.username)
       function hasKey(obj,key){
            // 获取这个对象的value 判断这个value是否为undefined
            // 如果是undefined 代表对象里面没有这个键
            // 如果不是undefined 代表对象里面有这个键
            return obj[key] !== undefined
       }
       console.log(hasKey(person, 'username')) // true
       console.log(hasKey(person02, 'username')) // false
       console.log(hasKey(person03, 'username')) // false
       console.log(hasKey(person03, 'age'))
       console.log(hasKey(person03, 'score')) // false


       // in属性
       // 一个对象可以使用in来检测是否有这个键名
       // 如果对象有这个键返回true
       // 如果对象没有这个键返回false
       // 用法 键名 in 对象
       console.log('username' in person) // person有没有username这个键名
       console.log('score' in person) // person有没有score这个键名
       function hasKey(obj,key){
            return key in obj
       }
       console.log(hasKey(person, 'username')) // true
       console.log(hasKey(person02, 'username')) // false
       console.log(hasKey(person03, 'username')) // false
       console.log(hasKey(person03, 'age'))
       console.log(hasKey(person03, 'score')) // true

       // hasOwnProperty
       console.log(person.hasOwnProperty('username'))
       console.log(person.hasOwnProperty('score'))
       function hasKey(obj,key){
            return obj.hasOwnProperty(key)
       }
    </script>

(4)Object.defineProperty

    <script>
        var person = {
            username: 'jack',
            age: 18,
            gender: '男'
        }
        // 对于对象person里面的username进行修改
        Object.defineProperty(person, 'username', {
            // value: '哈哈哈', // 对于person里面username的值进行了修改
            // writable: false, // person里面的username不允许被修改了
            // enumerable: false, // person里面username不可以被遍历
            // configurable: false // person里面的username不可以删除
            // get: function(){
            //     // 该方法在对象访问username的时候会触发一次
            //     console.log('get方法触发了')
            //     // 需要有一个返回值 这个返回值就是获取的value
            //     // 如果没有返回值 得到的value是undefined
            //     return 'rose'
            // },
            set(newVal){
                // 该方法是在设置username的时候会触发一次
                // 该方法会有一个形参 这个形参就是要设置的值
                console.log('set方法触发了')
                console.log(newVal)
            }
        })
        console.log(person.username)
        person.username = '哈哈哈'
        console.log(person.username)

        // delete person.username
        // console.log(person)
        // person.username = 'rose'
        // console.log(person.username)
        // for(let key in person){
        //     console.log(key)
        //     console.log(person[key])
        // }
        /* // 访问
        console.log(person.username) // 访问对象里面某个value
        // 遍历
        for(let key in person){
            console.log(key)
            console.log(person[key])
        }
        // 修改
        person.username = 'rose'
        console.log(person.username)
        // 删除
        delete person.gender
        console.log(person)  */
    </script>

(5)Object.defineProperty的get和set应用

 <script>
        var person = {
            username: 'jack',
            age: 18,
            gender: '男'
        }
        /* 利用Object.defineProperty保证对象正常使用的同时还可以知道对象的值什么时候被获取,什么时候被设置 */
        var tmp = person.username
        Object.defineProperty(person, 'username', {
            get: function(){
                // 获取person的username时候触发
                console.log('get触发了')
                // 此时返回值又获取了username 又会调用get方法 这时候会出现死递归
                // return person.username
                return tmp
            },
            set: function(newVal){
                console.log('set触发了')
                console.log(newVal)
                tmp = newVal
            }
        })
        console.log(person.username)
        person.username = 'rose'
        console.log(person.username)
    </script>

(6)双向数据绑定原理1

<body>
    <div id="app"></div>
    <div class="red"></div>
    <script>
      var person = {
        age: 18,
      };
      // 把数据显示到页面
      document.getElementById("app").innerHTML = person.age;
      document.querySelector(".red").innerHTML = person.age;
      // 修改数据
      setInterval(() => {
        person.age++;
        // 每一次数据修改都需要重新渲染页面
        document.getElementById("app").innerHTML = person.age;
        document.querySelector(".red").innerHTML = person.age;
      }, 1000);
    </script>
  </body>

(6)双向数据绑定原理2

<body>
    <div id="app"></div>
    <div class="red"></div>
    <script>
        /* 
            双向数据绑定: 
            单向: 数据改变,视图自动更新把一个数据显示到页面  数据变化了 页面会自动更新
            利用的是Object.defineproperty的set方法
        */
      var person = {
        age: 18,
      };
      var tmp = person.age
      Object.defineProperty(person, 'age', {
        get:function(){
          console.log('get被触发')
          return tmp
        },
        set: function(newVal){
          console.log('set被触发')
          console.log(newVal)
          // 修改person里面的age
          tmp = newVal
          // 除了改变数据之外还要去渲染视图
          document.getElementById("app").innerHTML = newVal;
          document.querySelector(".red").innerHTML = newVal;
        }
      })
      // 把数据显示到页面
      document.getElementById("app").innerHTML = person.age;
      document.querySelector(".red").innerHTML = person.age;
      // 对于person的age进行拦截 知道它什么时候被设置 什么时候被获取
      // 修改数据
      setInterval(() => {
        // 只需要修改数据 无需再次更新页面
        person.age = Math.random();
      }, 1000);

      person.age++
    </script>
  </body>

(6)双向数据绑定原理3

<body>
    <div id="app"></div>
    <div class="red"></div>
    <input type="text" id="input" />
    <button id="change">改变</button>
    <script>
      /* 
            双向数据绑定: 
            数据改变,视图自动更新:把一个数据显示到页面  数据变化了 页面会自动更新
            利用的是Object.defineproperty的set方法
            视图更新 数据也会更新
        */
      var person = {
        age: 18,
      };
      var tmp = person.age;
      Object.defineProperty(person, "age", {
        get: function () {
          console.log("get被触发");
          return tmp;
        },
        set: function (newVal) {
          console.log("set被触发");
          console.log(newVal);
          // 修改person里面的age
          tmp = newVal;
          // 除了改变数据之外还要去渲染视图
          document.getElementById("app").innerHTML = newVal;
          document.querySelector(".red").innerHTML = newVal;
          document.getElementById("input").value = newVal;
        },
      });
      // 把数据显示到页面
      document.getElementById("app").innerHTML = person.age;
      document.querySelector(".red").innerHTML = person.age;
      document.getElementById("input").value = person.age;
      // 对于person的age进行拦截 知道它什么时候被设置 什么时候被获取
      // 修改数据
      // setInterval(() => {
      //   // 只需要修改数据 无需再次更新页面
      //   person.age = Math.random();
      // }, 1000);
      document.querySelector("#change").onclick = function () {
        person.age++;
      };

      /* 视图变化数据更新 */
      document.getElementById("input").oninput = function(){
        // 获取到它的value值 赋值给person的age
        person.age = this.value
      }
    </script>
  </body>

(7)Vue使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <span>{{ message }}</span>
      <input type="text" v-model="message">
      <button @click="changeMessage">添加</button>
    </div>
    <script>
      var app = new Vue({
        el: "#app",
        data: {
          message: "Hello Vue!",
        },
        methods: {
            changeMessage(){
                this.message = '哈哈哈'
            }
        },
      });
    </script>
  </body>
</html>

(8)Object.defineProperty的缺点

<body>
    <ul>

    </ul>
    <script>
        var person = {
            username: 'jack',
            age: 17,
            gender: '男',
            score: 100
        }
        // 取出对象的所有的key 给每个key都添加get和set
        for(let key in person){
            Object.defineProperty(person, key, {
                get(){
                    console.log('get触发了')
                },
                set(){
                    console.log('set被触发了')
                }
            })
        }
        console.log(person.score)
        person.gender = '女'
        // 无法支持新增的数据
        person.aa = 1
        var list = ['猪八戒', '孙悟空', '唐三藏', '沙和尚']
        for(let i = 0;i<list.length;i++){
            let li = document.createElement('li')
            li.innerText = list[i]
            document.querySelector('ul').appendChild(li)
            Object.defineProperty(list, i, {
                get(){
                    console.log('数组的get')
                },
                set(){
                    console.log('数组的set')
                }
            })
        }

        list[0] = '天蓬元帅'
        // 无法支持新增的数据
        list.push('白骨精')
        list[list.length-1] = '咕咕'
    </script>
</body>

(9)proxy方法

<body>
    <script>
        var person = {
            username: 'jack',
            age: 18,
            gender: '男'
        }
        // 返回一个代理后的数据
        var proxyPerson = new Proxy(person, {
            get(){
                console.log('get触发了')
            },
            set(){
                console.log('set触发了')
            }
        })
        console.log(proxyPerson)
        console.log(person == proxyPerson)
        // 只要修改和访问这个代理的数据 就会触发set和get
        // proxyPerson.username
        // proxyPerson.age
        // proxyPerson.gender
        // proxyPerson.username = 'rose'
        // proxyPerson.age = 19
        // proxyPerson.gender = '女'

        // 新增一个数据
        person.score = 100

        proxyPerson.score
        proxyPerson.score = 200
        console.log(person)
        console.log(proxyPerson)

        // 代理数组
        var list = ['猪八戒', '孙悟空', '唐三藏', '沙和尚']

        var proxyList = new Proxy(list, {
            get(){
                console.log('数组get触发了')
            },
            set(){
                console.log('数组set触发了')
            }
        })

        console.log(proxyList[0])
        proxyList[1] = '哈哈哈'

        // 新增一个数据
        list.push('李逵')

        console.log(proxyList[proxyList.length-1])
        proxyList[proxyList.length-1] = '葵葵'
    </script>
</body>

(10)proxy语法

    <script>
        var person = {
            username: 'jack',
            age: 18,
            gender: '男'
        }
        // 返回一个代理后的数据
        var proxyPerson = new Proxy(person, {
            // get方法有
            get(target, key, receiver){
                // 需要有一个返回值 这个返回就是得到的属性值
                console.log('get触发了')
                // console.log(target) // 原数组
                // console.log(key) // 访问的key
                // console.log(receiver) // 代理后的数据
                return target[key] // 取出源数据的属性值返回
            },
            set(target, key, newVal, receiver){
                console.log('set触发了')
                // 设置的时候要设置源数据
                target[key] = newVal
            }
        })
        console.log(proxyPerson.username) // 
        console.log(proxyPerson.age) // 

        proxyPerson.age = 20
        console.log(proxyPerson.age)
    </script>

(11)双向数据绑定

<body>
    <div id="app">
        <p class="username"></p>
        <p class="age"></p>
        <p class="gender"></p>
        <input type="text" id="input">
        <button onclick="click()">按钮</button>
    </div>
    <script>
        var person = {
            username: 'jack',
            age: 18,
            gender: '男'
        }
        let proxyPerson = new Proxy(person, {
            get(target,key,receiver){
                return target[key]
            },
            set(target,key,val,receiver){
                console.log('set触发了')
                target[key] = val
                // 修改数据的时候  更新视图
                document.querySelector('.username').innerHTML = person.username
                document.querySelector('.age').innerHTML = person.age
                document.querySelector('.gender').innerHTML = person.gender
                document.getElementById('input').value = person.age
            }
        })
        // 第一次渲染视图
        document.querySelector('.username').innerHTML = person.username
        document.querySelector('.age').innerHTML = person.age
        document.querySelector('.gender').innerHTML = person.gender
        document.getElementById('input').value = person.age
        // 单向数据绑定 数据变化 视图会自动更新
       
        function click(){
            proxyPerson.username = 'rose'
            proxyPerson.age = Math.random()
        }

        // 视图更新 数据也会变化
        document.getElementById('input').oninput = change
        function change(){
            console.log('input...')
            console.log(this)
            proxyPerson.age = this.value
        }
    </script>
</body>

(12)函数定义阶段

<body>
    <div class="box">
        Lorem ipsum dolor, sit amet consectetur adipisicing elit. Rem amet, praesentium recusandae aut, repellendus vel doloribus laudantium qui reprehenderit saepe eius odit corrupti fugit labore quo, autem enim. Consequatur, laboriosam.
    </div>
    <script>
        // 函数在定义的时候 函数体内不会执行
        function getSum(){
            var a = 1
            console.log(a)
            console.log(2)
            console.log(3)
        }

        function fn(){}
        function foo(){}
        // fn和foo存储都是地址
        console.log(fn == foo)
        var box = document.querySelector('.box')
        function doClick(){
            console.log(1)
        }
        box.addEventListener('click', doClick)
        box.removeEventListener('click', doClick)
    </script>
</body>

(13)函数调用阶段

<body>
    <div class="box">
        Lorem ipsum dolor, sit amet consectetur adipisicing elit. Rem amet, praesentium recusandae aut, repellendus vel doloribus laudantium qui reprehenderit saepe eius odit corrupti fugit labore quo, autem enim. Consequatur, laboriosam.
    </div>
    <script>
        // 函数在定义的时候 函数体内不会执行
        function getSum(){
            var a = 1
            function fn(){
                console.log('fn被调用')
            }
            console.log(a)
            console.log(2)
            console.log(3)
            fn() 
        }
        // 函数的调用阶段
        // 会将之前存储的字符串代码 转换成 js代码执行
        // 函数内的定义变量 定义方法需要有一个地方进行存储
        // 函数在调用的时候会开辟一块空间 用于存储函数内部的定义的属性和方法,这个开辟的空间被称为函数的执行空间
        // 这个执行空间在我们函数调用结束之后就会自动销毁
        getSum()  // 在getSum调用的时候会开辟一块执行空间
        // getSum调用结束 调用结束执行空间就会自动销毁
    </script>
</body>

(14)闭包

    <script>
        function foo(){
            var a = 1
            // 闭包返回的是一个函数
            // 函数是引用的数据类型
            return function A(){
                console.log(a)
                a++
            }
        }
        var data = foo() // 会生成一个执行空间  在执行空间内部存储a
        // foo调用结束之后 执行空间被销毁了 a就消失了
        // 闭包是实现在foo外面访问foo内部的a

        // data就是函数foo返回的函数A
        console.log(data)
        // 调用data其实就是调用A A函数内的代码就会执行  A函数用了一个变量a本身没有 就会自动往上一级作用域查找
        // 上一级作用域foo 里面有一个变量a 用的就是foo里面的a
        data()
        data()
        data()
        data()

        function A(){
            var a = 1
            return function B(){
                console.log(a)
            }
        }

        var data = A()

        data()
    </script>

(15)创建不销毁的执行空间

    <script>
        // 当函数调用返回一个引用的数据类型
        function foo(){
            var obj = {
                a:1
            }
            return obj
        }
        foo()
        // 函数外部有一个变量使用着函数的返回值
        var data = foo()
        // 在foo调用结束之后 使用data  data就是函数foo的返回值 其实就是obj  obj存储在函数foo的执行空间里面
        // 能用data.a 就说明函数foo在调用之后执行空间并没有被销毁
        console.log(data.a)
    </script>

(16)闭包作用

    <script>
        // 闭包是为了避免全局变量污染的问题
        // 有一个变量需要在函数外部使用 但是如果把这个变量放在全局 有可能被别人覆盖
        
        function foo(){
            var a = 1
            return function(){
                console.log(a)
                a++
            }
          
        }
        var data = foo()
        data()
        // 污染了a
        var a = '哈哈哈'
        data()
        data()
    </script>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不二哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值