20230417----重返学习-对象方法-Reflect反射对象-代理Proxy-vue2中mounted后面阶段更新非响应式数据
day-051-fifty-one-20230417-对象方法-Reflect反射对象-代理Proxy-vue2中mounted后面阶段更新非响应式数据
对象方法
对象的成员规则
-
对象的
成员
规则,即属性描述符。-
value值
-
writable 该成员的属性值是否可以修改
//普通创建的对象都是 可修改 可删除 可枚举 let obj = {name: "lili",age: 18, };obj.age=88;//age可以被修改。 console.log(obj);
-
enumerable 该成员的属性名是否可枚举
- 能用for-in循环就是可枚举
- 内置类自带的属性,都是不可枚举
//普通创建的对象都是 可修改 可删除 可枚举 let obj = {name: "lili",age: 18, }; for (let i in obj) {console.log(i, obj[i]); //name与age可以被枚举。isPrototypeOf不可枚举。 }
-
configurable 该成员是否可以删除,以及是否可以配置
//普通创建的对象都是 可修改 可删除 可枚举 let obj = {name: "lili",age: 18, }; let arr = [];obj.age = 88; delete obj["isPrototypeOf"]; //对象原型上的方法,是对象内置的。删除不了。 delete obj["name"]; //可以被删除。 console.log(`obj-->`, obj);
-
-
普通形式创建的对象的对象成员都是可修改、可删除、可枚举
实例对象与对象构造函数与对象原型的关系
const obj = new Object()
const 实例对象 = obj
const 对象构造函数 = Object
const 对象原型 = Object.prototype
obj.__proto__.constructor.prototype===Object//true
obj.__proto__===obj.__proto__.constructor.prototype//true
对象原型上的原型公有方法
console.dir(Object);
let obj={name:"lili",age:18
};
console.log(obj);
- 对象原型上原型公有方法7个,常用5个。在控制台中实例对象上如果要看原型链上属性,可以通过[[Prototype]]看到下面的东西
-
*constructor() -->执行自己的构造函数
- 也就是用new的形式调用,可以创建出来一个实例对象。
-
*hasOwnProperty() —>判断是否为私有属性
-
isPrototypeOf() —>返回一个布尔值,表明当前对象是否在某对象的原型链上
let obj = {name: "lili",age: 18, }; console.log(Object.prototype.isPrototypeOf(obj));//true let arr = []; console.log(Object.prototype.isPrototypeOf(arr));//true // arr--->Array.prototype--->Object.prototype-->null
-
propertyIsEnumerable() 返回一个布尔值,表明当前属性或方法是否是对象自有的且可枚举的。
-
是否能进行for-in循环。
let obj = {name: "lili",age: 18, }; console.log(obj.propertyIsEnumerable("age")); // true console.log(obj.propertyIsEnumerable("isPrototypeOf")); // false for (let i in obj) {console.log(i, obj[i]); //name与age可以被枚举。isPrototypeOf不可枚举。 }
-
-
toLocaleString() 返回对象的本地化字符串,与toString()基本没区别
-
*toString() 返回对象的字符串描述,专门做数据类型判断的(比typeof 更强大,可以细分对象)
let obj = {name: "lili",age: 18, }; console.log(obj.toString());//[object Object] console.log(obj.toLocaleString());//[object Object]
-
有一些基本对象上的toString()返回的值不一样,是因为它们的包装对象的原型上重写了toString()方法,它们的toString()被调用时,调用的是它们包装对象原型上的方法,而不是Object.prototype上的toString()。
//Array.prototype toString() 将数组转换为字符串 let arr = []; console.log(arr.toString()); //"" console.log(arr);
-
可以通过Object.prototype.toString.call()来间接调用。
let obj = {name: "lili",age: 18, }; console.log(obj.toString());//'[object Object]' console.log(obj.toLocaleString());//'[object Object]' let arr = []; console.log(Object.prototype.toString.call(arr));//'[object Array]'
-
备注:只有 Number.prototype与Date.prototype与Array.prototype上的toString()与toLocaleString()返回的值有区别
-
-
valueOf() 返回对象的原始值
let obj = {name: "lili",age: 18, }; console.log(obj.valueOf());//{name: 'lili', age: 18}
- 普通对象的原始值还是对象
-
toLocaleString()与toString()
- toLocaleString()与toString() 在Number.prototype与Date.prototype与Array.prototype上返回的值不一样,其它的常用对象基本上没区别。
-
是因为Number.prototype与Date.prototype与Array.prototype上的toString()、toLocaleString()已经被修改过了。
//日期和数值和数组类型 //Number.prototype toString()和toLocaleString() let theNumber = new Number(1234132435); console.dir(theNumber);//Number console.log(theNumber.toString());//'1234132435' console.log(theNumber.toLocaleString());//'1,234,132,435'//Date.prototype toString()和toLocaleString() let theDate = new Date(); console.dir(theDate);//Mon Apr 17 2023 23:02:49 GMT+0800 (中国标准时间) console.log(theDate.toString());//'Mon Apr 17 2023 23:02:49 GMT+0800 (中国标准时间)' console.log(theDate.toLocaleString());//'2023/4/17 23:02:49'//Array.prototype toString()和toLocaleString() let theArray = new Array(10022, 55552, 355, 4, 5); console.dir(theArray);//Array(5) console.log(theArray.toString());//'10022,55552,355,4,5' console.log(theArray.toLocaleString());//'10,022,55,552,355,4,5'
-
对象与对象成员的关系
- 实例对象上的对象成员分成几种情况
- 实例对象上没有的成员 值为undefined
- 实例对象上有的成员
- 对象自身上的私有成员 只在实例对象上的,不同实例的值一般不同
- 原型对象上的公有成员 在实例对象的原型上的成员,不同实例的值一般一样,但不同时期进行调用或访问时的得到的值可能不一样
- 直系原型对象上的公有成员 也就是
实例对象.__proto__
上的私有成员 - 非直系原型对象上的公有成员 就是沿着原型链一直找能找到的所有成员
- 直系原型对象上的公有成员 也就是
- 每一个对象对于自身所有成员有四种操作
-
新增成员
- 给一个没定义过的成员赋值
-
修改成员
- 给一个已经定义过的成员赋值
-
删除成员 : delete只能删除自身的私有成员
f = {name:666,f:555}; fang = Object.create(f,{key:{value:999}}); console.log(delete fang.name);//true console.log(f,fang)//{name: 666, f: 555} {key: 999} console.log(delete f.name);//true console.log(f,fang)//{f: 555} {key: 999}
-
查询成员
- 成员后面没有赋值语句
-
- 成员自身有四个描述值
- value值 决定访问或修改该成员时的值
- 方法
- 修改
- 访问
- 单纯访问
- 调用
- 非方法
- 修改
- 访问
- 方法
- enumerable 是否可枚举,能用for-in循环就是可枚举
- 内置类自带的属性,都是不可枚举
- writable 是否可以修改
- configurable 对否可以删除,以及是否可以配置
- value值 决定访问或修改该成员时的值
对象构造函数上静态方法
- 对象构造函数Object上的静态方法常见有22个
- *Objcect.assign(obj1,obj2,…) 合并对象
- *Object.create(XXX[,初始化的对象]) 创建一个对象,原型指向XXX
- Object.create(null) 创建一个没有原型链的空对象
- Object.hasOwn(对象,属性)–检查对象中是否含有指定属性
- 如果指定的对象具有指定的属性,则 Object.hasOwn() 方法返回 true。
- 如果属性被继承或不存在,则该方法返回 false。
- Object.fromEntries(arr) 将指定数组(二维数组)转换为对象
- Object.getPrototypeOf(XXX) 查找某个对象的原型对象
- *Object.setPrototypeOf(obj,原型对象(XXX)) 给obj 修改原型对象(XXX)
- Object.getOwnPropertyNames(XXX)方法返回一个数组,
成员是参数对象本身的所有属性的键名(可枚举),不包含继承的属性键名 - Object.getOwnPropertyDescriptor(obj,prop)
- 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性).
- 如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。
- Object.getOwnPropertyDescriptors(XXX)
- ES2017 引入了Object.getOwnPropertyDescriptors()方法,
- 返回指定对象所有自身属性(非继承属性)的描述对象
- Object.getOwnPropertySymbols(XXX)
- 返回一个给定对象自身的所有 Symbol 属性的数组
- *Object.is(XXX,XXX) 用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
- 特殊 Object.is(NaN,NaN) true Object.is(+0,-0) false
- *Object.keys(XXX)
- ES5 引入了Object.keys方法,返回一个数组,
- 成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
- *Object.values(XXX)
- Object.values方法返回一个数组,
- 成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
- *Object.entries(XXX)返回一个数组(二维数组),
- 成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组(二维数组)。
- *Object.freeze() 冻结一个对象,冻结指的是不能向这个对象添加新的属性,
- 不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。
- 也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。
- 不新增,不删除,不修改
- *Object.isFrozen(XXX)判断一个对象是否被冻结
- *Object.seal(XXX)方法可以让一个对象密封,并返回被密封后的对象。
- 密封一个对象会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。
- 属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。
- 但属性的值仍然可以修改。
- 可修改,不删除,不新增
- *Object.isSealed(XXX) 判断一个对象是否被密封(密封,冻结也是true)
- *Object.preventExtensions(XXX)对象不能再添加新的属性。 不可扩展
- 可修改,删除现有属性,不能添加新属性
- 可修改,可删除,不新增
- *Object.isExtensible(XXX)判断对象是否是可扩展的,
- Object.preventExtensions,Object.seal 或 Object.freeze 方法,都可以标记一个对象为不可扩展(non-extensible)—(false)
- *Object.defineProperty(obj,prop,descriptor)在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象自身。
- 定义新属性,可以具体定义该属性的四个成员规则
- 修改旧属性
- 修改现有属性的时候,成员规则中的writable必须为true,value不可用
- *Object.defineProperties(obj,props)直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
Object.defineProperty(obj,prop,descriptor)使用
Reflect反射对象
- ES6时新增的Reflect对象,目的应该是
- 可以让开发者通过调用这些方法,访问一些js底层功能由于它类似于其他语言的反射,因此取名为Reflect。
- 把命令操作函数化
- 把key in obj、delete这些方式函数化。
- 可以进行属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能。
- 把key in obj、delete这些方式函数化。
- Reflect的性能方面其实是有优势的。
- 把命令操作函数化
- Object里已有的明显属于元编程的函数挪到Reflect里,日后如果加新的元编程函数将只会加在Reflect里
- 挪动时如果发现Object里的原始函数行为不合理,在挪动后的新版上做修正
- Proxy支持,配合Proxy实例对象。
- 可以让开发者通过调用这些方法,访问一些js底层功能由于它类似于其他语言的反射,因此取名为Reflect。
- Reflect对象上的静态方法
-
Reflect.getPrototypeOf(target):类似于 Object.getPrototypeOf()。
-
Reflect.setPrototypeOf(target, prototype):设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。
-
Reflect.isExtensible(target):类似于 Object.isExtensible()
-
Reflect.preventExtensions(target):类似于 Object.preventExtensions()。返回一个Boolean。
-
Reflect.getOwnPropertyDescriptor(target, propertyKey) :类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined.
-
*Reflect.defineProperty(target, propertyKey, attributes):和 Object.defineProperty() 类似。如果设置成功就会返回 true
-
*Reflect.ownKeys(target):返回一个包含所有自身属性(不包含继承属性)的数组。(类似于Object.keys(), 但不会受enumerable影响)。
let obj = {name: "lili",fang: "5566", }; console.log(Reflect.ownKeys(Object.prototype));//['constructor', '__defineGetter__', '__defineSetter__', 'hasOwnProperty', '__lookupGetter__', '__lookupSetter__', 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'valueOf', '__proto__', 'toLocaleString']
-
Reflect.has(target, propertyKey) :判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
let obj = {name: "lili",fang: "5566", }; console.log(Reflect.has(obj, "name"));//true
-
*Reflect.get(target, propertyKey[, receiver]):获取对象身上某个属性的值,类似于 target[name]。
let obj = {name: "lili",fang: "5566", }; console.log(Reflect.get(obj, "name"));//lili console.log(obj);//{name: 'lili', fang: '5566'}
-
*Reflect.set(target, propertyKey, value[, receiver]):将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
let obj = {name: "lili",fang: "5566", }; Reflect.set(obj, "age", 18); console.log(obj);//{name: 'lili', fang: '5566', age: 18}
-
*Reflect.deleteProperty(target, propertyKey) :作为函数的delete操作符,相当于执行 delete target[name]。
let obj = {name: "lili",fang: "5566", };Reflect.deleteProperty(obj, "name"); console.log(obj);//{fang: '5566'}
-
Reflect.apply(target, thisArgument, argumentsList) :对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和Function.prototype.apply() 功能类似。
-
Reflect.construct(target, argumentsList[, newTarget]):对构造函数进行 new 操作,相当于执行 new target(…args)。
-
代理Proxy
-
Proxy(es6 新增 代理对象)+Reflect是vue3核心,了解一下
let obj = {n: {m: 100,},};//Proxy(es6 新增 代理对象)+Reflect vue3 了解//将 newObj 代理 obj.n ----以后直接操作 newObjlet newObj = new Proxy(obj.n, {get(target, prop) {//获取某个属性时触发//console.log(target,prop);console.log("get");return Reflect.get(target, prop);},set(target, prop, val) {//修改某个属性时触发console.log("set");return Reflect.set(target, prop, val);},deleteProperty(target, prop) {//删除某个属性时触发console.log("deleteProperty");return Reflect.deleteProperty(target, prop);},});newObj.m = 200;console.log(newObj.m);
vue2中mounted()及后面阶段更新非响应式数据
- 在vue2中,在mounted()模板编译之后及后面其它阶段里,页面渲染已经完成。
-
如果直接用this修改了响应式数据,那么数据是更新的,页面也会在之后同步进行更新。
-
直接用this修改了非响应式数据后,数据是更新的,页面并不会同步进行更新。
<!DOCTYPE html> <html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app"><input type="text" v-model="arr[0]" /><h1>{{arr}}---{{obj}}</h1></div><script>var vm = new Vue({el: "#app",data: {firstName: "李",obj: {n: {a: 10,},},arr: [10, 20, 30, { str: "hello" }],},mounted() {this.arr[5] = 500;this.obj.name = "lili";console.log(this.arr);console.log(this.obj);},});console.log(vm);</script></body> </html>
- 如果想同时进行更新,即数据更新+页面更新。有三种方式
-
this.$forceUpdate() 强制更新页面
<!DOCTYPE html> <html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app"><input type="text" v-model="arr[0]" /><h1>{{arr}}---{{obj}}</h1></div><script>var vm = new Vue({el: "#app",data: {firstName: "李",obj: {n: {a: 10,},},arr: [10, 20, 30, { str: "hello" }],},mounted() {this.arr[5] = 500;this.obj.name = "lili";console.log(this.arr);console.log(this.obj);this.$forceUpdate()},});console.log(vm);</script></body> </html>
-
this.$set(对象/数组,key/index,value)
<!DOCTYPE html> <html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app"><input type="text" v-model="arr[0]" /><h1>{{arr}}---{{obj}}</h1></div><script>var vm = new Vue({el: "#app",data: {firstName: "李",obj: {n: {a: 10,},},arr: [10, 20, 30, { str: "hello" }],},mounted() {this.$set(this.arr,5,520)this.$set(this.obj,"name","lili5")},});console.log(vm);</script></body> </html>
-
数组可用–重写的7大方法
- vue2里data的数据 通过 Object.defineProperty() 实现双向数据绑定的
- 数组也会get与set
- 数组内容是值类型(不会被get与set)
- 数组内容如果是对象(get与set)
- 为解决数组内容中值类型为非对象不进行监听跟踪,vue2重写了数组的七大方法,进一步提升对数组的操作
- pop push reverse shift sort splice unshift—>只要调用7个方法之一,就会更新页面
- 数组也会get与set
<!DOCTYPE html> <html><head><meta charset="utf-8" /><title></title><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body><div id="app"><input type="text" v-model="arr[0]" /><h1>{{arr}}---{{obj}}</h1></div><script>var vm = new Vue({el: "#app",data: {firstName: "李",obj: {n: {a: 10,},},arr: [10, 20, 30, { str: "hello" }],},mounted() {//3.数组可用--重写的7大方法this.arr.push(600);},});console.log(vm);</script></body> </html>
- vue2里data的数据 通过 Object.defineProperty() 实现双向数据绑定的
-
- 如果想同时进行更新,即数据更新+页面更新。有三种方式
-
进阶参考
- Object函数的内置方法 Object.defineProperties
- Object.prototype解析
- Reflect对象
- ES6中的Reflect对象重点梳理
- es6中对于反射Reflect的理解
- ES6设计反射Reflect的意义是什么?