欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳談Object.defineProperty 及實現數據雙向綁定

 更新時間:2020年07月18日 15:38:35   作者:龍恩0707  
這篇文章主要介紹了詳談Object.defineProperty 及實現數據雙向綁定,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

Object.defineProperty() 和 Proxy 對象,都可以用來對數據的劫持操作。何為數據劫持呢?就是在我們訪問或者修改某個對象的某個屬性的時候,通過一段代碼進行攔截行為,然后進行額外的操作,然后返回結果。那么vue中雙向數據綁定就是一個典型的應用。

Vue2.x 是使用 Object.defindProperty(),來進行對對象的監(jiān)聽的。

Vue3.x 版本之后就改用Proxy進行實現的。

下面我們先來理解下Object.defineProperty作用。

一: 理解Object.defineProperty的語法和基本作用。

在理解之前,我們先來看看一個普通的對象,對象它是由多個名/值對組成的無序集合。對象中每個屬性對于任意類型的值。

比如現在我們想創(chuàng)建一個簡單的對象,可以簡單的如下代碼:

const obj = new Object; // 或 const obj = {};
obj.name = 'kongzhi';
console.log(obj.name); // 在控制臺中會打印 kongzhi
obj.xxx = function() {
 console.log(111);
}
// 調用 xxx 方法
obj.xxx(); // 在控制臺中會打印 111

但是除了上面添加對象屬性之外,我們還可以使用 Object.defineProperty 來定義新的屬性或修改原有的屬性。最終會返回該對象。

接下來我們慢慢來理解下該用法。

基本語法:

Object.defineProperty(obj, prop, descriptor);

基本的參數解析如下:

obj: 可以理解為目標對象。

prop: 目標對象的屬性名。

descriptor: 對屬性的描述。

那么對于第一個參數obj 和 prop參數,我們很容易理解,比如上面的實列demo,我們定義的 obj對象就是第一個參數的含義,我們在obj中定義的name屬性和xxx屬性是prop的含義,那么第三個參數描述符是什么含義呢?

descriptor: 屬性描述符,它是由兩部分組成,分別是:數據描述符和訪問器描述符,數據描述符的含義是:它是一個包含屬性的值,并說明這個屬性值是可讀或不可讀的對象。訪問器描述符的含義是:包含該屬性的一對 getter/setter方法的對象。

下面我們繼續(xù)來理解下 數據描述符 和 訪問器描述符具體包含哪些配置項含義及用法。

1.1 數據描述符

const obj = {
 name: 'kongzhi'
};
// 對obj對象已有的name屬性添加數據描述
Object.defineProperty(obj, 'name', {
 configurable: true | false,
 enumerable: true | false,
 value: '任意類型的值',
 writable: true | false
});
// 對obj對象添加新屬性的描述
Object.defineProperty(obj, 'newAttr', {
 configurable: true | false,
 enumerable: true | false,
 value: '任意類型的值',
 writable: true | false
});

如上代碼配置,數據描述符有如上configurable,enumerable,value 及 writable 配置項。

下面我們來看下 每個描述符中每個屬性的含義:

1)value

屬性對應的值,值的類型可以是任意類型的。比如我先定義一個obj對象,里面有一個屬性 name 值為 'kongzhi', 現在我們通過如下代碼改變 obj.name 的值,如下代碼:

const obj = {
 name: 'kongzhi'
};
// 對obj對象已有的name屬性添加數據描述
Object.defineProperty(obj, 'name', {
 value: '1122'
});
console.log(obj.name); // 輸出 1122

如果上面我不設置 value描述符值的話,那么它返回的值還是 kongzhi 的。比如如下代碼:

const obj = {
 name: 'kongzhi'
};
// 對obj對象已有的name屬性添加數據描述
Object.defineProperty(obj, 'name', {
 
});
console.log(obj.name); // 輸出 kongzhi

2)writable

writable的英文的含義是:'可寫的',在該配置中它的含義是:屬性的值是否可以被重寫,設置為true可以被重寫,設置為false,是不能被重寫的,默認為false。

如下代碼:

const obj = {};
Object.defineProperty(obj, 'name', {
 'value': 'kongzhi'
});
console.log(obj.name); // 輸出 kongzhi
// 改寫obj.name 的值
obj.name = 111;
console.log(obj.name); // 還是打印出 kongzhi

上面代碼中 使用 Object.defineProperty 定義 obj.name 的值 value = 'kongzhi', 然后我們使用 obj.name 進行重新改寫值,再打印出 obj.name 可以看到 值 還是為 kongzhi , 這是 Object.defineProperty 中 writable 默認為false,不能被重寫,但是下面我們將它設置為true,就可以進行重寫值了,如下代碼:

const obj = {};
Object.defineProperty(obj, 'name', {
 'value': 'kongzhi',
 'writable': true
});
console.log(obj.name); // 輸出 kongzhi
// 改寫obj.name 的值
obj.name = 111;
console.log(obj.name); // 設置 writable為true的時候 打印出改寫后的值 111

3)enumerable

此屬性的含義是:是否可以被枚舉,比如使用 for..in 或 Object.keys() 這樣的。設置為true可以被枚舉,設置為false,不能被枚舉,默認為false.

如下代碼:

const obj = {
 'name1': 'xxx'
};
Object.defineProperty(obj, 'name', {
 'value': 'kongzhi',
 'writable': true
});
// 枚舉obj的屬性
for (const i in obj) {
 console.log(i); // 打印出 name1
}

如上代碼,對象obj本身有一個屬性 name1, 然后我們使用 Object.defineProperty 給 obj對象新增 name屬性,但是通過for in循環(huán)出來后可以看到 只打印出 name1 屬性了,那是因為 enumerable 默認為false,它里面的值默認是不可被枚舉的。但是如果我們將它設置為true的話,那么 Object.defineProperty 新增的屬性也是可以被枚舉的,如下代碼:

const obj = {
 'name1': 'xxx'
};
Object.defineProperty(obj, 'name', {
 'value': 'kongzhi',
 'writable': true,
 'enumerable': true
});
// 枚舉obj的屬性
for (const i in obj) {
 console.log(i); // 打印出 name1 和 name
}

4) configurable

該屬性英文的含義是:可配置的意思,那么該屬性的含義是:是否可以刪除目標屬性。如果我們設置它為true的話,是可以被刪除。如果設置為false的話,是不能被刪除的。它默認值為false。

比如如下代碼:

const obj = {
 'name1': 'xxx'
};
Object.defineProperty(obj, 'name', {
 'value': 'kongzhi',
 'writable': true,
 'enumerable': true
});
// 使用delete 刪除屬性 
delete obj.name;
console.log(obj.name); // 打印出kongzhi

如上代碼 使用 delete命令刪除 obj.name的話,該屬性值是刪除不了的,因為 configurable 默認為false,不能被刪除的。但是如果我們把它設置為true,那么就可以進行刪除了。

如下代碼:

const obj = {
 'name1': 'xxx'
};
Object.defineProperty(obj, 'name', {
 'value': 'kongzhi',
 'writable': true,
 'enumerable': true,
 'configurable': true
});
// 使用delete 刪除屬性 
delete obj.name;
console.log(obj.name); // 打印出undefined

如上就是 數據描述符 中的四個配置項的基本含義。那么下面我們來看看 訪問器描述符 的具體用法和含義。

1.2 訪問器描述符

訪問器描述符的含義是:包含該屬性的一對 getter/setter方法的對象。如下基本語法:

const obj = {};
Object.defineProperty(obj, 'name', {
 get: function() {},
 set: function(value) {},
 configurable: true | false,
 enumerable: true | false
});

注意:使用訪問器描述符中 getter或 setter方法的話,不允許使用 writable 和 value 這兩個配置項。

getter/setter

當我們需要設置或獲取對象的某個屬性的值的時候,我們可以使用 setter/getter方法。

如下代碼的使用demo.

const obj = {};
let initValue = 'kongzhi';
Object.defineProperty(obj, 'name', {
 // 當我們使用 obj.name 獲取該值的時候,會自動調用 get 函數
 get: function() {
  return initValue;
 },
 set: function(value) {
  initValue = value;
 }
});
// 我們來獲取值,會自動調用 Object.defineProperty 中的 get函數方法。
console.log(obj.name); // 打印出kongzhi
// 設置值的話,會自動調用 Object.defineProperty 中的 set方法。
obj.name = 'xxxxx';
console.log(obj.name); // 打印出 xxx

注意:configurable 和 enumerable 配置項和數據描述符中的含義是一樣的。

1.3:使用 Object.defineProperty 來實現一個簡單雙向綁定的demo

如下代碼:

<!DOCTYPE html>
 <html>
  <head>
   <meta charset="utf-8">
   <title>標題</title>
  </head>
  <body>
   <input type="text" id="demo" />
   <div id="xxx">{{name}}</div>
   <script type="text/javascript">
    const obj = {};
    Object.defineProperty(obj, 'name', {
     set: function(value) {
      document.getElementById('xxx').innerHTML = value;
      document.getElementById('demo').value = value;
     }
    });
    document.querySelector('#demo').oninput = function(e) {
     obj.name = e.target.value;
    }
    obj.name = '';
   </script>
  </body>
</html>

1.4 Object.defineProperty 對數組的監(jiān)聽

看如下demo代碼來理解下對數組的監(jiān)聽的情況。

const obj = {};
let initValue = 1;
Object.defineProperty(obj, 'name', {
 set: function(value) {
  console.log('set方法被執(zhí)行了');
  initValue = value;
 },
 get: function() {
  return initValue;
 }
});
console.log(obj.name); // 1
obj.name = []; // 會執(zhí)行set方法,會打印信息
// 給 obj 中的name屬性 設置為 數組 [1, 2, 3], 會執(zhí)行set方法,會打印信息
obj.name = [1, 2, 3];
// 然后對 obj.name 中的某一項進行改變值,不會執(zhí)行set方法,不會打印信息
obj.name[0] = 11;
// 然后我們打印下 obj.name 的值
console.log(obj.name);
// 然后我們使用數組中push方法對 obj.name數組添加屬性 不會執(zhí)行set方法,不會打印信息
obj.name.push(4);
obj.name.length = 5; // 也不會執(zhí)行set方法

如上執(zhí)行結果我們可以看到,當我們使用 Object.defineProperty 對數組賦值有一個新對象的時候,會執(zhí)行set方法,但是當我們改變數組中的某一項值的時候,或者使用數組中的push等其他的方法,或者改變數組的長度,都不會執(zhí)行set方法。

也就是如果我們對數組中的內部屬性值更改的話,都不會觸發(fā)set方法。

因此如果我們想實現數據雙向綁定的話,我們就不能簡單地使用 obj.name[1] = newValue; 這樣的來進行賦值了。

那么對于vue這樣的框架,那么一般會重寫 Array.property.push方法,并且生成一個新的數組賦值給數據,這樣數據雙向綁定就觸發(fā)了。

因此我們需要重新編寫數組的push方法來實現數組的雙向綁定,我們可以參照如下方法來理解下。

1) 重寫編寫數組的方法:

const arrPush = {};
// 如下是 數組的常用方法
const arrayMethods = [
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
];
// 對數組的方法進行重寫
arrayMethods.forEach((method) => {
 const original = Array.prototype[method]; 
 arrPush[method] = function() {
  console.log(this);
  return original.apply(this, arguments);
 }
});
const testPush = [];
// 對 testPush 的原型 指向 arrPush,因此testPush也有重寫后的方法
testPush.__proto__ = arrPush;
testPush.push(1); // 打印 [], this指向了 testPush
testPush.push(2); // 打印 [1], this指向了 testPush

2)使用 Object.defineProperty 對數組方法進行監(jiān)聽操作。

因此我們需要把上面的代碼繼續(xù)修改下進行使用 Object.defineProperty 進行監(jiān)聽即可:

Vue中的做法如下, 代碼如下:

function Observer(data) {
 this.data = data;
 this.walk(data);
}
var p = Observer.prototype;
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
[
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
].forEach(function(method) {
 // 使用 Object.defineProperty 進行監(jiān)聽
 Object.defineProperty(arrayMethods, method, {
  value: function testValue() {
   console.log('數組被訪問到了');
   const original = arrayProto[method];
   // 使類數組變成一個真正的數組
   const args = Array.from(arguments);
   original.apply(this, args);
  }
 });
});
p.walk = function(obj) {
 let value;
 for (let key in obj) {
  // 使用 hasOwnProperty 判斷對象本身是否有該屬性
  if (obj.hasOwnProperty(key)) {
   value = obj[key];
   // 遞歸調用,循環(huán)所有的對象
   if (typeof value === 'object') {
    // 并且該值是一個數組的話
    if (Array.isArray(value)) {
     const augment = value.__proto__ ? protoAugment : copyAugment;
     augment(value, arrayMethods, key);
     observeArray(value);
    }
    /* 
     如果是對象的話,遞歸調用該對象,遞歸完成后,會有屬性名和值,然后對
     該屬性名和值使用 Object.defindProperty 進行監(jiān)聽即可
     */
    new Observer(value);
   }
   this.convert(key, value);
  }
 }
}
p.convert = function(key, value) {
 Object.defineProperty(this.data, key, {
  enumerable: true,
  configurable: true,
  get: function() {
   console.log(key + '被訪問到了');
   return value;
  },
  set: function(newVal) {
   console.log(key + '被重新設置值了' + '=' + newVal);
   // 如果新值和舊值相同的話,直接返回
   if (newVal === value) return;
   value = newVal;
  }
 });
}
function observeArray(items) {
 for (let i = 0, l = items.length; i < l; i++) {
  observer(items[i]);
 }
}
function observer(value) {
 if (typeof value !== 'object') return;
 let ob = new Observer(value);
 return ob;
}
function def (obj, key, val) {
 Object.defineProperty(obj, key, {
  value: val,
  enumerable: true,
  writable: true,
  configurable: true
 })
}
// 兼容不支持 __proto__的方法
function protoAugment(target, src) {
 target.__proto__ = src;
}
// 不支持 __proto__的直接修改先關的屬性方法
function copyAugment(target, src, keys) {
 for (let i = 0, l = keys.length; i < l; i++) {
  const key = keys[i];
  def(target, key, src[key]);
 }
}

// 下面是測試數據
var data = {
 testA: {
  say: function() {
   console.log('kongzhi');
  }
 },
 xxx: [{'a': 'b'}, 11, 22]
};
var test = new Observer(data);
console.log(test); 
data.xxx.push(33);

以上這篇詳談Object.defineProperty 及實現數據雙向綁定就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • vue配置electron使用electron-builder進行打包的操作方法

    vue配置electron使用electron-builder進行打包的操作方法

    這篇文章主要介紹了vue配置electron使用electron-builder進行打包的操作方法,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • 詳解Jest結合Vue-test-utils使用的初步實踐

    詳解Jest結合Vue-test-utils使用的初步實踐

    這篇文章主要介紹了詳解Jest結合Vue-test-utils使用的初步實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-06-06
  • antd-DatePicker組件獲取時間值,及相關設置方式

    antd-DatePicker組件獲取時間值,及相關設置方式

    這篇文章主要介紹了antd-DatePicker組件獲取時間值,及相關設置方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • vue-cli3設置代理無效的解決

    vue-cli3設置代理無效的解決

    這篇文章主要介紹了vue-cli3設置代理無效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • vue3中的抽離封裝方法實現

    vue3中的抽離封裝方法實現

    vue3中的任何一個組合式api都可以單獨抽離出去在另一個文件,最后只需要回歸到setup()中即可,這篇文章主要介紹了vue3的抽離封裝方法,需要的朋友可以參考下
    2022-08-08
  • Vue?事件處理函數的綁定示例詳解

    Vue?事件處理函數的綁定示例詳解

    這篇文章主要為大家介紹了Vue?事件處理函數的綁定示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • vue+iview使用樹形控件的具體使用

    vue+iview使用樹形控件的具體使用

    這篇文章主要介紹了vue+iview使用樹形控件的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • vue中v-if失效原因及解決方法

    vue中v-if失效原因及解決方法

    本文主要介紹了vue中v-if失效原因及解決方法,一般v-if失效都是和綁定變量有關,下面就來具體介紹一下,感興趣的可以了解一下
    2023-09-09
  • element用腳本自動化構建新組件的實現示例

    element用腳本自動化構建新組件的實現示例

    本文主要介紹了element-ui的用腳本自動化構建新組件的實現示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • vue.js的提示組件

    vue.js的提示組件

    這篇文章主要為大家詳細介紹了vue.js實現一個漂亮、靈活、可復用的提示組件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03

最新評論