【JS不正经学习】--ES6 Proxy(代理)

1 篇文章 0 订阅

Proxy

Proxy用于创建一个对象的代理,从而修改某些操作的默认行为。可以理解成在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,让我们可以对外界的访问进行过滤和改写,这些过滤,可以由我们自己来定义:

语法

  cosnt p = new Proxy(target, handler);

参数

target
要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)

handler
是一个以函数作为属性的对象,其属性是当执行各种操作时代理的函数(可以理解为对象某些操作的捕捉器)。

// 定义一个和张三一样普通的对象
let userInfo = {
  name: "kobe"
};
// 我们用proxy做一个简单的代理
userInfo = new Proxy(userInfo, {
  // 设置一个读取和操作的捕捉器
  get(target, prop) {
    console.log('getter:获取了对象属性' + prop);
    return target[prop]
  },
  set(target, prop, value) {
    console.log(`setter: 操作对象属性${prop},值为${value}`);
    // 也可以写一些属性设置的条件'l
    if(prop === 'age' && value >= 200) {
      throw new RangeError("这怕不是个老妖怪吧");
    }
    // 符合条件的属性保存
    target[prop] = value;
    return true;
  }
});

// 兄弟们让我们来操作它
// 读取userInfo的属性看会发生什么
console.log(userInfo.name); // getter:获取了对象属性name  kobe
// 给userInfo添加新的属性age
userInfo.age = 41; // setter: 操作对象属性age,值为41  41
// 添加另外属性
userInfo.gender = 'male'; // setter: 操作对象属性gender,值为male  'male'
++userInfo.age;
// getter:获取了对象属性age
// setter: 操作对象属性age,值为42

从上面的例子中,我们先定义了一个包含name的用户信息对象userInfo,然后我们通过Proxy包装,再返回给userInfo,此时的userInfo就成了一个 Proxy实例,我们对其进行的操作,都会被 Proxy拦截。对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。

handler 对象的方法

下面是 Proxy 支持的拦截操作一览,一共 13 种。

handler.get()

var userInfo = {
  name: "张三"
}

const user = new Proxy(userInfo, {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    } else {
      throw new ReferenceError("prop name \"" + prop + "\" does not exist.");
    }
  }
})
user.name // "张三"
user.age // Uncaught ReferenceError: prop name "age" does not exist.

handler.set()

let handler = {
  set(target, prop, value) {
    if(prop === "age") {
      if(!Number.isInteger(value))
      if(!Number.isInteger(value))
        throw new TypeError('The age is not an integer');
      if(value > 200)
         throw new RangeError('这怕不是个老妖怪吧!') 
    }
    target[prop] = value;
    return true;
  }
}

let user = new Proxy({}, handler);
user.name = "张三"; // '张三'
user.age = 100; // 100
user.age = 1.1; // Uncaught TypeError: The age is not an integer
user.age = "old"; // Uncaught TypeError: The age is not an integer
user.age = 250; // Uncaught RangeError: 这怕不是个老妖怪吧!

handler.has()

has()方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。

has()方法可以接受两个参数,分别是目标对象、需查询的属性名。

下面的例子使用has()方法隐藏某些属性,不被in运算符发现。

  let handler = {
    has(target, prop) {
      if(prop.indexOf('_') > -1) {
        return false;
      }
      return true;
    }
  }
  let person = {
    _name: "zhang",
    name: "张三",
    old_name: "张狗蛋"
  }
  let user = new Proxy(person, handler);

  "name" in user; // true
  "_name" in user; // false
  "old_name" in user; // false

handler.deleteProperty()

deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

// 定义狗蛋信息对象
let person = {
  name: "狗蛋",
  ID_number: "88888888",
  age: 25,
  gender: 'male'
}
// 创建代理对象
let user = new Proxy(person, {
  deleteProperty(target, prop) {
    // 输出信息
    console.log(`老子要删除你的${prop}`);
    if(prop === 'name' || prop === 'ID_number') {
      throw new Error(`二货,爸爸的${prop}是你能删的吗!!!`)
    }
    return true;
  }
})

delete user.age //  true
delete user.name // Uncaught Error: 二货,爸爸的name是你能删的吗!!!

handler.ownKeys()

ownKeys()方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • for...in循环
// 1、拦截Object.keys()的例子
let person = {
  name: "狗蛋",
  ID_number: "88888888",
  age: 25,
  gender: 'male'
} 

let user = new Proxy(person, {
  ownKeys(target) {
    // 返回属性中不包含下划线的
    return Object.keys(target).filter(key => key.indexOf('_') < 0)
  }
})

Object.keys(user); //  ['name', 'age', 'gender']

// 2、拦截Object.getOwnPropertyNames()
Object.getOwnPropertyNames(user); // ['name', 'age', 'gender']

// 3、for...in循环
for (let key in user) {
  console.log(key); // name age gender
}
// 返回自己没有的属性
let user = new Proxy(person, {
  ownKeys(target) {
    return ['name', 'age', 'a', 'b']
  }
})
for (let key in user) {
  console.log(key); // name age 
}

ownKeys()方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错


handler.getOwnPropertyDescriptor()

getOwnPropertyDescriptor()方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined

const user = new Proxy(person, {
  getOwnPropertyDescriptor(target, prop) {
    if(prop === "ID_number") return;
    return Object.getOwnPropertyDescriptor(target, prop);
  }
})

Object.getOwnPropertyDescriptor(user, 'name');
// {configurable: true, enumerable: true, value: "狗蛋", writable: true}
Object.getOwnPropertyDescriptor(user, 'ID_number');
// undefined

handler.defineProperty()

defineProperty()方法拦截了Object.defineProperty()操作。

  const user = new Proxy(person, {
    defineProperty (target, prop, desc) {
      return false;
    }
  })

  user.foo = "bar"; // 不会生效

defineProperty()方法内部没有任何操作,导致添加新属性总是无效。

注意: 如果目标对象不可扩展(non-extensible),则defineProperty()不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则defineProperty()方法不得改变这两个设置。


handler.preventExtensions()

preventExtensions()方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。

这个方法有一个限制,只有目标对象不可扩展时(即Object.isExtensible(proxy)false),proxy.preventExtensions才能返回true,否则会报错。

var user = new Proxy(person, {
  preventExtensions: function(target) {
    return true;
  }
});
Object.preventExtensions(user);
// Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible

上面代码中,proxy.preventExtensions()方法返回true,但这时Object.isExtensible(proxy)会返回true,因此报错。

为了防止出现这个问题,通常要在proxy.preventExtensions()方法里面,调用一次Object.preventExtensions()

var user = new Proxy(person, {
  preventExtensions: function(target) {
    Object.preventExtensions(target);
    return true;
  }
});
Object.preventExtensions(user); // Proxy {...}

handler.getPrototypeOf()

getPrototypeOf()方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。

  • -Object.prototype.__proto__
  • -Object.prototype.isPrototypeOf()
  • -Object.getPrototypeOf()
  • -Reflect.getPrototypeOf()
  • -instanceof
// 定义一个和你一样普通的对象
var obj = {};
var user = new Proxy({}, {
  getPrototypeOf(target) {
    return obj;
  }
})
Object.getPrototypeOf(user) === obj // true

getPrototypeOf()方法拦截Object.getPrototypeOf(),返回obj对象。

注意,getPrototypeOf()方法的返回值必须是对象或者null,否则报错。另外,如果目标对象不可扩展,getPrototypeOf()方法必须返回目标对象的原型对象。


handler.setPrototypeOf()

setPrototypeOf()方法主要用来拦截Object.setPrototypeOf()方法。

var obj = {};
var target = function () {};
var handler = {
  setPrototypeOf (target, proto) {
    throw new Error('Changing the prototype is forbidden');
  }
};

var user = new Proxy(target, handler);
Object.setPrototypeOf(user, obj); // Error: Changing the prototype is forbidden

上面代码中,只要修改target的原型对象,就会报错。

注意,该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展,setPrototypeOf()方法不得改变目标对象的原型。


handler.isExtensible()

isExtensible()方法拦截Object.isExtensible()操作。Object.isExtensible()方法用于判断一个对象是否是可扩展的。

var user = new Proxy({}, {
  isExtensible: function(target) {
    console.log("called");
    return true;
  }
});

Object.isExtensible(user); // called true

注意,该方法只能返回布尔值,否则返回值会被自动转为布尔值。

这个方法有一个强限制,它的返回值必须与目标对象的isExtensible属性保持一致,否则就会抛出错误。


handler.apply()

apply方法拦截函数的调用、callapply操作。

apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

var person = function() {
  return "我是个比张三还普通的人";
}

var handler = {
  apply() {
    return "我特么的被拦截了!"
  }
}

var user = new Proxy(person, handler);
user(); // '我特么的被拦截了!'
user.call(); // '我特么的被拦截了!'
user.apply(); // '我特么的被拦截了!'
Reflect.apply(user, null, []); // '我特么的被拦截了!'

上面代码中,每当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。

另外,直接调用Reflect.apply方法,也会被拦截。


handler.construct()

construct()方法用于拦截new命令,下面是拦截对象的写法。

var user = new Proxy(function () {}, {
  construct: function(target, args) {
    console.log('called: ' + args.join(', '));
    return { name: args[0]};
  }
});
new user('张三'); 
// called: 张三
// {name: '张三'}

由于construct()拦截的是构造函数,所以它的目标对象必须是函数,否则就会报错。
注意,construct()方法中的this指向的是handler,而不是实例对象。

实例

Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。

const service = createWebService('http://exam.com/data');
service.employees().then(json => {
  // ....
})

新建一个web服务的接口,Proxy可以拦截这个对象的任意属性,所以不用为每一种数据写适配方法,只要用proxy拦截就可以了。

function createWebService(baseUrl) {
  return new Proxy({}, {
    get(target, prop) {
      return Http.get(`${baseUrl}/{prop}`)
    },
    // ...
  });
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: cesium-popup-es6是一个基于ES6语法编写的Cesium.js的弹窗组件,用于在Cesium地图上显示自定义的弹窗内容。 这个组件主要包含了以下几个部分: 1. Popup类:这是弹窗的主要类,负责创建和管理弹窗的各种属性和方法。通过使用Popup类,可以很方便地在地图上创建弹窗,并设置弹窗的位置、大小、内容等。 2. 事件监听:cesium-popup-es6还提供了一些事件监听的方法,可以在弹窗的打开、关闭以及其他交互操作时进行相应的处理。例如,可以监听鼠标点击事件来关闭弹窗,或者监听地图的移动事件来更新弹窗的位置。 3. 样式定制:该组件允许用户通过设置自定义的CSS样式来定制弹窗的外观和风格。这使得用户可以根据自己的需要来修改弹窗的颜色、字体、边框等样式属性,从而实现个性化的弹窗显示效果。 4. 兼容性:cesium-popup-es6能够很好地与Cesium.js的其他功能和插件进行集成,同时对不同的浏览器和设备也具备良好的兼容性。这意味着无论是在PC端还是移动端,无论是在Chrome还是在Safari等浏览器上,cesium-popup-es6都能够正常运行。 总的来说,cesium-popup-es6文档提供了关于如何使用和定制这个弹窗组件的详细说明,方便开发者在Cesium.js地图项目中加入自定义的弹窗功能。无论是展示地理信息、交互操作还是其他相关需求,cesium-popup-es6都能够帮助开发者实现一个易于使用和美观的弹窗效果。 ### 回答2: cesium-popup-es6 是一个基于 Cesium.js 的弹出窗口组件,该组件使用 ES6 编写。它为用户提供了在 Cesium 场景中高度可定制的弹出窗口功能。 组件的文档详细介绍了 cesium-popup-es6 的使用方法和各个配置项的说明。首先,你需要通过 npm 或者其他构建工具下载并引入该组件。然后,在你的代码中创建一个 Popup 实例并传入相应的参数,包括弹出窗口的内容、位置、大小、样式等。 文档中还介绍了组件的主要功能,包括弹出窗口显示和隐藏的方法,以及与 Cesium 场景的交互。你可以通过调用 show 方法来显示弹出窗口,通过调用 hide 方法来隐藏弹出窗口。组件还提供了许多配置项,例如你可以自定义弹出窗口的样式、位置以及与其它元素的交互等。 文档中也提供了丰富的示例代码,以帮助用户更好地理解和使用 cesium-popup-es6 组件。这些示例代码覆盖了各种场景,包括在固定位置显示弹出窗口、在鼠标点击位置显示弹出窗口等。 总的来说,cesium-popup-es6 文档提供了详细的使用说明和示例代码,帮助用户了解和使用该弹出窗口组件。用户可以根据自己的需求进行定制,实现丰富多样的弹出窗口效果。 ### 回答3: cesium-popup-es6 是一个基于ES6语法的Cesium.js库的文档。 该文档主要用于指导开发者在使用cesium-popup-es6库时的操作步骤和相关功能的使用方法。 文档的目录结构清晰明了,按照功能模块进行分类,方便开发者快速找到需要的信息。 在文档的开头部分,首先介绍了cesium-popup-es6的基本概述,包括其功能特点和适用场景,帮助开发者了解该库的作用和优势。 接下来,文档详细介绍了cesium-popup-es6的安装和配置步骤。通过简明的指导,开发者可以快速将该库引入到自己的项目中,并进行相应的配置。 然后,文档详细说明了cesium-popup-es6库的各种功能和使用方法。比如,如何创建和定位弹出窗口、如何设置窗口内容和样式、如何捕获窗口的事件等等。每一项功能都给出了具体的代码示例和详细的说明,方便开发者理解和应用。 文档还提供了一些常见问题的解答和一些建议的最佳实践,帮助开发者在使用cesium-popup-es6库时避免一些常见的问题和错误。 最后,文档附带了一些附录,包括cesium-popup-es6的API参考和一些相关的资源链接,方便开发者查阅进一步的信息和深入理解该库。 总之,cesium-popup-es6 文档是一份详尽而全面的文档,对于开发者学习和使用cesium-popup-es6库非常有帮助,可以提高开发效率并简化开发过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

small_Axe

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

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

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

打赏作者

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

抵扣说明:

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

余额充值