關於JavaScript對象我要知道的事

  • 與某些語言把原始類型存儲在棧中,把引用類型存儲在堆中不一樣,JavaScript使用一個變量對象來追蹤變量的生存週期:原始值直接保存在這個對象內,引用類型的指針(指向這個引用類型在內存中的地址)被保存在這個對象內。數組

  • 因爲上面的緣由,當把一個引用類型賦值給一個變量時,只是把它的指針賦值給了這個變量。框架

  • 解除對一個對象的引用的最好的方法是將對象變量賦值爲null,這樣垃圾收集機制就能更好的處理無用的垃圾了。例如:函數

    let pointer = {};  // 建立一個引用了對象的變量 pointer, 稱爲對象變量
    
    // do something with pointer
    
    pointer = null;  // 將對象變量賦值爲 null, 它引用的對象會被垃圾收集,那塊內存就能空出來
    複製代碼

    上面的代碼用一個pointer變量引用了一個對象,在將pointer變量設置爲 null以後,垃圾收集器就能更好的處理那個已經沒有了引用的對象。ui

  • 對於全部的引用類型,使用字面量形式建立的對象並無調用構造函數,可是JavaScript引擎在背後作的工做和調用了構造函數時同樣。例如對於一個普通對象:spa

    // 使用對象字面量的方式建立一個對象
    let person = {
        name: '王大錘',
        age: 30
    };
    
    /* 等價於下面使用構造函數建立的對象 */
    
    // 使用構造函數建立一個對象
    let person = new Object();
    person.name = '王大錘';
    person.age = 30;
    複製代碼

    又例如對於一個數組:指針

    let arr = [1, 2, 3]; // 使用字面量形式建立一個數組
    
    /* 等價於下面使用構造函數的形式建立數組 */
    
    let arr = new Array(1, 2, 3);
    複製代碼
  • 使用Array.isArray方法鑑定一個變量是否是數組,使用instanceof操做符也能夠判斷一個變量的值是否是數組的實例,可是若是變量在同一個網頁的不一樣框架之間傳遞,因爲每一個框架都有本身的環境,因此後者可能沒法獲得準確的結果,可是前者始終能獲得正確的結果。code

    let arr = [1, 2, 3]; 
    console.log(Array.isArray(arr), arr instanceof Array);  // true true
    複製代碼
  • 對原始類型的值使用instanceof操做符判斷其對應的類型,總會返回false,這是由於原始類型雖然有打包操做,可是在使用instanceof進行判斷時,打包操做就已經結束了,此時打包出來的臨時對象已經被銷燬,因此結果爲false對象

    let name = 'Jack Ma';
    let age = 40;
    let flag = false;
    
    console.log(name instanceof String);  // false
    console.log(age instanceof Number);   // false
    console.log(flag instanceof Boolean); // false
    複製代碼
  • 函數存在一個被稱爲[[call]]的內部屬性,內部屬性沒法經過代碼訪問,這裏使用雙中括號來標註此類屬性名。[[call]]屬性是函數的獨有屬性,並且typeof操做符對具備[[call]]屬性的對象返回function,這就是使用typeof判斷函數類型的原理。排序

  • 函數聲明能夠被提高是由於引擎提早知道了函數的名字;函數表達式是使用匿名函數定義的,變量名只是引用了這個匿名函數,雖然能夠經過變量對函數進行調用,但變量名並非函數的名字,因此沒法進行提高。ip

  • sort方法在排序的時候會將對象轉換成字符串而後再比較,因此在不指定比較函數的時候不能對純數字數組進行準確排序。

  • 函數也有length屬性,表示函數指望的參數個數,也就是函數聲明的形參個數。

  • 函數和方法的區別:其實這兩個名稱指的都是函數,只是當一個函數是一個對象的屬性時,相對於這個對象,函數就被稱爲了方法。

  • 當屬性***第一次***被添加給對象時,JavaScript會調用對象名爲[[put]]的內部方法來建立這個屬性,並賦值。

  • 當屬性被添加給對象以後,再改變屬性的值時,不會再調用再調用[[put]]方法了,這時會調用[[set]]這個內部屬性。

    let obj = {};  //定義一個對象,這個對象沒有任何用戶本身建立的屬性 
    
    obj.name = 'Jack M';  // 調用了 [[put]] 方法,由於 obj 原本沒有 name 屬性,這裏給它添加了 name 屬性
    obj.name = 'Mask';    // 調用了 [[get]] 方法,由於這時已經存在了name屬性,這裏只是從新給屬性賦值
    複製代碼
  • 若是想刪除對象的某個屬性,應使用delete操做符。注意:直接將屬性設置爲null是沒法刪除這個屬性的,這樣只是給屬性賦了一個新值爲null

    // 建立一個帶有 name 屬性的對象
    let obj = {
        name: 'Yuri'
    };
    
    // 將 name 屬性值設爲 null,沒法刪除這個屬性,只是給它賦值爲 null, 即 obj.name === null
    obj.name = null;
    console.log(obj.name === null);  // true
    
    // 使用 delete 關鍵詞能夠真正刪除屬性
    delete obj.name;
    console.log(obj.name);  // undefined
    console.log('name' in obj);  // false
    複製代碼
  • 想遍歷對象的屬性時,能夠用兩種方法:

    1. for ... in ... :迭代對象的可枚舉的***屬性名***,可枚舉屬性是指[[Enumerable]]值爲 true 的屬性
    2. Object.keys(object):這個方法返回對象的全部可枚舉屬性的屬性名組成的數組。
    // 建立一個帶有兩個自有屬性的對象
    let obj = {
        name: 'Yuri',
        age: 40,
        speak: 'You will obey ...'
    };
    
    
    for(let propertyName in obj){  // 每次都會將 對象的屬性名賦值給 propertyName
        console.log(propertyName);
    }
    // name
    // age
    // speak
    
    
    let allProperties = Object.keys(obj);  // 得到 obj 對象的全部可枚舉屬性的屬性名
    console.log(allProperties);  // ["name", "age", "speak"]
    複製代碼

    注意:這兩個方法取得的可枚舉屬性是有差異的,for in方法會遍歷對象的原型鏈,而Object.keys()方法只會涉及到對象自己的屬性,不會訪問原型鏈。

  • 對象的大部分自帶的屬性的[[Enumerable]]的值都是false,即不可遍歷,用實例對象的propertyIsEnumerable方法能夠判斷一個屬性是否是可枚舉的:

    console.log(obj.propertyIsEnumerable('name'));  // true
    console.log(obj.propertyIsEnumerable('age'));   // true
    console.log(obj.propertyIsEnumerable('speak')); // true
    複製代碼
  • 使用構造函數建立對象時,若是不須要傳遞參數時,能夠不加小括號:

    // 定義一個構造函數
    function Person(name){
        /* xxx */
    }
    
    let p1 = new Person();
    let p2 = new Person;  // 不加小括號
    
    console.log(p1 instanceof Person, p2 instanceof Person);  // true true
    複製代碼
相關文章
相關標籤/搜索