淘先锋技术网

首页 1 2 3 4 5 6 7

一、ECMAScript-es6-Proxy
1. Proxy-代理
  • 什么代理

    在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写.

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzGDAOaf-1605415224187)(../ECMAScript-imgs/image-20201103220241596.png)]

  • 构造代理

    // ES5的提供的代理方式
    var obj = {}
    var newVal = ''
    Object.defineProperty(obj, 'name', {
      get(){
        return newVal
      },
      set(val){
        newVal = val
      }
    })
    obj.name = 'es'
    console.log(obj.name)
    
    // ES6
    let proxy = new Proxy(target, handler);
    // new Proxy()表示生成一个Proxy实例
    // target参数表示所要拦截的目标对象
    // handler参数也是一个对象,用来定制拦截行为。如果handler没有设置任何拦截,那就等同于直接通向原对象。
    
2. 常用的拦截方法
拦截器作用
get拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]
set拦截对象属性的设置,返回一个布尔值,比如proxy.foo = v 或 proxy[‘foo’] = v
has拦截propKey in proxy 的操作,返回一个布尔值
ownKeys拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for …in循环,返回一个数组。
deleteProperty拦截delete proxy[propKey]的操作,返回一个布尔值
apply拦截函数的调用、call和apply操作
construct拦截new命令,返回一个对象
// get(target, propKey, receiver)
// get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
// 数组下标是否有对应的值,有则返回
let arr = [7, 8, 9]
arr = new Proxy(arr, {
  get(target, prop, receiver) {
    console.log(target, prop, receiver)
    return prop in target? target[prop]: 'error'
  }
})
console.log(arr[1]) // 8
console.log(arr[10]) // error

// set(target, propKey, value, receiver)
// set方法用来拦截某个属性的赋值操作,可以接受四个参数set(目标对象, 属性名, 属性值,Proxy 实例本身),其中最后一个参数可选。
// 往数组里面设置一个值
let arr = []
arr = new Proxy(arr, {
  set(target, prop, val) {
    if (typeof val === 'number') {
      target[prop] = val
      return true
    } else {
      return false
    }
  }
})
arr.push(5)
arr.push(6)
console.log(arr[0], arr[1])

// has(target, propKey)
// has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
// has方法可以接受两个参数,分别是目标对象、需查询的属性名。
let range = {
    start: 1,
    end: 5
  }
  range = new Proxy(range, {
    has(target, prop) {
        return prop >= target.start && prop <= target.end
    }
  })
console.log(2 in range)
console.log(9 in range)

// ownkeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
let obj = {
    name: 'imooc',
    [Symbol('es')]: 'es6'
  }
console.log(Object.getOwnPropertyNames(obj)) // ["name"]
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(es)]
console.log(Object.keys(obj)) // ["name"]
for (const key in obj) {
    console.log(key) // name
}

// 应用场景
// 隐藏一个对象的私有属性
let userInfo = {
    username: 'zhangsan',
    age: 34,
    _pwd: '****'
}
userInfo = new Proxy(userInfo, {
    ownKeys(target) {
       return Object.keys(target).filter(key => !key.startsWith('_'))
    }
})

for (const key in userInfo) {
    console.log(key) // username age
}

// 综合案例
let user = {
    username: 'zhangsan',
    age: 34,
    _pwd: '****'
}

user = new Proxy(user, {
    get(target, prop) {
        if (prop.startsWith('_')) {
            throw new Error('不可访问')
        } else {
            return target[prop]
        }
    },
    set(target, prop, val) {
        if (prop.startsWith('_')) {
            throw new Error('不可访问')
        } else {
            target[prop] = val
            return true
        }
    },
    deleteProperty(target, prop) { // 拦截删除操作
       if (prop.startsWith('_')) {
            throw new Error('不可删除')
       } else {
            delete target[prop]
       }
    },
    ownKeys(target) {
       return Object.keys(target).filter( key => !key.startsWith('_'))
    }
})

// console.log(user.age) // 34
// console.log(user._pwd) //  Uncaught Error: 不可访问
// user._pwd = '123' // Uncaught Error: 不可访问
// delete user._pwd // 不可删除
// for (let key in user) {
    // console.log(key)
// }

// apply
// 拦截 Proxy实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
let sum = (...args) => {
    let num = 0
    args.forEach(item => {
        num += item
    })
    return num
}

sum = new Proxy(sum, {
    apply(target, ctx, args) {
        return target(...args) * 2
    }
})

console.log(sum(1, 2))
console.log(sum.call(null, 1, 2, 3))
console.log(sum.apply( null, [1, 2, 3]))

// construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
let User = class {
    constructor(name) {
        this.name = name
    }
}

User = new Proxy(User, {
    construct(target, args, newTarget){
        return new target(...args)
    }
}) 
console.log(new User('zhangsan'))