一、vue响应式使用的底层方法
vue框架
-
vue2.0 : Object.defineProperty()
-
vue3.0 : Proxy
-
他们的目的都是为了实现响应式
-
什么是响应式?
- 我们只需要操作数据,这个框架帮我们操作dom,不推荐我们使用dom操作
- 响应式:数据的更新会导致视图的更新
二、Object.defineProperty
- 在对象上定义一个新属性,或修改现有属性
- 语法:Object.defineProperty(obj, prop, descriptor)
- obj:要定义属性的对象
- prop:要定义或修改的属性的名称
- descriptor:要定义或修改的属性描述符
- configurable: 属性描述符是否可以改变以及属性是否可以删除, 默认 false
- enumerable: 属性是否可以for…in循环出来(遍历/枚举),默认false
- value: 该属性对应的值, 默认 undefined。
- writable: 属性的值是否可以修改,默认为 false
- get:默认undefined,需要设置一个函数,当访问该属性时,会调用此函数,不能传参,该函数的返回值会被用作属性的值。 默认为 undefined
- set:默认undefined,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined。
configurable | enumerable | value | writable | get | set |
---|---|---|---|---|---|
数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 |
存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 |
- 返回值: 被传递给函数的对象
三、实现简单响应式
<body>
<span></span>
<script>
// 需求:实现数据的变化导致界面的更新
// 要监控数据的变化,这个数据必须是对象的属性值
let data = {}; // data对象里面专门定义和界面有关的属性
// text属性是和span这个标签相关的属性
// 只要text属性值变化,span的内容也变化
Object.defineProperty(data,'text',{
set:function(val){
// 发现你在设置data的text属性
// 我就把你设置的值,用来更新span的内容
let sp = document.querySelector('span');
sp.innerHTML = val;
}
})
// 写一个计数器,每一秒,更新span里面的数字
let num = 0;
setInterval(()=>{
num++;
// 要更新span的界面,其实只要更新data的text属性值
data.text = num;
},1000)
</script>
</body>
四、实现简单双向数据绑定
<body>
<input type="text" value="sz2209">
<h3>hello world</h3>
<script>
/*
双向数据绑定
== 数据的更新会导致界面的更新
== 界面的更新会导致数据的更新
*/
// 双向数据绑定 => 要能监控数据的变化和界面的更新
// 1 数据的更新会导致界面的更新
let data = {} ;// data里面记录和界面有关的属性值
// input的value,h3的innerHTML都受data的banji属性控制
Object.defineProperty(data,'banji',{
set:(val)=>{
// 获取到你要设置的值,赋值给input的value,h3的innerHTML
document.querySelector('input').value = val;
document.querySelector('h3').innerHTML = val;
}
})
// 更新数据
data.banji = 'sz2303'
// 2 界面的更新会导致数据的更新
// 通过 input事件和value 监控文本框里面内容的变化
document.querySelector('input').oninput = function(){
// 界面更新,获取界面的表单的value赋值给data.banji
data.banji = this.value;
// 事件处理函数中的this就是事件源
}
</script>
</body>
五、Proxy
创建对象代理
- Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
- 语法:
const p = new Proxy(target, handler)
- 参数
- target:使用 Proxy 包装的目标对象
- handler:是一个成员值是函数的对象,定义各种操作对象属性时调用的函数,常见有:
- has:in 操作符的捕捉器
- get:属性读取操作的捕捉器
- 第一个参数:目标对象
- 第二个参数:要操作的属性名
- set:属性设置操作的捕捉器
- 第一个参数:目标对象
- 第二个参数:要操作的属性名
- 第三个参数:要设置额属性值
- deleteProperty:delete 操作符的捕捉器
- construct:new 操作符的捕捉器
- apply:函数调用操作的捕捉器
- 第一个参数:目标函数
- 第二个参数: 函数中的this
- 第三个参数:函数的实参集合
六、hasOwnproperty
识别自身属性
class Person{
constructor(){
this.name = "lucy";
this.age = 12
}
sayHi(){
console.log('hello world')
}
}
let p1 = new Person()
// in运算符(属性名 in 对象)
// 情况1:对象自身属性
"age" in p1//true
// 情况2:对象继承的属性
"sayHi" in p1 //true
// 总结:in运算符 不仅能识别对象自身的属性 也能识别继承的属性
// hasOwnProperty 只能识别对象自身的属性
p1.hasOwnProperty("age")//true
p1.hasOwnProperty("sayHi")// false,p1 本身没有toString属性
七、深拷贝和浅拷贝
浅拷贝
- 创建一个新对象,这个对象有着原始独享属性的一份精确拷贝,如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果属性是引用数据类型,拷贝的 就是内存地址
深拷贝
- 将一个对象从内存中完整的拷贝一份出来,从对堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象.