Repository navigation
ES6 系列之 defineProperty 与 proxy #107
Description
Activity
赞~ 写的很好 易懂
watch api 那块代码
(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) } }); // 这会调用被包装对象的 setter 方法 if (value) obj[name] = value } this.watch = watch; })()所以一开始用 watch 来初始化监听 obj 的时候,就会调用一次注册进去的 callback 吧?感觉有点不太符合平时的逻辑
watch(obj, "value", function(newvalue){ document.getElementById('container').innerHTML = newvalue; })首先感谢大佬分享,然后问一个问题,希望大佬能解答一下,为什么这一块要加这一句
(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) } }); // 注册watch就调用一次setter的目的是什么? if (value) obj[name] = value } this.watch = watch; })()好像理解了,是为了在obj中的value有初始值的情况下能立即在页面上展示吧
Reacted by psychola发现一个小的拼写错误
polyfill🐛Reacted by tianyu and Jlg1128肚子小了整个人都有活力了
感觉可以说下 proxy对比defineProperty的区别,以及优势在在哪里耶
感觉可以说下 proxy对比defineProperty的区别,以及优势在在哪里耶
博主有说啊,proxy 可以代理更多的行为啊,defineProperty 只能代理 get 和 set。
首先感谢大佬分享,然后问一个问题,希望大佬能解答一下,为什么这一块要加这一句
(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) } }); // 注册watch就调用一次setter的目的是什么? if (value) obj[name] = value } this.watch = watch; })()好像理解了,是为了在obj中的value有初始值的情况下能立即在页面上展示吧
这一点我也没看明白,赋值时调用 set 方法就会自动更新 obj 中的 value 属性了。
博主写的简单易懂,太适合小白了,感谢。
为什么要把watch函数放到自执行函数里面?
Reacted by Ethan感觉可以说下 proxy对比defineProperty的区别,以及优势在在哪里耶
Proxy
- 代理的是 对象
- 可以拦截到数组的变化
- 拦截的方法多达13种
- 返回一个拦截后的数据
Object.defineProperty
- 代理的是属性
- 对数组数据的变化无能为力
- 直接修改原始数据
Reacted by 流采, coco, stray_bird_, xsfxtsxxrxfy, answer33, Northerner, mengmenglv, flashhu, MEDASSSSSSS, Stanley Wind and 1 moreReacted by 大涛子, xsfxtsxxrxfy and Tang那proxy 会有 polyfill来支持 IE11以下的浏览器吗
应用场景
vue
-
vue 2 使用 defineProperty 通 getter / setter 进行数据劫持
-
vue 3 换成 Proxy, 存在向下兼容问题
Reacted by Lchyang-
为什么要把watch函数放到自执行函数里面?
应该是用了ES6之前,模块封装的通用写法。
将 watch 函数封装在 IIFE 之后,在编译阶段 JavaScript 引擎就不会为 watch 创建函数对象,这样 watch 就不会被外部代码访问到(仅对于编译阶段;注意 watch 是绑定到全局上下文的)。github中文这个屑排版,唉。。。
watch api 上方的例子是不是有点问题?
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 }]value = value 的话没有办法把值更新到var定义的value上
Reacted by 銀桑面试官问 :Object.definedProperty 和 Proxy 相比优势和劣势 怎么回答噢?
watch api 上方的例子是不是有点问题?
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 }]value = value 的话没有办法把值更新到var定义的value上
应该使用newValue赋值的,笔者笔误了吧。
core-js的作者最近很困难
前言
我们或多或少都听过“数据绑定”这个词,“数据绑定”的关键在于监听数据的变化,可是对于这样一个对象:
var obj = {value: 1},我们该怎么知道 obj 发生了改变呢?definePropety
ES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
语法
参数
举个例子:
虽然我们可以直接添加属性和值,但是使用这种方式,我们能进行更多的配置。
函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符和存取描述符。
两者均具有以下两种键值:
configurable
enumerable
数据描述符同时具有以下可选键值:
value
writable
存取描述符同时具有以下可选键值:
get
set
值得注意的是:
属性描述符必须是数据描述符或者存取描述符两种形式之一,不能同时是两者 。这就意味着你可以:
也可以:
但是不可以:
此外,所有的属性描述符都是非必须的,但是 descriptor 这个字段是必须的,如果不进行任何配置,你可以这样:
Setters 和 Getters
之所以讲到 defineProperty,是因为我们要使用存取描述符中的 get 和 set,这两个方法又被称为 getter 和 setter。由 getter 和 setter 定义的属性称做”存取器属性“。
当程序查询存取器属性的值时,JavaScript 调用 getter方法。这个方法的返回值就是属性存取表达式的值。当程序设置一个存取器属性的值时,JavaScript 调用 setter 方法,将赋值表达式右侧的值当做参数传入 setter。从某种意义上讲,这个方法负责“设置”属性值。可以忽略 setter 方法的返回值。
举个例子:
这不就是我们要的监控数据改变的方法吗?我们再来封装一下:
watch API
既然可以监控数据的改变,那我可以这样设想,即当数据改变的时候,自动进行渲染工作。举个例子:
HTML 中有个 span 标签和 button 标签
当点击按钮的时候,span 标签里的值加 1。
传统的做法是:
如果使用了 defineProperty:
代码看似增多了,但是当我们需要改变 span 标签里的值的时候,直接修改 obj.value 的值就可以了。
然而,现在的写法,我们还需要单独声明一个变量存储 obj.value 的值,因为如果你在 set 中直接
obj.value = newValue就会陷入无限的循环中。此外,我们可能需要监控很多属性值的改变,要是一个一个写,也很累呐,所以我们简单写个 watch 函数。使用效果如下:我们来写下这个 watch 函数:
现在我们已经可以监控对象属性值的改变,并且可以根据属性值的改变,添加回调函数,棒棒哒~
proxy
使用 defineProperty 只能重定义属性的读取(get)和设置(set)行为,到了 ES6,提供了 Proxy,可以重定义更多的行为,比如 in、delete、函数调用等更多行为。
Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。我们来看看它的语法:
proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
除了 get 和 set 之外,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。
又比如说 apply 方法拦截函数的调用、call 和 apply 操作。
apply 方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组,不过这里我们简单演示一下:
又比如说 ownKeys 方法可以拦截对象自身属性的读取操作。具体来说,拦截以下操作:
下面的例子是拦截第一个字符为下划线的属性名,不让它被 for of 遍历到。
更多的拦截行为可以查看阮一峰老师的 《ECMAScript 6 入门》
值得注意的是,proxy 的最大问题在于浏览器支持度不够,而且很多效果无法使用 poilyfill 来弥补。
watch API 优化
我们使用 proxy 再来写一下 watch 函数。使用效果如下:
我们也可以发现,使用 defineProperty 和 proxy 的区别,当使用 defineProperty,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截。
ES6 系列
ES6 系列目录地址:https://github.com/mqyqingfeng/Blog
ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。