目录
一、对象方法Object.prototype.hasOwnProperty()
二、构造函数类方法Object.defineProperty()
1.构造函数类方法Object.defineProperty()
3.构造函数类方法Object.defineProperty()的作用示例
前言
今天我们主要学习对象详解,主要介绍处理对象三个方法,原型对象上的方法Object.prototype.hasOwnProperty(),构造函数类方法Object.defineProperty() ,Proxy代理,以及在面试中经常问到的深浅拷贝。
一、对象方法Object.prototype.hasOwnProperty()
hasOwnProperty表示是否有自己的属性。这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链
应用:
通过原型链继承该方法
判断属性是否是对象自身的
语法:
let 引用变量=实例对象.hasOwnProperty(属性或方法)
代码如下:(示例)
let obj = {
name: 'jack',
age: 18,
}
Object.prototype.score = 100
// 判断属性age是否是对象obj的
// let isOk = obj.hasOwnProperty('name')
console.log('obj.score ',obj.score)
let isOk = obj.hasOwnProperty('score')
console.log('isOk ', isOk)
let arr = []
console.dir(arr)
console.dir(Array.prototype);
console.log(arr.hasOwnProperty('length') )
console.log(arr.hasOwnProperty('find') )
二、构造函数类方法Object.defineProperty()
1.构造函数类方法Object.defineProperty()
作用:①将对象属性绑定到另一个对象上
②数据劫持-监听对象数据变化,实现数据变化自动更新界面
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
语法 :
Object.defineProperty(obj, prop, descriptor)
参数
obj : 要定义属性的对象。
prop:要定义或修改的属性的名称或 Symbol 。
descriptor:要定义或修改的属性描述符对象。返回值
被传递给函数的对象。
2.属性描述对象:descriptor
1)数据属性
①configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
②enumerable:表示能否通过for in循环访问属性,默认值为false
③writable:表示能否修改属性的值。默认值为false。
④value:包含这个属性的数据值。默认值为undefined。
代码如下:(示例)
let obj = {}
Object.defineProperty(obj, 'name', {
value: 'jack',
configurable: true, // 允许删除属性
writable: true, //允许修改属性值
enumerable: true, // 遍历对象for-in
})
obj = {name:'jack'}
console.log(obj.name)
delete obj.name // 删除对象属性
obj.name = 'rose' // 修改对象属性值
console.log(obj.name)
2)访问器属性
①get:在读取属性时调用的函数,默认值是undefined
②set:在写入属性的时候调用的函数,默认值是undefined
代码如下:(示例)
let obj = {}
Object.defineProperty(obj,'age',{
// 访问对象age属性时自动调用 obj.age
get:function(){
console.log('get >>>> ')
return 20
},
// 赋值给对象属性age时自动调用并赋值
set(newValue){
// this.age = newValue
console.log('set >>>> ')
}
})
console.log('111',obj.age)
obj.age = 18
console.log('222',obj.age)
3.构造函数类方法Object.defineProperty()的作用示例
1)动态将对象属性绑定到另一个对象上
代码如下:(示例)
let obj = {
name: 'jack',
age: 18,
}
let vm = {}
// 将obj对象的所有属性动态添加到vm对象上
//1. 遍历obj对象
for (const key in obj) {
// 2. 动态给vm添加属性
Object.defineProperty(vm, key, {
// vm.name
get() {
return obj[key]
},
// vm.name = 'rose'
set(newValue) {
// 原值与新值相同直接返回
if (obj[key] == newValue) {
return
}
obj[key] = newValue // 设置属性新值
},
})
}
2)数据劫持-监听对象数据变化,实现数据变化自动更新界面
代码如下:(示例)
<p class="title">jack</p>
<script>
let obj={
name:'jack',
age:18
}
let vw={}
for(const key in obj){
Object.defineProperty(vw,key,{
get(){
return obj[key]
},
set(newValue){
if(obj[key] === newValue){
return
}
obj[key]=newValue
//动态改变p内容
const titleEle=document.querySelector('.title')
titleEle.innerHTML=obj.name
}
})
}
</script>
三、Proxy代理
1.什么是代理
简单理解就是我们不直接对对象、函数或者数组进行操作,而是把它挂载到Proxy(代理)上,
直接对代理的做一系列操作。我们去买房,房产中介就相当于我们的代理,我们不需要直接向卖家沟通。
2.JS代理Proxy
代理是目标对象的抽象。目标对象既可以直接被操作,也可以通过代理来操作。 但直接操作会绕过代理施予的行为。
首先就是空代理,就是什么也不做,在代理对象上执行的所有操作都会无障碍地传播到目标对象。
代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。缺少其中任何一个参数都会抛出 TypeError。
通过代理对象操作目标对象时,可以对目标对象做一些拦截操作, 在处理程序handler中设置,这就要使用到捕获器(trap)
3.捕获器(trap)
使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的“基本操作的“拦截器”。
每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接 或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对 象之前先调用捕获器函数,从而拦截并修改相应的行为
捕获器一共13种
apply 、construct、defineProperty、deleteProperty、
get、getOwnPropertyDescriptor、getPrototypeOf、has、isExtensible
ownKeys、preventExtensions、set、setPrototypeOf
接下来我们主要介绍get与set两种捕获器
1)get捕获器
get捕获器通过代理对象获取目标对象属性值时触发
get参数:get(target,propery,receiver){ }
target:目标对象 propery:属性 receiver:处理程序
2)set捕获器
set捕获器通过代理对象给属性设置值触发
set参数:set(target,propery,value,receiver){ }
target:目标对象 propery:属性 value:设置的新值 receiver:处理程序
4.Proxy代理应用
通过Proxy代理目标对象obj,实现数据劫持-通过代理对象proxy改变目标对象属性值时,
在get,set捕获器方法中做一些处理, 比如更新界面
代码如下:(示例)
<div></div>
<script>
let obj={
message:''
}
let proxyObj=new Proxy(obj,{
get(target,propery){
return target[propery]
},
set(target,propery,value){
target[propery]=value // 通过代理对象改变目标对象属性值
// 更新界面操作-更改div内容
document.querySelector('div').innerHTML=value
}
})
proxyObj.message='hello'
</script>
四、深拷贝和浅拷贝
1.深拷贝和浅拷贝
浅拷贝 – 复制的obj对象只复制一层,如果对象属性值是对象则不能复制
深拷贝 – 完全复制的obj对象,如果对象属性值是对象一起复制得到全新对象
深拷贝和浅拷贝内存图:
深拷贝:
浅拷贝:
2.实现深浅拷贝的方式
1)展开运算符...obj实现浅拷贝
缺点:只能拷贝一层,如果属性值是对象,无法拷贝
代码如下:(示例)
let obj = {
name: 'jack',
age: 21,
score: 98,
fun: {
swiming: '游泳'
}
}
let newObj = {
...obj
}
newObj.name = 'rose'
newObj.fun.swiming = '打游戏'
console.log('newObj :', newObj.name, ' oldObj :', obj.name);
console.log('newObj fun :', newObj.fun.swiming, ' oldObj fun :', obj.fun.swiming);
console.dir(obj)
console.dir(newObj)
运行结果:
2)JSON.parse(JSON.stringify(obj))实现深拷贝
JSON对象
JSON.stringify(obj)
obj -> 字符串
注: 只能是Object形式对象不是Math,Date...
'{name:"jack",age:18,fun:{swiming:"游泳"}}' <- JSON字符串
JSON.parse(str)
字符串 -> obj
注: 字符串必须 是 json格式
代码如下:(示例)
let obj = {
name: 'jack',
age: 21,
score: 98,
fun: {
swiming: '游泳'
}
}
let newobj=JSON.parse(JSON.stringify(obj))
newobj.fun.swiming='打游戏'
newobj.age=20
console.dir(obj)
console.dir(newobj)
运行结果:
3)递归 cloneDeep()实现深拷贝
代码如下:(示例)
let obj = {
name: 'jack',
age: 21,
score: 98,
fun: {
swiming: '游泳'
}
}
function cloneobj(obj){
let newObj=Array.isArray(obj) ? []:{}
for(const key in obj){
if(obj[key] && typeof obj[key] === 'object'){
newObj[key] =cloneobj(obj[key])
}else{
newObj[key]=obj[key]
}
}
return newObj
}
let newObj = cloneobj(obj)
newObj.fun.swiming='打游戏'
newObj.age=25
console.dir(obj)
console.dir(newObj)
运行结果: