深入瞭解Object.defineProperty

来源:https://www.cnblogs.com/z937741304/archive/2018/09/28/9716869.html
-Advertisement-
Play Games

原來寫文章都是一次寫兩三個小時寫完,偶爾看到一個人的博客瞭解到還有草稿箱這個功能,所以以後寫文章的時候就舒服多了哈哈,可以存起來再發,不需要一口氣寫完了 最近一直在看JavaScript高級程式設計,看到defineProperty的時候感受挺深的,因為大名鼎鼎的Vue的雙向數據綁定的原理就是根據這 ...


  原來寫文章都是一次寫兩三個小時寫完,偶爾看到一個人的博客瞭解到還有草稿箱這個功能,所以以後寫文章的時候就舒服多了哈哈,可以存起來再發,不需要一口氣寫完了

  最近一直在看JavaScript高級程式設計,看到defineProperty的時候感受挺深的,因為大名鼎鼎的Vue的雙向數據綁定的原理就是根據這個東西來的,所以看到這裡的時候長了見識

  要說起Object.defineProperty的話,需要先來介紹一下JavaScript中的對象。

JavaScript中的對象

  面向對象的語言有一個標誌,那就是他們都有類的概念,而通過類可以創建任意多個具有相同屬性和方法的對象。ECMAScript中沒有類的概念,因此它的對象也與基於類的語言中的對象有所不同。

  JavaScript中的對象定義: 無序屬性的集合,其屬性可以包含基本值、對象或者函數。嚴格來講,這就相當於說對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值。正因為這樣,可以把對象想象成散列表,就是一個鍵值對,其中的值可以是任意的(數據,函數,對象,數組~~)

  前面balabala一堆概念,真心感覺如果可以理解挺重要的 

 

創建對象

  可以使用構造函數,創建一個Object的實例,然後為其添加屬性和方法

var person = new Object();
person.name = 'zhang';
person.age = 123;
person.job = "SoftWare Engineer";
person.sayName = function () {
    alert(this.name);
};

person.sayName();

   這個例子創建了一個名為person的對象,併為其添加了三個屬性(name、age、和job)和一個方法(sayName),其中sayName()方法用於顯示this.name 這個會被解析為person.name和值。

  不過現在創建這種對象大都是通過字面量的方式來創建了

var person = {
    name: 'zhang',
    age: 18,
    job: 'SoftWare Engineer',
    sayName: function () {
       alert(this.name);
    }
};

person.sayName();

 這種寫法的結果和上面的那一種是一樣的,這種寫法如果你要拓展功能的話就不能再賦值了,也是用到了上面的寫法

person.home = 'hebei';
person.showHome = function () {
    alert(this.home);
};

這個樣子去拓展它。

 

屬性類型

    ES5定義只有內部採用的特性時,描述了屬性的各種特征。定義這些特性時為了實現JavaScript引擎用的,因此在JavaScript中不能直接訪問它們。為了表示特征是內部值,該規範把它們放在了兩對兒方括弧中,例如[[Enumerable]]

    前面例子中的name,age,job,home,showHome都是屬性,引號後面的東西是值,說一下屬性的一些東西。

    ECMAScript中有兩種屬性: 數據屬性和訪問器屬性

數據屬性

  數據屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。數據屬性有4個描述其行為的特性。

  [[Configurable]] 表示能否通過delete刪除屬性從而重新定義屬性,能夠通過屬性的特性,或者能否把屬性修改為訪問器屬性,像前面的例子中那樣直接在對象上定義的屬性,它們的這個特性預設值為true。

  [[Enumerable]] 表示能否通過for-in迴圈返回屬性。像前面例子中那樣直接在對象上定義的屬性,他們的這個特性預設值為true

  [[Writable]] 表示能夠修改屬性的值。像前面例子中那樣直接在對象上定義的屬性,他們的這個特性預設值為true 

  [[Value]] 包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。這個特性的預設值為undefined

例子

var person = {
    name: 'zhang'
};

for(var key in person){
    console.log(key);
}
// 他這個屬性的value值就是他在上面所定義的那一些,對這個值的任何修改都反映在這個位置
console.log(person.name);

person.name = 'goudan';
// 在這裡也可以修改,因為[[Writable]]屬性預設為true
console.log(person.name);

// [[Configurable]] 這個東西為true 表示也可以刪除它
delete person.name;
console.log(person.name);   // undefined  因為刪除之後這個屬性就不存在了 [[Value]]特性的預設值為undefined

 重點來了

Object.defineProperty()

  如果要修改 屬性預設的特性 就必要要使用ES5的Object。defineProperty()方法。這個方法接收三個參數。

  屬性所在的對象、 屬性的名字、 一個描述符對象 (其中描述符對象的屬性必須是: configurable、enumerable、writeable、和value)設置其中一個或多個值,可以修改對應的特性值

  下麵去演示這些例子了,希望你也可以試著去嘗試一遍,我舉得例子寫法可能比較啰嗦,希望沒有嘗試過的可以試著練習一下

  提示:我後面 // 註釋後面的東西 是輸出列印的東西,如有錯誤可以自己具體情況分析一下

writable的情況演示

var person = {
    name: 'zhang',
    age: 12
};
Object.defineProperty(person,"name",{
    writable: false
});
// 讀取person.name
console.log(person.name);  // zhang
console.log(person.age); //12
// 修改person.name
person.name = 'wang';
person.age = 14;
console.log(person.name);   // zhang  ?????? 咋沒變
console.log(person.age);  // 14
// 原因: 那個配置裡面也說了的[[Writable]]預設是true,但是改為false之後 就是這個屬性不可寫,沒有寫進去的也可以看到是可以正常修改的

  

enumerable的情況演示

var person = {
    name: 'zhang',
    baba: '1',
    job: 'Software Engineer'
};

for(var key in person){
    console.log(key);  //   name, baba, job
}
//  三個屬性都可以正常的列印出來
// 配置一下 enumerable Object.defineProperty(person,'baba',{ enumerable: false }); for(var key1 in person){ console.log(key1); // name job } // ??? baba去哪了? 我告訴你去哪了 因為配置了 enumerable: false console.log(person.baba); // 1 不會影響輸出

  

Configurable 的演示

var person = {
    name: 'zhang',
    age: 12
};

Object.defineProperty(person,'name',{
    configurable: false
});

console.log(person.name);   // zhang
console.log(person.age);    // 12

delete person.name;
console.log(person.name);  // zhang
// 剛纔刪除不是undefined嗎  configurable: false就不允許刪除了
// 如果聲明是嚴格模式的話 那一句 delete person.name  還會報錯
person.name =  'wang'
console.log(person.name); // wang

註意:這個配置有一點需要註意一下

var person = {
    name: 'zhang'
};
Object.defineProperty(person,'name',{
    configurable: false
});
// 如果以後你有需求你還想刪除他的話 你會想到
Object.defineProperty(person,'name',{
    configurable: true
});
// 控制台列印錯誤消息:    Uncaught TypeError: Cannot redefine property: name at Function.defineProperty (<anonymous>)
// 這個東西一旦被配置為 false,以後在也不可以把他變為true了 會報錯

 

Value的演示

var person = {
    name: 'zhang'
};
console.log(person.name);  // zhang
Object.defineProperty(person,'name',{
    value: 'wang'
});
console.log(person.name); // wang
// 你小子怎麼改姓了
person.name = 'zhang';  // 給我改回來
console.log(person.name);  //zhang  又是我老張家的孩子了,這個是可以修改回來的

當然了上面的屬性只是單獨演示的, 這四個屬性可以結合起來使用,比如我要定義一個永遠都無法改變它的值

var person = {
    name: 'zhang'
};
console.log(person.name);  // zhang
Object.defineProperty(person,'name',{
    writable: false,
    value: 'wang'
});

console.log(person.name);  // wang
// 卧槽你個小兔崽子又改姓了 給我改回來
person.name = 'zhang';
console.log(person.name); // wang
// 你會發現 有些事情發生了就是發生了,永遠也回不來了
person.name = 'zhang1';
console.log(person.name); // wang

  註意 在調用 Object.defineProperty()方法時,如果不指定,configurable、enumerable和writable特性的預設值都是true。

  你給我演示這一堆東西到底有啥意思,能幹啥。我想告訴你多數情況下可能都沒有必要利用Object.defineProperty()方法提供的這些高級功能。不過,理解這些概念對理解JavaScript對象卻非常有用

 

訪問器屬性

  訪問器屬性不包含數據值;他們包含一對兒getter和setter函數(不過這兩個函數都不是必須的)在讀取訪問器屬性時,會調用getter函數,這個函數負責返回有效的值;在寫入訪問器屬性時,會調用setter函數並傳入新值,這個函數負責決定如何處理數據。訪問器屬性有如下4個特性  

[[Configurable]] : 能否修改屬性的特性,或者能否把屬性修改為數據屬性。像前面的例子中那樣直接在對象上定義的屬性,它們的這個特性預設值為true

[[Enumerable]] : 能否通過for-in迴圈 *********和上面的一樣
[[Get]]: 在讀取屬性的時候調用。 預設值 undefined
[[Set]]: 在寫入屬性的時候調用 預設值 undefined

前面兩個屬性大同小異,幾乎是一樣的,同樣的訪問器屬性不能直接定義,必須使用Object.defineProperty()來定義.

get和set的演示,

  這個重要

var book = {
_year: 2018,
edition: 1
};

Object.defineProperty(book,'year',{
get: function () {
    console.log(`獲取的時候觸發函數`);  // 每次獲取的時候都會觸發這個函數
    console.log(this === book); // true   這裡面的this就是book的引用 也就是book對象
    return this._year;
},
set: function (newValue) {
    console.log('修改值的時候觸發函數');
    // newValue ----- 這裡的newValue就是你設置的修改後的值
    if(newValue> 2018){
        this._year = newValue;
        this.edition = newValue - 2018;
    }
}
});

console.log(book.year);
// 2018  他的值也就是我們返回的值 book._year
// 如果get函數不寫返回值就是undefined

book.year = 2020; // 修改值的時候觸發函數
console.log(book); // {_year: 2019, edition: 2};

  vue雙向數據綁定就是這個,當我們在修改數據的時候,會觸發set那個函數,就在set函數中執行我們的邏輯,去修改數據,以後我會寫一個大概的雙向數據綁定的那個代碼,會發出來,網上也有好多類似的代碼。

上面的代碼創建了一個book對象,並給他定義兩個預設的屬性: _year和edition。

  而訪問器屬性year則包含一個getter函數和一個setter函數。 getter函數返回_year的值,setter函數修改了_year和edition。這是使用訪問器屬性的常見方式,即設置一個屬性的值會導致其他屬性發生變化。

  不一定非要同時制定getter和setter。只指定getter意味著屬性是不能寫,嘗試寫入屬性會被忽略。在嚴格模式下,嘗試寫入至指定了getter函數的屬性會拋出錯誤。類似的,只指定setter函數的屬性也不能讀,否則在非嚴格模式下回返回undefined,而在嚴格模式下會拋出錯誤。

 

  同時定義多個屬性的寫法由於未對象定義多個屬性的可能性很大,ES5又定義了一個Object.defineProperties()方法利用這個方法可以通過描述符一次定義多個屬性。

  這個方法接收兩個對象參數:第一個對象是喲啊添加和修改其屬性的對象,第二個對象的屬性與第一個對象中要添加或修改的屬性一一對應。

同時定義多個屬性的寫法

var book = {};

Object.defineProperties(book,{
    _year: {
        value: 2018
    },
    edition: {
        value: 1
    },
    year: {
        get: function () {
            return this._year;
        },
        set: function (newValue) {
            console.log('修改函數觸發觸發了嗎');
            if(newValue>2018){
                this._year = newValue;
                this.edition += newValue - 2018;
            }
        }
    }
});

console.log(book.year);

book.year = 2020;
console.log(book);  //{_year: 2018, edition: 1}
// ???? 我前面不是觸發了修改的函數了嗎為什麼沒有改呢 上一個分開寫的都修改成功了

  

彆著急,下麵來看一個新的API

Object.getOwnPropertyDescriptor()  

  讀取屬性的特性

使用Es5的Object.getOwnPropertyDescription() 方法,可以取得給定屬性描述符。這個方法接收兩個參數: 屬性所在的對象和要讀取其描述符的屬性名稱。返回值是一個對象,如果是訪問器屬性,這個對象的屬性有 configurable enumerable writable 和value

var descriptor1 = Object.getOwnPropertyDescriptor(book,"_year");
var descriptor2 = Object.getOwnPropertyDescriptor(book,"edition");
console.log(descriptor1);
/*
{configurable: false,
enumerable: false,
value: 2018,
writable: false}
*/
console.log(descriptor2);
/*
    {
    configurable: false,
    enumerable: false,
    value: 1,
    writable: false}
 */
 */
// 前面分開寫可以成功的原因可以用這個東西去獲取一下,可以看到他們是true的,這個是false,所以
// 雖然你修改了,但是他是不生效的

  在JavaScript中,可以針對任何對象------包括DOM和BOM對象,使用Object.getOwnPropertyDescriptor()方法。

  如果你閱讀了本文章有了一些收穫,我會感到非常開心,由於能力有限,文章有的部分解釋的不到位,希望在以後的日子里能慢慢提高自己能力,如果不足之處,還望指正。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • rem適配 <meta name="viewport" content="width=device-width,user-scalable=no"/> <script> (function(){ var html=document.documentElement; var hWidth=html.g ...
  • 前端安全方面,主要需要關註 XSS(跨站腳本攻擊 Cross-site scripting) 和 CSRF(跨站請求偽造 Cross-site request forgery) 當然了,也不是說要忽略其他安全問題:後端範疇、DNS劫持、HTTP劫持、加密解密、釣魚等 CSRF主要是借用已登錄用戶之手 ...
  • 本文記錄並總結了一些九月本人參加面試當中遇到的題目,由於本人水平也有限,這些題目對應的解答一些是我自己的思路或者回來之後進行查閱總結得到的,可能並非最佳答案。分享出來給大家參考,如果出現錯誤,請大佬們多多見諒並勘誤,感謝。 9.4 面試題目 1. 數字如何轉換成字元串? 使用 toString() ...
  • 今天給大家搞一個彈性盒佈局的實例,最近一直在複習一些基礎的東西,所以一直會跟大家分享一些基礎的東西 說實話,最近感覺被掏空了,別問我為什麼,誰去保健誰知道,哈哈,註意安全,好了步入正題,今天用彈性盒寫了一個小例子, 關於彈性盒的一些基礎我就不列舉了,大家有需要可以去 http://www.ruany ...
  • inline-block 控制台-代碼: PS:inline-block是讓元素以內聯形式存在,也就是不是塊級,但是表現起來(又具有塊級元素的高度)--也就是可以調高度(margin或者padding,height) 二· 如果是inline的話,也就是內聯,inline是只有水平的寬度可以調的,沒 ...
  • 表格表單 一、表格 1、基本結構 2、常用屬性 3、垂直水平居中 二、表單 1、基本結構 2、input常用類型 3、常用類型標簽 文本框 密文框 單選框 覆選框 下拉選項 多行文本輸入 按鈕 4、全局屬性 required:必填項 pattern:正則 5、偽類 focus:獲得焦點 ...
  • https://cli.vuejs.org/zh/guide/ Vue CLI 是一個基於 Vue.js 進行快速開發的完整系統,提供: 通過 @vue/cli 搭建互動式的項目腳手架。 通過 @vue/cli + @vue/cli-service-global 快速開始零配置原型開發。 一個運行時 ...
  • 最近在看梁顛編著的《Vue.js實戰》一書,感覺頗有收穫,特此記錄一些比價實用的技巧。 組件是MVVM框架的核心設計思想,將各功能點組件化更利於我們在項目中復用,這類似於我們服務端面向對象三大特性之一的封裝,將複雜的會被多次調用的代碼封裝成組件,在需要調用的地方註冊使用即可。這樣設計的前端代碼方便移 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...