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/2736899.html

(0)
上一篇 2025年3月8日 01:29:44
下一篇 2025年3月8日 01:29:50

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

相关推荐

  • JavaScript的数据类型与变量的解析(附示例)

    本篇文章给大家带来的内容是关于javascript的数据类型与变量的解析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 这篇文章,来聊聊 JS 中的数据类型与变量。这是在学习 JS 时最基础的一类问题,但却很重要…

    2025年3月8日
    200
  • React中高阶组件的使用示例

    本篇文章给大家带来的内容是关于React中高阶组件的使用示例,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 Higher-Order Components hoc 不是react的标准api。 HOC 是一个函数。 HOC…

    编程技术 2025年3月8日
    200
  • Node.js中阻塞与非阻塞的讲解

    本篇文章给大家带来的内容是关于Node.js中阻塞与非阻塞的讲解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 阻塞与非阻塞概述 此概述介绍了node.js中阻塞与非阻塞调用之间的区别,此概述将引用事件循环和libuv,但…

    编程技术 2025年3月8日
    200
  • 浅谈ES6中的装饰器

    本篇文章给大家带来的内容是关于浅谈ES6中的装饰器,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 Decorator 装饰器主要用于: 装饰类 装饰方法或属性 装饰类 @annotationclass MyClass { …

    编程技术 2025年3月8日
    200
  • JS数组中的indexOf方法的分析(附示例)

    本篇文章给大家带来的内容是关于js数组中的indexof方法的分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 这两天在家中帮朋友做项目,项目中使用了数组的indexOf 方法,找到了一篇文章,感觉非常不错,顺…

    编程技术 2025年3月8日
    200
  • javascript可以做什么

    js可以做的功能有很多,其中就包括用户的交互,还可以收集用户的信息,有的时候用户也可以去创建一个变量语句,去实现变量的计算等等。 本文操作环境:Windows7系统、Dell G3电脑、javascript1.8.5版。 js是什么语言?j…

    2025年3月8日
    200
  • Vue进度条组件的实现教程(代码示例)

    本篇文章给大家带来的内容是关于 vue进度条组件的实现教程(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 最近在个人的项目中,想对页面之间跳转的过程进行优化,想到了很多文档或 npm 等都用到的页面跳转进度条,…

    2025年3月8日
    200
  • JavaScript预解释是什么?JavaScript预解释的解析(附代码)

    本篇文章给大家带来的内容是关于javascript预解释是什么?javascript预解释的解析(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 JavaScript是一门解释型的语言 , 想要运行JavaScri…

    2025年3月8日
    200
  • react函数this相关问题的分析(代码示例)

    本篇文章给大家带来的内容是关于react函数this相关问题的分析(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 react 函数this相关 在使用react的过程中,常常因为函数的this问题导致执行结果不…

    2025年3月8日
    200
  • vue无痕刷新的实现方法介绍

    本篇文章给大家带来的内容是关于vue无痕刷新的实现方法介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 最近遇到一个需求,需要刷新当前页面来更新数据,网上找了很多方法,在这里做个总结。 这里主要记录三种方法,分别是:强制…

    2025年3月8日
    200

发表回复

登录后才能评论