Object.prototype.hasOwnProperty()
hasOwnProperty表示是否有自己的属性。这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链
应用:通过原型链继承该方法判断属性是否是对象自身的
var obj = {
a: 1,
fn: function(){
},
c:{
d: 5
}
};
console.log(obj.hasOwnProperty ('a'));// true
console.log(obj.hasOwnProperty ('fn'));// true
console.log(obj.hasOwnProperty ('c'));// true
console.log(obj.c.hasOwnProperty ('d'));// true
console.log(obj.has0wnProperty ('d'));// false,obj对象没有d属性
var str = new String();
// split方法是String这个对象的方法,str对象本身是没有这个split这个属性的
console.log(str.hasOwnProperty ('split'));// false
console.log(String.prototype.hasOwnProperty('split'));// true
Object.defineProperty()
-
方法:
直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 -
语法 :Object.defineProperty(obj, prop, descriptor)
-
参数
obj:要定义属性的对象。
prop:要定义或修改的属性的名称或 Symbol。
descriptor:要定义或修改的属性描述符对象。 -
返回值
被传递给函数的对象。
descriptor 属性描述符对象
-
数据属性
1.configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
2.enumerable:表示能否通过for in循环访问属性,默认值为false
3.writable:表示能否修改属性的值。默认值为false。
4.value:包含这个属性的数据值。默认值为undefined。let obj = {} object.defineProperty(obj,'name', { value: 'jack', configurable: true,//是否可以删除属性 writable: false,//只读,不能修改name属性 enumerable: true,//for-in遍历属性 }) console.log(obj) // delete obj.name//删除 obj.name = 'rose'//修改console.log(obj //遍历 for (const key in obj){ console.log(key, obj[key]) } -
访问器属性
1.get:在读取属性时调用的函数,默认值是undefined
2.set:在写入属性的时候调用的函数,默认值是undefined<script> let obj = {} object.defineProperty(obj, 'age', { get() { console.log('get'); return 18 }, set(newvalue) { console.log('set:', newValue);}, }) </script>
作用
-
将对象属性绑定到另一个对象上
<script> let obj = { name: "jack ", age: 18, } let vm = {0} //遍历obj对象key,将obj对象属性绑定到vm对象上 object.keys(obj).forEach(key => { object.defineProperty(vm,key,{ get() { return obj[key] }, set(newValue) { if(obj[key] === newValue){ return } obj[key] = newValue }, }) } </script> ========================================= let obj = { name: "jack ',age: 18, } let vm = {} //遍历obj对象key,将obj对象属性绑定到vm对象上 for(const key in obj){ object.defineProperty(vm, key,{ get() { return obj[key] }, set(newValue) { if(obj[key] === newValue){ return } obj[key] = newValue), } }) } -
数据劫持,监听对象数据变化,实现数据变化自动更新界面
<body> <div id="app"> <h2</h2> </div> <script> //数据message动态显示到h2标签上,message变化h2的内容自动更新 const data = { message: 'hello', } const vm = {} //1.遍历数据对象上所有属性 for (let key in data) { //2.使用object.defineProperty()绑定属性到vm对象上, //vm对象上属性值变化自动触发get,set方法 object.defineProperty(vm,key, { geto { return data[key] }, //value属性的值 set(value){ if (data[key] === value) return document.queryselector('h2').innerHTML = value }, }) } </script> </body>
Proxy
-
代理是什么?
简单理解就是我们不直接对对象、函数或者数组进行操作,而是把它挂载到Proxy(代理)上,直接对代理的做一系列操作。我们去买房,房产中介就相当于我们的代理,我们不需要直接向卖家沟通。 -
JS代理Proxy
代理是目标对象的抽象。目标对象既可以直接被操作,也可以通过代理来操作。 但直接操作会绕过代理施予的行为。 首先就是空代理,就是什么也不做,在代理对象上执行的所有操作都会无障碍地传播到目标对象。
代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。缺少其中任何一个参数都会抛出 TypeError。function test1() { //目标对象 const obj = { name: 'jack ', age: 18, } //处理程序对象 const handler = 0 //代理对象 const proxy = new Proxy(obj, handler) //代理访问对象 console.log( 'obj.name: ', obj.name, ' proxy.name : ', proxy.name) //obj.name : jack proxy . name : jack //代理改变对象 proxy.name = 'rose' console.log( 'obj.name : ', obj.name, ' proxy.name : ', proxy.name) //obj.name : rose proxy.name : rose } -
捕获器(trap)
使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的基本操作的“拦截器”。每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为
1.捕获器一共13种
apply 、construct、defineProperty、deleteProperty、get、getOwnPropertyDescriptor、getPrototypeOf、has、isExtensible、ownKeys、preventExtensions、set、setPrototypeOf
2.get()
const target = {
foo:'bar'
}
const handler = {
get() {
return 'handler override';
}
}
const proxy = new Proxy(target, handler);
console.log(target.foo) //bar
console.log(proxy.foo) //handler override
==========================================
function test3() {
const obj = {
name: 'jack',
age: 18,
}
const handler = {
get: function (target, property, receiver) {
console.log('get >>> target :', target)
},
}
}
3.srt()
让proxy数组只接受数字,当然如果是数字要返回true,不然只要返回false就会触发TypeError
let num = [];
const handler = {
set(target,property, value) {
if (typeof value == 'number' ) {
target[property] = value;
console.log ('insert success');
return true;
}else {
console.log ('this is not a number! ' );
return false;
}
}
}
let proxy = new Proxy(num, handler);
proxy.push(1);
proxy.push(2);
console:log(proxy, length); //2
proxy.push('test'); //TypeError: 'set' om proxy: trap
4.ownKeys()
let user = {
name:'Sonic' ,
age: 18,
_pwd : '123'
};
let handler = {
ownkeys (target) {
return Object.keys(target).filter ( key => ! key. startswith( '_' ));
}
}
let proxy = new Proxy(user,handler);
for (let key in proxy){
console.log ( key);
}
//name
// age
console.log(0bject.keys(proxy));// [ 'name ', 'age' ]
console.log(0bject.values(proxy));// [ 'Sonic' , 18 J
5.deleteProperty()
有一个普遍的约定,即以下划线 _ 开头的属性和方法是内部的。不应从对象外部访问它们。
et user = {
name: 'Sonic',
age: 15,
_pwd: '123456'
};
let handler = {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error('Access defined');
}
let value = target[prop];
return (typeof value === 'function') ? value.bind(target) : value;
},
set(target, prop, val) {
if (prop.startsWith('_')) {
throw new Error('Access defined');
} else {
target[prop] = val;
return true;
}
},
deleteProperty(target, prop) {
if (prop.startsWith('_')) {
throw new Error('Access defined');
} else {
delete target[prop];
return true;
}
},
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'));
}
};
let proxy = new Proxy(user, handler);
try {
console.log(proxy._pwd);
} catch (e) {
console.log(e.message);
};
try {
proxy._pwd = '456789';
} catch (e) {
console.log(e.message);
};
try {
delete proxy._pwd;
} catch (e) {
console.log(e.message);
};
for (let prop in proxy) console.log(prop);
-
应用vue3.x数据劫持
<h2 id="app">数据劫持</h2> <script> //模拟vue中的data 选项let data = { msg: "hello", count: e, } //模拟 vue实例 let vm = new Proxy(data,{ //当访问vm的成员会执行 get(target, key){ console.log( 'get, key: ", key,target [key]) return target[key] } //当设置vm的成员会执行 set(target,key,newValue) { console.log( 'set, key: ", key,newValue) if (target[key] === newValue) { return } target[key] = newValue document.querySelector('#app').textContent = target[key] }, }) // data = vm //测试 vm.msg ='Hello world' console.log(vm.msg) </script>
对象代理
const name = ititle: "Joker"};
const proxy = new Proxy(name,{
get(obj, property) {
return obj[property] ;
},
set(obj, property, value) {
obj[property] = value;
return true;
}
});
console.log(proxy.title); //Joker
proxy.title = "newJoker" ;
console.log(proxy.title) // newJoker
函数代理
function factorial(num) {
return num === 1 ? 1 : num* factorial(num - 1)
}
let proxy = new Proxy(factorial,{
apply(func, obj, args) {
console.log (func.apply(this,args)) ; //120
}
});
proxy(5)
数组使用代理
let arr = [iname: "Joker" , age: 19},{name: "newJoker" , age: 20}];
let proxy = new Proxy(arr, {
get(array,key) {
console.log(array) ;
console.log(array [ key].name) ;
}
});
proxy[0];
proxy[1];
深拷贝与浅拷贝
拷贝 - 复制obj对象得到一个全新的对象
- 浅拷贝 – 复制的obj对象只复制一层,如果对象属性值是对象则不能复制
- 深拷贝 – 完全复制的obj对象,如果对象属性值是对象一起复制得到全新对象
实现方式
-
JSON.parse(JSON.stringify(obj))
缺点: 数据类型是Function或数据值为undefined无法拷贝const obj = { name: "jack",age: 18, hobby: iswiming: "游泳"}, arr: [{score: 98 }], say: () =>{}, //Function无法拷贝 number: undefined, // undefined无法拷贝 } -
Object.assign(obj)或展开运算符{…obj}
缺点:只能拷贝一层,如果属性值是对象,无法拷贝const obj = { name: "jack", age: 18, hobby: iswiming:"游泳"},// object无法拷贝 arr: [{score: 98}], // Array无法拷贝 say: () => {}, //Function无法拷贝 number: undefined, //undefined } -
递归 cloneDeep()
const cloneDeep = data => { const newData = Array.isArray(data) ? [] : {} for (let key in datay { if(data[key] && typeof data[key] === 'object') { newData[key] = cloneDeep(data[key]) } else { newData[key] = data[ key] } } return newData } //test递归-拷贝 const testcloneDeep = () => { const obj = { name: "jack", age: 18, hobby: {swiming:"游泳"},} // object arr: [{ score: 98 }], say: () => {}, //Function number: undefined, //undefined students: [ { student: { num: { id: 1, }, }, }. }, } const cloneobj = cloneDeep(obj) cloneobj.name= "rose" cloneobj.age = 20 cloneobj.hobby.swiming = "不会游泳" cloneobj.arr[0].score= 100 cloneobj.students[0].student.num.id = 100 console.log('obj:', obj, "\n cloneobj :', cloneobj) console.log(cloneobj.students[0].student.num.id === obj.students[0].student.num.id)