ES6中defineProperty和proxy的详细介绍(代码示例)

本篇文章给大家带来的内容是关于ES6中defineProperty和proxy的详细介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

我们或多或少都听过“数据绑定”这个词,“数据绑定”的关键在于监听数据的变化,可是对于这样一个对象:var obj = {value: 1},我们该怎么知道 obj 发生了改变呢?

definePropety

ES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。

语法

Object.defineProperty(obj, prop, descriptor)

参数

obj: 要在其上定义属性的对象。prop:  要定义或修改的属性的名称。descriptor: 将被定义或修改的属性的描述符。

登录后复制

举个例子:

var obj = {};Object.defineProperty(obj, "num", {    value : 1,    writable : true,    enumerable : true,    configurable : true});//  对象 obj 拥有属性 num,值为 1

登录后复制

虽然我们可以直接添加属性和值,但是使用这种方式,我们能进行更多的配置。

函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符和存取描述符

两者均具有以下两种键值

configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,也能够被删除。默认为 false。

enumerable

当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。

数据描述符同时具有以下可选键值

value

该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

writable

当且仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变。默认为 false。

存取描述符同时具有以下可选键值

get

一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined。

set

一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。

值得注意的是:

属性描述符必须是数据描述符或者存取描述符两种形式之一,不能同时是两者。这就意味着你可以:

Object.defineProperty({}, "num", {    value: 1,    writable: true,    enumerable: true,    configurable: true});

登录后复制

也可以:

var value = 1;Object.defineProperty({}, "num", {    get : function(){      return value;    },    set : function(newValue){      value = newValue;    },    enumerable : true,    configurable : true});

登录后复制

但是不可以:

// 报错Object.defineProperty({}, "num", {    value: 1,    get: function() {        return 1;    }});

登录后复制

此外,所有的属性描述符都是非必须的,但是 descriptor 这个字段是必须的,如果不进行任何配置,你可以这样:

var obj = Object.defineProperty({}, "num", {});console.log(obj.num); // undefined

登录后复制

Setters 和 Getters

之所以讲到 defineProperty,是因为我们要使用存取描述符中的 get 和 set,这两个方法又被称为 getter 和 setter。由 getter 和 setter 定义的属性称做”存取器属性“。

当程序查询存取器属性的值时,JavaScript 调用 getter方法。这个方法的返回值就是属性存取表达式的值。当程序设置一个存取器属性的值时,JavaScript 调用 setter 方法,将赋值表达式右侧的值当做参数传入 setter。从某种意义上讲,这个方法负责“设置”属性值。可以忽略 setter 方法的返回值。

举个例子:

var obj = {}, value = null;Object.defineProperty(obj, "num", {    get: function(){        console.log('执行了 get 操作')        return value;    },    set: function(newValue) {        console.log('执行了 set 操作')        value = newValue;    }})obj.value = 1 // 执行了 set 操作console.log(obj.value); // 执行了 get 操作 // 1

登录后复制

这不就是我们要的监控数据改变的方法吗?我们再来封装一下:

function Archiver() {    var value = null;    // archive n. 档案    var archive = [];    Object.defineProperty(this, 'num', {        get: function() {            console.log('执行了 get 操作')            return value;        },        set: function(value) {            console.log('执行了 set 操作')            value = value;            archive.push({ val: value });        }    });    this.getArchive = function() { return archive; };}var arc = new Archiver();arc.num; // 执行了 get 操作arc.num = 11; // 执行了 set 操作arc.num = 13; // 执行了 set 操作console.log(arc.getArchive()); // [{ val: 11 }, { val: 13 }]

登录后复制

watch API

既然可以监控数据的改变,那我可以这样设想,即当数据改变的时候,自动进行渲染工作。举个例子:

HTML 中有个 span 标签和 button 标签

1

登录后复制

当点击按钮的时候,span 标签里的值加 1。

传统的做法是:

document.getElementById('button').addEventListener("click", function(){    var container = document.getElementById("container");    container.innerHTML = Number(container.innerHTML) + 1;});

登录后复制

如果使用了 defineProperty:

var obj = {    value: 1}// 储存 obj.value 的值var value = 1;Object.defineProperty(obj, "value", {    get: function() {        return value;    },    set: function(newValue) {        value = newValue;        document.getElementById('container').innerHTML = newValue;    }});document.getElementById('button').addEventListener("click", function() {    obj.value += 1;});

登录后复制

代码看似增多了,但是当我们需要改变 span 标签里的值的时候,直接修改 obj.value 的值就可以了。

然而,现在的写法,我们还需要单独声明一个变量存储 obj.value 的值,因为如果你在 set 中直接 obj.value = newValue 就会陷入无限的循环中。此外,我们可能需要监控很多属性值的改变,要是一个一个写,也很累呐,所以我们简单写个 watch 函数。使用效果如下:

var obj = {    value: 1}watch(obj, "num", function(newvalue){    document.getElementById('container').innerHTML = newvalue;})document.getElementById('button').addEventListener("click", function(){    obj.value += 1});

登录后复制

我们来写下这个 watch 函数:

(function(){    var root = this;    function watch(obj, name, func){        var value = obj[name];        Object.defineProperty(obj, name, {            get: function() {                return value;            },            set: function(newValue) {                value = newValue;                func(value)            }        });        if (value) obj[name] = value    }    this.watch = watch;})()

登录后复制

现在我们已经可以监控对象属性值的改变,并且可以根据属性值的改变,添加回调函数,棒棒哒~

proxy

使用 defineProperty 只能重定义属性的读取(get)和设置(set)行为,到了 ES6,提供了 Proxy,可以重定义更多的行为,比如 in、delete、函数调用等更多行为。

Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。我们来看看它的语法:

var proxy = new Proxy(target, handler);

登录后复制

proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

var proxy = new Proxy({}, {    get: function(obj, prop) {        console.log('设置 get 操作')        return obj[prop];    },    set: function(obj, prop, value) {        console.log('设置 set 操作')        obj[prop] = value;    }});proxy.time = 35; // 设置 set 操作console.log(proxy.time); // 设置 get 操作 // 35

登录后复制

除了 get 和 set 之外,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。

// 使用 has 方法隐藏某些属性,不被 in 运算符发现var handler = {  has (target, key) {    if (key[0] === '_') {      return false;    }    return key in target;  }};var target = { _prop: 'foo', prop: 'foo' };var proxy = new Proxy(target, handler);console.log('_prop' in proxy); // false

登录后复制

又比如说 apply 方法拦截函数的调用、call 和 apply 操作。

apply 方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组,不过这里我们简单演示一下:

var target = function () { return 'I am the target'; };var handler = {  apply: function () {    return 'I am the proxy';  }};var p = new Proxy(target, handler);p();// "I am the proxy"

登录后复制

又比如说 ownKeys 方法可以拦截对象自身属性的读取操作。具体来说,拦截以下操作:

Object.getOwnPropertyNames()

Object.getOwnPropertySymbols()

Object.keys()

下面的例子是拦截第一个字符为下划线的属性名,不让它被 for of 遍历到。

let target = {  _bar: 'foo',  _prop: 'bar',  prop: 'baz'};let handler = {  ownKeys (target) {    return Reflect.ownKeys(target).filter(key => key[0] !== '_');  }};let proxy = new Proxy(target, handler);for (let key of Object.keys(proxy)) {  console.log(target[key]);}// "baz"

登录后复制

更多的拦截行为可以查看阮一峰老师的 《ECMAScript 6 入门》

值得注意的是,proxy 的最大问题在于浏览器支持度不够,而且很多效果无法使用 poilyfill 来弥补。

watch API 优化

我们使用 proxy 再来写一下 watch 函数。使用效果如下:

(function() {    var root = this;    function watch(target, func) {        var proxy = new Proxy(target, {            get: function(target, prop) {                return target[prop];            },            set: function(target, prop, value) {                target[prop] = value;                func(prop, value);            }        });        if(target[name]) proxy[name] = value;        return proxy;    }    this.watch = watch;})()var obj = {    value: 1}var newObj = watch(obj, function(key, newvalue) {    if (key == 'value') document.getElementById('container').innerHTML = newvalue;})document.getElementById('button').addEventListener("click", function() {    newObj.value += 1});

登录后复制

我们也可以发现,使用 defineProperty 和 proxy 的区别,当使用 defineProperty,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截。

以上就是ES6中defineProperty和proxy的详细介绍(代码示例)的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2737297.html

(0)
上一篇 2025年3月8日 01:34:06
下一篇 2025年3月8日 01:34:15

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • JavaScript如何模拟实现new关键字?(附代码)

    本篇文章给大家带来的内容是关于javascript如何模拟实现new关键字?(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 最近工作太忙,快接近两周没更新博客,总感觉有一些事情等着自己去做,虽然工作内容对自己提升…

    编程技术 2025年3月8日
    000
  • 浏览器事件循环的深入了解(代码示例)

    本篇文章给大家带来的内容是关于浏览器事件循环的深入了解(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 浏览器的事件循环,前端再熟悉不过了,每天都会接触的东西。但我以前一直都是死记硬背:事件任务队列分为macro…

    编程技术 2025年3月8日
    200
  • JS+CSS实现流星雨的动画效果(代码)

    本篇文章给大家带来的内容是关于js+css实现流星雨的动画效果(代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 1,效果图 2,源码 立即学习“前端免费学习笔记(深入)”; HTML 登录后复制 CSS /* – -…

    2025年3月8日
    200
  • JavaScript和原生应用常用的数据交互方式的介绍

    本篇文章给大家带来的内容是关于javascript和原生应用常用的数据交互方式的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 场景1 在原生app中经常会使用到H5页面,比如说电商中的活动页,一些电商中的详情页,等等…

    编程技术 2025年3月8日
    200
  • JavaScript中原型与原型链的代码讲解

    本篇文章给大家带来的内容是关于javascript中原型与原型链的代码讲解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 对于很多前端开发者而言,JavaScript中原型与原型链是一个比较容易疑惑的点,所以本文记录了自己…

    编程技术 2025年3月8日
    200
  • JavaScript中Array对象常用的方法介绍(代码示例)

    本篇文章给大家带来的内容是关于javascript中array对象常用的方法介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 在js中,数组作为一个特殊的对象。是我们常用的数据格式。今天就来梳理一下常用的数组…

    编程技术 2025年3月8日
    200
  • JavaScript中什么是Window 对象?什么是Navigator 对象?

    本篇文章给大家带来的内容是介绍javascript中什么是window 对象,什么是navigator 对象。有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所帮助。【相关视频教程推荐:javascript教程】 一、 Window…

    2025年3月8日 编程技术
    200
  • JavaScript中数组的方法有哪些?有什么用?

    本篇文章给大家带来的内容是介绍javascript中数组的方法、数组方法的作用和使用方法。有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所帮助。【相关视频教程推荐:javascript教程】 一、数组的创建和赋值 定义数组,数组赋…

    2025年3月8日 编程技术
    200
  • 浅谈Canvas能做什么?Canvas的使用

    本篇文章给大家带来的内容是浅谈canvas能做什么?canvas的使用。有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所帮助。 canvas能做什么? canvas是HTML5中的新元素,你可以使用javascript用它来绘制图…

    编程技术 2025年3月8日
    200
  • javascript 中一些奇葩的日期换算

    本篇文章给大家带来的内容是关于javascript 中一些奇葩的日期换算,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 【推荐阅读:JS怎么转换日期格式】 1.获取今天的0时0分0秒(常用于开始日期的获取) 立即学习“Ja…

    2025年3月8日
    200

发表回复

登录后才能评论