hasOwnProperty
=> Object.prototype.hasOwnProperty() ->所有实例对象都可以访问
=> 作用: 判断属性或方法是不是对象本身自带的
=> 沿原型链上查找的方法不是它自身的
<script>
let obj={
name:'jack',
age:18,
}
Object.prototype.score=100
//判断age属性是否是对象obj的
// let isOk=obj.hasOwnProperty('name')
console.log('obj.score',obj.score) //100
let isOk=obj.hasOwnProperty('score')//不会查找他的原型链 所以为false
console.log(isOk)
let arr=[]
console.dir(arr)
console.log(arr.hasOwnProperty('length')) //true
console.log(arr.hasOwnProperty('find')) //false
</script>
Object.defineProperty()
作用:
- 给对象动态添加属性
- 将对象属性绑定到另一个对象上
- 数据劫持-监听对象数据变化, 实现数据变化自动更新界面 (vue2.x实现原理应用)
语法
Object.defineProperty(obj, prop, descriptor)
参数
obj : 要定义属性的对象。
prop:要定义或修改的属性的名称或 Symbol 。
descriptor:要定义或修改的属性描述符对象。返回值
被传递给函数的对象。
descriptor :
- configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
- enumerable:表示能否通过for in循环访问属性,默认值为false
- writable:表示能否修改属性的值。默认值为false。
- value:包含这个属性的数据值。默认值为undefined。
<script>
function test1() {
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)
for (const key in obj) {
console.log(key, obj[key])
}
}
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)
</script>
get:在读取属性时调用的函数,默认值是undefined
set:在写入属性的时候调用的函数,默认值是undefined
<script>
let obj={}
Object.defineProperty(obj,'age',{
get(){
console.log('get ');
return 18
},
set(newValue){
console.log('set:',newValue);
},
})
</script>
Object.defineProperty作用
- 将对象属性绑定到另一个对象上
- 数据劫持-监听对象数据变化
- 实现数据变化自动更新界面
<body>
<div>
<p class="title">jack</p>
</div>
<script>
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() {
console.log('get >>>> ')
return obj[key]
},
// vm.name = 'rose'
set(newValue) {
// 原值与新值相同直接返回
if (obj[key] == newValue) {
return
}
obj[key] = newValue // 设置属性新值
console.log('set >>>> ')
// 监听对象数据变化后执行其它操作
// 更新界面
const titleEle = document.querySelector('.title')
titleEle.innerHTML = obj.name
},
})
}
</script>
</body>
此时可以看出,页面的数据随之变化
Proxy代理:
代理是什么?简单理解就是我们不直接对对象、函数或者数组进行操作,而是把它挂载到Proxy(代理)上,直接对代理的做一系列操作。我们去买房,房产中介就相当于我们的代理,我们不需要直接向卖家沟通。
代理是目标对象的抽象。目标对象既可以直接被操作,也可以通过代理来操作。 但直接操作会绕过代理施予的行为。
首先就是空代理,就是什么也不做,在代理对象上执行的所有操作都会无障碍地传播到目标对象。
代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。缺少其中任何一个参数都会抛出 TypeError。
捕获器一共13种
apply 、construct、defineProperty、deleteProperty、
get、getOwnPropertyDescriptor、getPrototypeOf、has、isExtensible
ownKeys、preventExtensions、set、setPrototypeOf
Proxy支持的拦截操作
get(target(目标对象),propkey(属性名称),receiver(实例本身))
set( target(目标对象),propkey(属性名称),value(值),receiver(实例本身) ) 方法是设置属性值操作的捕获器。
has( target(目标对象),propkey(属性名称)) 对in操作符的代理方法。
deleteProperty()有一个普遍的约定,即以下划线 _ 开头的属性和方法是内部的。不应从对象外部访问它们。
function test1() {
// 目标对象
let obj = {
name: 'jack',
age: 18,
}
// 处理程序
let handler = {}
// 代理对象
let proxyObj = new Proxy(obj, handler)
// 通过代理对象操作目标对象和直接操作目标对象是一样的效果,
console.log('obj.name ', obj.name)
console.log('proxyObj.name ', proxyObj.name)
proxyObj.name = 'rose' // 通过代理对象更改目标对象属性值
console.log('obj.name ', obj.name)
}
// 通过代理对象操作目标对象时,可以对目标对象做一些拦截操作, 在处理程序handler中设置
// 目标对象
let obj = {
name: 'jack',
age: 18,
}
// 处理程序
let handler = {
// 通过代理对象获取目标对象属性值时触发
// get(){
// return '这是代理给的新值'
// }
// get参数
get(target,propery,receiver){
console.log('target ',target)
console.log('propery ',propery)
console.log('receiver ',receiver)
return target[propery]
},
// 通过代理对象给属性设置值触发
set(target,propery,value,receiver){
console.log('set >>>');
console.log('target ',target)
console.log('propery ',propery)
console.log('value ',value)
console.log('receiver ',receiver)
target[propery] = value
}
}
// 代理对象
let proxyObj = new Proxy(obj, handler)
// console.log( proxyObj.age )
proxyObj.age = 20
深拷贝与浅拷贝
一、深拷贝和浅拷贝的概念
深拷贝和浅拷贝的概念只适用于对象或者数组这种复杂数据类型(引用数据类型)。
浅拷贝:只是拷贝数据的内存地址,而不是在内存中重新创建一个一模一样的对象(数组),复制的对象只复制一层,如果对象属性值是对象则不能复制。
深拷贝:在内存中开辟一个新的存储空间,完完全全的拷贝一整个一模一样的对象(数组),完全复制数组的对象,如果对象属性值是对象一起复制得到全新对象
不论是number,string,boolean还是object,array都会被存储在内存中。
而内存又被分为栈内存和堆内存。
基本数据类型比如:string,number,boolean,null,undefined等等会被直接存储到栈内存中。
而像数组,对象等由多种基本数据类型组成的复杂数据类型,他们的实体内容则会被存储到堆内存中。栈内存只会存储他们在堆内存的一串地址。
二、浅拷贝
浅拷贝的意思就是,复制了对象(数组)存储在栈内存中的地址,而不是在内存中重新开辟一个新的存储空间用于存储新的对象。也就是两个对象共用一个内容。其值也会随着改变而该变。
三、深拷贝
深拷贝不同于浅拷贝的是。不比浅拷贝只会拷贝栈内存中的数据地址。深拷贝会在内存中重新开辟一段新的存储空间。使得两个对象(数组)指向两个不同的堆内存数据。从而实现改变互不影响。
总结
- 深拷贝和浅拷贝的概念只针对复杂数据类型。
- 深拷贝可以通过JSON的stringify方法和parse方法进行(但是有坑,不推荐使用)
- 浅拷贝是只拷贝复杂数据类型的在栈内存中的地址。本质上两个对象(数组)指向的还是同一块堆内存数据空间
- 深拷贝会在堆内存中重新生成一块新的存储空间实现完完全全的对象(数组)复制
1. JSON.parse(JSON.stringify(obj))
缺点: 数据类型是Function或数据值为undefined无法拷贝
2. Object.assign(obj)或展开运算符{…obj}
缺点:只能拷贝一层,如果属性值是对象,无法拷贝
JSON.stringify(obj)
obj -> 字符串
注: 只能是Object形式对象不是Math,Date...
JSON.parse(str)
字符串 -> obj
注: 字符串必须 是 json格式