xiaochengzi6 / Blog

个人博客
GNU Lesser General Public License v2.1
0 stars 0 forks source link

数据劫持、数据绑定和双向绑定 #36

Open xiaochengzi6 opened 2 years ago

xiaochengzi6 commented 2 years ago

属性描述符

所有的属性都具备了属性描述符【数据描述符】,它有四个特性

{
    value: ,
    configurable: ,
    writable: ,
    enumerable: ,
}

value特性:保存属性的值,configurable特性:属性是否可配置,属性是否能删除;enumerable特性:属性可枚举,除了value其余三个的值为布尔类型,在创建一个对象的属性的时候使用默认值

var obj = {};
obj.a = 3

//默认值
{
    value: 3 ,
    configurable: true,
    writable: true,
    enumerable: true,
}

使用Object.defineProperty(...)来添加一个属性或设置一个属性。

var obj = {};
Object.defineProperty(obj,'a',{
    value: 3 ,
    configurable: true,
    writable: true,
    enumerable: true,
})
obj.a //3

如何判断什么时候添加还是设置一个属性呢看一个例子:

let object = {
    a: 'ss'
}
let uu = Object.create(object);
uu.a //return 'ss'
Object.defineProperty(uu, 'a', {
    configurable: true,
    value: 2,
})

在这里是创建还是修改一个对象的属性呢

//hasOwnProperty()判断一个属性是否在它自身上
console.log(uu.hasOwnProperty('a')) //true

可以看到是明显的创建属性

uu.__proto__.a //return 'ss'
uu.a // return 2
//属性的屏蔽的作用

在设置input的数据劫持中想通过这样的方式来绑定数据

//html: <input type="number" class="input" value="3">
let input = document.querySelector('.input');
input.addEventListener('click', function () {
    Object.defineProperty(input, 'value', {
        get() {
            console.log('get')
        },
        set(){
            console.log('set')
        }
    })
}, false)

在浏览器中并不能返回想要的答案

input.value // return 3   在HTML中input的value=3
input.hasOwnProperty('value') //false

value属性并不是存在于自身,这个value是html特性映射过来的dom属性,input继承的是x的(object)value的属性,在Object.defineProperty操作中就会出现问题,这里就相当于创建了一个value的属性。

这里还有一个问题怎样判断属性来源于那个对象

对象属性的设置的过程

var obj = {};
obj.a = 'NO'
obj.a //return 'NO'

let objs = {
    foo: '123',
};
let per = Object.creat(objs);
per.foo //return '123'
per.foo = 'jj'; 
per.foo === 'jj';
per.__proto__.foo === '123';

属性发生了屏蔽
//这里会有几个过程
1. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性(参见第3章)并且没有被标记为只读(writable:false),那就会直接在myObject中添加一个名为foo的新属性,它是屏蔽属性。

2. 如果在[[Prototype]]链上层存在foo,但是它被标记为只读(writable:false),那么无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。

3. 如果在[[Prototype]]链上层存在foo并且它是一个setter(参见第3章),那就一定会调用这个setter。foo不会被添加到(或者说屏蔽于)myObject,也不会重新定义foo这个setter。

如果你希望在第二种和第三种情况下也屏蔽foo,那就不能使用=操作符来赋值,而是使用Object.defineProperty(..)(参见第3章)来向myObject添加foo。

访问器描述符

当给一个对象的属性定义 get set 或者两者都有时这个属性就会被定义为访问描述符,

let text = document.querySelector('.text');
input.addEventListener('click', function () {
    Object.defineProperty(text, 'value', {
        get() {
            console.log('get')
        },
        set(){
            console.log('set')
        }
    })
}, false)

更关注于 get set这样的属性和configurable和enumerable特性,忽略value 和wriable特性

判断存在性

 1. in 操作符检查属性是否在对象或者他的原型中
 2. object.hasOwnProperty(..) 判断属性是否在其对象中
 3. object.propertyIsEnumerable(..)检查给定的属性名是否存在对象中而不是原型链中并且满足 enumerable = true

实现一个简单的数据绑定功能

<body>
    <div id="app">
        <input type="number" id="txt">
        <p id="show-txt"></p>
    </div>
</body>
<script>
    var obj = {}
    let txt = document.querySelector('#txt');
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj
        },
        set: function (newValue) {
            document.getElementById('txt').value = newValue
            document.getElementById('show-txt').innerHTML = newValue
        }
    })
    txt.addEventListener('click', function (e) {
        obj.txt = e.target.value
    })
</script>

原理:

页面input点击后把他的value赋值给一个对象的属性,通过劫持这个对象的属性开始判断页面是否发生变化,input变化后页面也随之变化