属性描述符就是一个属性除了属性名与属性值之外的其他相关信息 通过 通过 接下来,说一说每一个属性描述符的作用 不多逼逼 当我们设置configurable为false以后,再去修改属性描述符的话,会报错 当设置一个属性的enumerable为false时,该属性不可被forin循环 数组也一样 当一个属性的writable为false时,该属性值不可修改 通过 属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。 get 和 set配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set方法。 存取器属性最大的意义,在于可以控制属性的读取和赋值。 当取一个值的时候,会执行该函数,并返回该函数结果 而且该值会显示为(…) 当给一个属性赋值的时候,会执行该函数, 无论是get还是set,在赋值时都不可直接赋值给当前操作的属性,需要赋值给另一个值,否则会陷入死循环 存取器可以帮我们在赋值和取值的时候顺便做一些其他的事 但是由于存取器不可直接给自己属性赋值,就导致了需要的内存会翻一倍 Reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能 由于它类似于其他语言的反射,因此取名为Reflect 栗子: 使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能 有一个重要的理念,在ES5就被提出:减少魔法、让代码更加纯粹 这种理念很大程度上是受到函数式编程的影响 ES6进一步贯彻了这种理念,它认为,对属性内存的控制、原型链的修改、函数的调用等等,这些都属于底层实现,属于一种魔法,因此,需要将它们提取出来,形成一个正常的API,并高度聚合到某个对象中,于是,就造就了Reflect对象 因此,你可以看到Reflect对象中有很多的API都可以使用过去的某种语法或其他API实现。 reflect的存在为我们参与js底层实现提供了可能 代理:提供了修改底层实现的方式 如果我们给一个obj做代理,那么赋值取值等一系列操作都有代理来实现,如果代理实现赋值取值的方式和直接操作obj一样,那么代理毫无意义,这就要求,代理需要使用底层实现去完成一些操作,所以需要反射 代理的出现,让开发者可以参与js的底层实现 代理修改了js的底层实现 objProxy是什么呢? 例: 观察者模式 Proxy实现 在我们使用defineProperty创建观察者的时候,返回的观察值对象和原对象是两个不同的对象,这就导致defineProperty使用了双倍内存 而代理是改写了底层实现,使用的是同一个值,不存在这个问题 如果我在控制台添加一个target.c = 3的时候,修改target.c是不会触发render方法的,因为只代理了最初的target属性a和b 而Proxy就可以 defineProperty只能通过存取器实现修改取值赋值逻辑 而Proxy可修改所有底层实现 Proxy对比Object.defineProperty: 优点: 缺点:Proxy的兼容性不是太好,不兼容IE 现在明白为什么Vue3.0放弃defineProperty使用Proxy了吗?
属性描述符
什么是属性描述符?
Object.getOwnPropertyDescriptor(对象, 属性名)
可以得到一个对象的某个属性的属性描述符let obj = { a: 1 } console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // { // value: 1, // writable: true, // enumerable: true, // configurable: true // }
Object.getOwnPropertyDescriptors(对象)
可以得到某个对象的所有属性描述符let obj = { a: 1, b: 2 } console.log(Object.getOwnPropertyDescriptors(obj)); // { // a: { // value: 1, // writable: true, // enumerable: true, // configurable: true // } // b: { // value: 2, // writable: true, // enumerable: true, // configurable: true // } // }
value-属性值
configurable-属性描述符是否可被修改
let obj = { a: 1, b: 2 } Object.defineProperty(obj, 'a', { value: 'a', configurable: false }) Object.defineProperty(obj, 'a', { value: 'a', configurable: true }) // Uncaught TypeError: Cannot redefine property: a // at Function.defineProperty (<anonymous>)
enumerable-该属性是否可被枚举
但是不影响forof循环,因为forof循环看有没有Symbol(Symbol.iterator)
forin循环的是属性名,forof循环的是属性值let obj = { a: 1, b: 2 } Object.defineProperty(obj, 'a', { value: 'a', enumerable: false }) for (const key in obj) { console.log(key) } // 只输出b
let arr = [1, 2, 3] Object.defineProperty(arr, 1, { value: 22, enumerable: false }) for (const key in arr) { console.log(key) } // 输出0和2
writable-该属性是否可被重新赋值
let obj = { a: 1, b: 2 } Object.defineProperty(obj, 'a', { value: 'a', writable: false }) obj.a = 'a_' console.log(obj) // {a: "a", b: 2}
同时修改多个属性的描述符
Object.defineProperties(对象, 配置)
可以同时修改多个属性的属性描述符let obj = { a: 1, b: 2 } Object.defineProperties(obj, { a: { value: 'a', configurable: true }, b: { value: 'b', configurable: true } })
存取器属性
get
let obj = { a: 1, b: 2 } Object.defineProperty(obj, 'a', { get() { console.log('取值') return 'aaa' } }) console.log(obj)
点击会执行get函数
点击后
set
let obj = { a: 1, b: 2 } Object.defineProperty(obj, 'a', { set(value) { obj.a_ = value } })
存取器总结
举个栗子<body> <span id="span">我是span</span> <script> let span = document.getElementById('span'); console.dir(span) </script>
在span的__proto__中就存在很多存取器
当我们修改span.innerText = 222的时候,页面上的span标签的内容也会跟着改变
但是span只是一个js对象,凭啥改了它,页面的内容也会跟着一起变呢?
当然因为修改这个值的时候,顺便做了其他的事,这就是由set完成的,所以在__proto__有很多存取器属性反射Reflect
Reflect是什么?
当你使用obj.a = 1的时候,就是在底层调用Reflect.set方法它可以做什么?
这些功能不是已经存在了吗?为什么还需要用Reflect实现一次?
它里面到底提供了哪些API呢?
Proxy 代理
为什么需要反射Reflect?
代理的原理
举个栗子let obj = { a: 1 } let objProxy = new Proxy(obj, { set(target, propertyKey, value) { console.log('通过代理修改') // 可以使用普通的赋值来修改 // target[propertyKey] = value // 但是由于是修改底层实现,最好还是使用底层方法 Reflect.set(target, propertyKey, value) } })
在这个栗子中,我们使用obj.a = 3的时候,实际上就是调用了底层函数Reflect.set(target, propertyKey, value),而使用代理就是可以修改这个底层Reflect.set方法的内容
这个代理是一个对象,但是这个对象没有原型代理相比defineProperty强大在什么地方?
defineProperty实现<body> <div id="container"> </div> <script> //创建一个观察者 function observer(target) { const div = document.getElementById("container"); const ob = {}; const props = Object.keys(target); for (const prop of props) { Object.defineProperty(ob, prop, { get() { return target[prop]; }, set(val) { target[prop] = val; render(); }, enumerable: true }) } render(); function render() { let html = ""; for (const prop of Object.keys(ob)) { html += ` <p><span>${prop}:</span><span>${ob[prop]}</span></p> `; } div.innerHTML = html; } return ob; } const target = { a: 1, b: 2 } const obj = observer(target) </script> </body>
<body> <div id="container"> </div> <script> //创建一个观察者 function observer(target) { const div = document.getElementById("container"); const proxy = new Proxy(target, { set(target, prop, value) { Reflect.set(target, prop, value); render(); }, get(target, prop){ return Reflect.get(target, prop); } }) render(); function render() { let html = ""; for (const prop of Object.keys(target)) { html += ` <p><span>${prop}:</span><span>${target[prop]}</span></p> `; } div.innerHTML = html; } return proxy; } const target = { a: 1, b: 2 } const obj = observer(target) </script> </body>
占用内存更小
defineProperty不可以代理后加的属性
defineProperty只能修改赋值取值,而Proxy可以修改所有底层实现
不能星期其他实现总结
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算