JS中的Object、Proxy、Reflect以及Promise对象

一、创建对象的方式

  • 字面量

    let a = {}
  • new关键字

    let b = new Object()
  • Object.create()方法

    let c = Object.create(Object.prototype)

二、function、Function、prototype、constructor理解

  • function与Function

    • 通过function声明的函数,实际上都是通过Function实例化出来的,即每个js函数都是Function对象

      • functin test(){}    <=>    var test = new Function()
    • new Function()创建函数

      • 如果有参数,依次传入参数名字符串,最后传入函数体字符串
        // 函数创建方式一:通过function声明
        function test1(a, b) {
            console.log(a + b)
        }
        
        // 函数创建方式二:通过new Function(),或者省掉new关键字,直接Function()
        
        // 参数分开写
        var test2 = new Function('a', 'b', 'console.log(a+b)')
        
        // 参数合在一起写
        var test3 = Function('a, b', 'console.log(a+b)')
        test1(1, 2) // 3
        test2(3, 5) // 8
        test3(4, 6) // 10
        console.log(test1.constructor === Function); // true  => 说明每个js函数都是Function对象
  • prototype属性与constructor属性

    • prototype是所有函数对象所拥有的一个特殊属性,普通对象没有
      • 注意fun.__proto__ != fun.prototype,因为fun.__proto__ === Function.prototype,这样就方便理解了
    • 实例对象的constructor属性,返回该实例对象的构造函数,注意不是字符串

三、原型链

  • 代码理解

    // 1、原型      =>     类 或 构造函数
    // 2、原型对象   =>    对象
    // 3、实例对象   =>    创建的实例
    // 4、相互转化
    
    // 原型
    class Person {
    
    }
    console.log('原型:', Person) // 原型: [class Person]
    
    // 原型对象添加属性、方法
    Person.prototype.username = '默认用户'
    Person.prototype.password = '默认密码'
    Person.prototype.login = function login(username, password) {
        console.log(`${username}登录成功!`)
    }
    console.log('原型对象:', Person.prototype) // 原型对象: { username: '默认用户', password: '默认密码', login: [Function: login] }
    
    // 原型对象 转 原型
    console.log(Person.prototype.constructor === Person) // true
    
    // 创建实例对象
    const person = new Person();
    person.login('小李', '123456') // 小李登录成功!
    const person2 = new Person.prototype.constructor()
    console.log(person2.username) // 默认用户
    
    // 实例对象 转 原型对象
    console.log(person.__proto__ === Person.prototype) // true
    console.log(Object.getPrototypeOf(person) === Person.prototype) // true
    
    // 实例对象到原型
    console.log(person.__proto__.constructor === Person) // true => 可以用来检验某对象是否是该类的实例对象

四、Object对象

  • 常用方法

    • Object.create():创建对象
    • Object.is():判断两个对象是否是同一个对象
    • Object.hasOwn():判断某个对象自身是否包含某个属性
    • Object.getOwnPropertyDescriptor():获取指定对象某个自有属性描述符
    • Object.getOwnPropertyDescriptors():获取指定对象所有自有属性描述符
    • Object.getPrototypeOf():获取实例对象的原型对象
    • Object.setPrototypeOf():设置某个实例对象的原型对象
    • Object.getOwnPropertyNames():获取某个对象所有自身属性
    • Object.getOwnPropertySymbols():获取某个对象所有自身符号属性
    • Object.defineProperty():给对象定义或修改属性(存在局限性)
      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport"
                content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>测试</title>
          <script>
              // 1、创建对象
              let a = Object.create(Document.prototype)
              console.log(a.__proto__.constructor === Document) // true
      
              // 2、判断两个对象是否是同一个对象
              // window、self、top、parent也是一个补环境检测点
              console.log(Object.is(window, self)) // true
              console.log(Object.is(window, top)) // true
              console.log(Object.is(window, parent)) // true
              console.log(Object.is(window, self.top.parent)) // true
      
              // 3、判断对象自身是否含有某个属性
              console.log(Object.hasOwn(document, 'location')) // true
              console.log(Object.hasOwn(document, 'cookie')) // false
      
              // 4、获取原型对象
              console.log(Object.getPrototypeOf(document)) // HTMLDocument {Symbol(Symbol.toStringTag): 'HTMLDocument', onreadystatechange: undefined, onmouseenter: undefined, onmouseleave: undefined, constructor: ƒ}
      
              // 5、获取指定对象的指定属性描述符
              console.log(Object.getOwnPropertyDescriptor(document, 'location')) // {enumerable: true, configurable: false, get: ƒ, set: ƒ}
      
              // 6、获取指定对象所有自有属性描述符
              console.log(Object.getOwnPropertyDescriptors(document)) // {location: {…}}
      
              // 7、设置原型对象
              let obj = {}
              Object.setPrototypeOf(obj, Document.prototype)
              console.log(obj.__proto__) // Document {…}
      
              // 8、获取自身所有属性
              console.log(Object.getOwnPropertyNames(document)) // ['location']
      
              // 9、获取自身所有符号属性
              console.log(Object.getOwnPropertySymbols(Document.prototype)) // [Symbol(Symbol.toStringTag), Symbol(Symbol.unscopables)]
          </script>
      </head>
      <body>
      </body>
      </html>
  • Object.defineProperty()方法及属性描述符

    // 1、初始化对象时定义属性
    let user = {
        name: '小李'
    }
    
    // 2、添加属性
    user.age = 28
    
    // 3、Object.defineProperty()
    // 第一个参数是对象,第二个参数是属性,第三个参数是一个属性描述符(对象)
    Object.defineProperty(user, 'height', {
        enumerable: true, // 是否可遍历,设为false,则遍历时不会显示该属性
        configurable: true, // 是否可配置,设为false,则后续继续调用Object.defineProperty定义该属性时会报错
        writable: true, // 是否可写,设为false,后续修改该属性不会生效
        value: 175 // 设置属性具体值
    })
    
    // 4、属性描述符中,(writable、value)和(get、set)不能同时使用
    // 属性描述符第二种写法(推荐)
    let tmpWeight = 120
    Object.defineProperty(user, 'weight', {
        enumerable: true,
        configurable: true,
        get: function (){
            console.log('正在获取属性')
            return tmpWeight
        },
        set: function (value){
            console.log('正在设置属性')
            tmpWeight = value
        }
    })
    user.weight = 130 // 设置属性
    console.log(user.weight) // 获取属性
  • 判断对象类型

    • typeof
    • Object.prototype.toString.call() -- 推荐
      // 1、typeof
      console.log(typeof '1') // string
      console.log(typeof 1) // number
      console.log(typeof true) // boolean
      console.log(typeof {}) // object
      console.log(typeof []) // object 不容易区分
      console.log(typeof null) // object 不容易区分
      console.log(typeof undefined) // undefined
      console.log(typeof Symbol()) // symbol
      
      // 2、Object.prototype.toString.call
      console.log(Object.prototype.toString.call('1')) // [object String]
      console.log(Object.prototype.toString.call(1)) // [object Number]
      console.log(Object.prototype.toString.call(true)) // [object Boolean]
      console.log(Object.prototype.toString.call({})) // [object Object]
      console.log(Object.prototype.toString.call([])) // [object Array]
      console.log(Object.prototype.toString.call(null)) // [object Null]
      console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
      console.log(Object.prototype.toString.call(Symbol())) // [object Symbol]

五、Reflect对象

  • 简介

    • Reflect对象时ES6为了操作对象,而提供的新API
    • 把Object对象的一些方法放到Reflect对象上
    • 修改某些Object对象的返回值,让其变得合理,比如defineProperty方法在无法定义属性时不会报错,而是返回false
    • 让Object操作都变成函数行为,比如in和delete操作,变成了 Reflect.has(obj, name) 和 Reflect.delete(obj, name)
  • apply()

    • Function.prototype.apply(thisArg, args)
      // Reflect.apply(target, thisArg, args)
      const obj = {
          age: 28
      }
      function fn(a, b, c){
          return this.age + a + b + c
      }
      // 旧写法
      console.log(fn.apply(obj, [1, 2, 3])) // 34
      
      // 新写法
      console.log(Reflect.apply(fn, obj, [1, 2, 3])) // 34
  • construct()

    • new 构造函数()
      // Reflect.construct(target, argArray, newTarget)
      class Person{
          constructor(name, age) {
              this.name = name
              this.age = age
          }
      }
      // 旧写法
      const p1 = new Person('小李', 28)
      console.log(p1) // Person { name: '小李', age: 28 }
      
      // 新写法
      const p2 = Reflect.construct(Person, ['小王', 30])
      console.log(p2) // Person { name: '小王', age: 30 }
  • get()

    • obj.prop
      // Reflect.get(target, prop, receiver)
      const obj = {
          name: '刘德华',
          age: 40,
          get info(){
              return `${this.name}:${this.age}岁`
          }
      }
      console.log(Reflect.get(obj, 'name')) // 刘德华
      console.log(Reflect.get(obj, 'age')) // 40
      console.log(Reflect.get(obj, 'info')) // 刘德华:40岁
      // 访问对象中没有的属性,返回undefined
      console.log(Reflect.get(obj, 'height')) // undefined
      
      // receiver参数能够改变get属性访问器中的this指向
      const objNew = {
          name: '张学友',
          age: 38
      }
      console.log(Reflect.get(obj, 'info', objNew)) // 张学友:38岁
  • set()

    • obj.prop = value
      // Reflect.set(target, prop, value, receiver)
      const obj = {
          name: '刘德华',
          set info(value){
              this.name = value
          }
      }
      
      console.log(Reflect.set(obj, 'info', '周润发')) // true
      console.log(obj.name) // 周润发
      
      const objNew = {
          name: '张学友'
      }
      
      // receiver参数能改变set属性访问器中的this指向
      if(Reflect.set(obj, 'info', '吴彦祖', objNew)){
          console.log(obj.name) // 周润发
          console.log(objNew.name) // 吴彦祖
      }
  • has()

    • prop in obj
      // Reflect.has(target, prop)
      const obj = {
          name: '刘德华',
          age: 40
      }
      // 旧写法
      console.log('name' in obj) // true
      console.log('gender' in obj) // false
      
      // 新写法
      console.log(Reflect.has(obj, 'name')) // true
      console.log(Reflect.has(obj, 'gender')) // false
  • deleteProperty()

    • delete obj.prop
      // Reflect.deleteProperty(target, prop)
      const obj = {
          name: '刘德华',
          age: 40
      }
      // 旧写法
      // delete obj.age
      
      // 新写法
      // 属性能删除则返回true,不能删除则返回false,被删除的属性依然存在
      console.log(Reflect.deleteProperty(obj, 'age')) // true
      console.log(obj) // { name: '刘德华' }
      
      Reflect.defineProperty(obj, 'gender', {
          value: '男',
          configurable: false, // 不可配置,所以该属性无法被删除
          enumerable: true,
          writable: true
      })
      console.log(obj) // { name: '刘德华', gender: '男' }
      console.log(Reflect.deleteProperty(obj, 'gender')) // false
      console.log(obj) // { name: '刘德华', gender: '男' }
  • 其它方法对照

    // 新写法
    // Reflect.defineProperty(target, prop, descriptor)
    // Reflect.getPrototypeOf(target)
    // Reflect.setPrototypeOf(target, prototype)
    // Reflect.isExtensible(target)
    // Reflect.preventExtensions(target)
    // Reflect.ownKeys(target)
    
    // 旧写法
    // Object.defineProperty(target, prop, descriptor)
    // Object.getPrototypeOf(target)
    // Object.setPrototypeOf(target, prototype)
    // Object.isExtensible(target)
    // Object.preventExtensions(target)
    // Object.getOwnPropertyNames(target) + Object.getOwnPropertySymbols(target)

六、Proxy对象

  • 简介

    • Proxy 对象是ES6新出的一个特性,用于创建一个对象的代理,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
    • Object.defineProperty只能监听对象的属性,不能监听数组,而Proxy监听的是对象,包括数组
    • 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
      var proxy = new Proxy(target, handler);
      /*
          target: 表示所要代理的目标对象
          handler:是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。
      */
  • handler中的13种拦截方法

    /*
    has(target, prop):   Reflect.has(target, prop)方法的捕捉器。
    get(target, prop, receiver):   Reflect.get(target, prop, receiver)方法的捕捉器。
    set(target, prop, value, receiver):   Reflect.set(target, prop, value, receiver)方法的捕捉器。
    deleteProperty(target, prop):   Reflect.deleteProperty(target, prop)方法的捕捉器。
    apply(target, thisArg, argArray):   Reflect.apply(target, thisArg, argArray)方法的捕捉器。
    construct(target, argArray, newTarget):   Reflect.construct(target, argArray, newTarget)方法的捕捉器。
    ownKeys(target):   Reflect.ownKeys(target)方法的捕捉器。
    getPrototypeOf(target):   Object.getPrototypeOf(target)方法的捕捉器。
    setPrototypeOf(target, prototype):   Object.setPrototypeOf(target, prototype)方法的捕捉器。
    isExtensible(target):   Object.isExtensible(target)方法的捕捉器。
    preventExtensions(target):   Object.preventExtensions(target)方法的捕捉器。
    getOwnPropertyDescriptor(target, prop):   Object.getOwnPropertyDescriptor(target, prop)方法的捕捉器。
    defineProperty(target, prop, descriptor):   Object.defineProperty(target, prop, descriptor)方法的捕捉器。
     */
  • 示例

    • set()和get()方法

      const person = {
          name: '周杰伦',
          phone: '18888888888'
      }
      
      let personHandler = {
          // 拦截属性的获取
          get(target, prop, receiver){
              // console.log(receiver)
              // console.log(`正在获取属性:${prop}`)
              if(prop === 'phone'){
                  return '经纪人电话: 13066666666'
              }
              return Reflect.get(target, prop, receiver)
          },
          // 拦截属性的设置
          set(target, prop, value, receiver){
              // console.log(`正在设置属性:${prop}`)
              if(prop === 'gender'){
                  console.log('无法设置gender属性')
              }else{
                  return Reflect.set(target, prop, value, receiver)
              }
          }
      }
      // 创建代理对象,第一个参数是被代理的对象,第二个参数是处理对象
      const proxy = new Proxy(person, personHandler)
      
      // 通过代理获取对象的属性
      console.log(proxy.name) // 周杰伦
      console.log(proxy.phone) // 经纪人电话: 13066666666
      
      // 通过代理设置对象的属性
      proxy.gender = '男'
      proxy.age = 30
      
      // gender属性无法设置
      console.log(person.gender) // undefined
      console.log(person.age) // 30
    • apply():拦截函数对象调用

      // 原函数
      function sum(a, b){
          return a + b
      }
      //拦截sum函数调用
      let proxy_sum = new Proxy(sum,{
          apply(target, thisArg, argArray) {
              console.log('参数:' + argArray)
              return Reflect.apply(target, thisArg, argArray)
          }
      })
      console.log(proxy_sum(10, 20)); // 30
    • construct():拦截new操作

      /*
      function Person(name,age){
          this.name = name;
          this.age = age;
          this.say = function (){
              console.log('姓名:'+this.name+' 年龄:'+this.age)
          }
      }
      */
      class Person{
          constructor(name,age) {
              this.name = name
              this.age = age
          }
          say(){
              console.log('姓名:'+this.name+' 年龄:'+this.age)
          }
      }
      
      let proxy_person = new Proxy(Person, {
          construct(target, argArray, newTarget) {
              console.log('初始化参数:' + argArray)
              return Reflect.construct(target, argArray, newTarget)
          }
      })
      let p = new proxy_person('eliwang', 20)
      p.say()

七、Promise对象

  • 简介

    • Promise就是JS中专门用来存储异步代码的对象,可以确保异步代码的执行和返回结果
  • 创建

    • 创建Promise对象,需要指定一个回调函数作为参数,然后将需要执行的代码编写到该回调函数中
      • 回调函数参数
        • resolve:在代码正常执行时,来设置返回值
        • reject(可选):在代码执行出错时,来设置错误信息
  • 执行

    • 顺利执行时,会执行then()方法,并将resolve返回的结果作为参数,传递给then()方法内的回调函数
    • 执行异常时,会执行catch()方法,并将错误信息传递给内部的回调函数
      // 创建Promise对象,正常执行,返回'顺利执行'
      const promise1 = new Promise((resolve, reject) => {
          setTimeout(() =>{
              resolve('顺利执行')
              console.log('异步代码') // 会先执行该句代码,再返回执行结果'顺利执行'
          }, 2000)
      })
      
      // 创建Promise对象,返回报错信息'执行异常'
      const promise2 = new Promise((resolve, reject) => {
          setTimeout(() =>{
              reject('执行异常')
              console.log('异步代码')
          }, 2000)
      })
      
      // 当Promise对象中的代码顺利执行时,会执行then()方法,并将resolve返回的结果作为参数,传递给then()方法内的回调函数
      promise1
          .then(result => console.log(result))
          .catch(e => console.log(e))
      
      // 当Promise对象中的代码返回错误信息时,会调用catch()方法,并将错误信息作为参数,传递给catch()方法内的回调函数
      promise2
          .then(result => console.log(result))
          .catch(e => console.log(e))
  • then的链式调用

    • 如果上一个then()中的返回值不是Promise对象,则将该返回值直接传递给下一个then()
    • 如果上一个then()中的返回值是Promise对象,则将Promise对象中resolve返回值传给下一个then()
      // 定义3个函数,分别返回promise对象
      function fn1(n){
          return new Promise(resolve => {
              setTimeout(
                  () => resolve(n + 1),
                  1000
              )
          })
      }
      function fn2(n){
          return new Promise(resolve => {
              setTimeout(
                  () => resolve(n + 2),
                  1000
              )
          })
      }
      function fn3(n){
          return new Promise(resolve => {
              setTimeout(
                  () => resolve(n + 3),
                  1000
              )
          })
      }
      // 如果上一个then()的返回值不是Promise对象,则将该返回值传给下一个then
      // 因此下面执行结果是:30
      fn1(10)
          .then(n => 20)
          .then(n => 30)
          .then(n => console.log(n))
          .catch(e => console.log(e))
      
      // 如果上一个then()的返回值是Promise对象,则将上一个Promise对象中resolve返回值传给下一个then
      // 因此下面执行结果是:10+1+2+3 = 16
      fn1(10)
          .then(n => fn2(n))
          .then(n => fn3(n))
          .then(n => console.log(n))
          .catch(e => console.log(e))
  • 异步调试

    • 原则

      • 能跳过的则跳过(异步前后,目标数据是否均为加密/解密的,只要一致就可以跳过)
      • 如果不能跳过,遇到下面这种情形的
        • n = n.then(t.shift(), t.shift())
          • 首先定位到该代码处,不要下断点
          • 向上跟栈,找到一个可以确定的地方先下断点
          • 再在此处下上断点
          • 断住之后,找参数数组的第一个方法,该方法大概率是加密/解密地方
posted @ 2022-12-23 02:46  eliwang  阅读(937)  评论(0编辑  收藏  举报