JavaScript物件和JSON

依韻_宵音發表於2017-03-25

JSON(JavaScript Object Notation),指JavaScript的物件表示法,它本身是個字串,是一種資料交換格式,並非物件。通常所提的JSON物件實際是JSON字串解析成物件的結果,或是瀏覽器window物件下的JSON物件。

而且JSON不止用於JavaScript中,其廣泛用於資料交換。

JSON和JavaScript物件

一個JSON檔案,或一段JSON字串,通常是這樣的:

[{
    "name": "使用者許可權管理",
    "code": "99990002",
    "icon": "modicon-1",
    "items": [{
        "name": "模組許可權",
        "code": "999900020009",
        "url": "",
        "isBlank": false,
        "items": [{
            "name": "嚮導模板",
            "code": "9999000200090003",
            "url": "pages/accLayoutTest.html",
            "isBlank": false,
            "items": []
        }, {
            "name": "模組管理我們的",
            "code": "9999000200090001",
            "url": "pages/contentPageTest.html",
            "isBlank": false,
            "items": []
        }]
    }]
}, ... ]

而這樣類似的JavaScript物件則是這樣的:

var menuData = [{
    name: "使用者許可權管理",
    code: "99990002",
    icon: "modicon-1",
    items: [{
        name: "模組許可權",
        code: "999900020009",
        url: "",
        isBlank: false,
        items: [{
            name: "嚮導模板",
            code: "9999000200090003",
            url: "pages/accLayoutTest.html",
            isBlank: false,
            items: []
        }, {
            name: "模組管理我們的",
            code: "9999000200090001",
            url: "pages/contentPageTest.html",
            isBlank: false,
            items: []
        }]
    }]
}, ...]

兩者非常的相似,所不同的是就是JavaScript物件中屬性名,也就是物件的key值是可以沒有引號的,其值為字串時,使用``""包裹均可。 因此我們很多人將第一段程式碼塊裡所寫的JSON稱為JSON物件,實際上,它並不是一個物件,只是一個單純的字串而已,但是它符合JSON的語法規則,可以很方便地轉化為JavaScript物件,或者方便地用於資料交換。

以下我們來了解一下JSON

JSON語法

  • JSON語法規則

    JSON 語法是 JavaScript 物件表示語法的子集,其基本原則如下:

    • 資料在鍵值對中

    • 資料由逗號分隔

    • 花括號儲存物件

    • 方括號儲存陣列

  • JSON的值

    • 數字(整數或浮點數)

    • 字串(在雙引號中)

    • 邏輯值(truefalse

    • 陣列(在方括號中[]

    • 物件(在花括號中{}

    • null

JSON作為一種資料交換格式,為了保證其能被正確方便的解析,其格式有嚴格的要求,必須遵循以下規則:

  1. 複合型別的值只能是陣列物件,不能是函式、正規表示式物件、日期物件。

  2. 簡單型別的值只有四種:字串、數值(必須以十進位制表示)、布林值和null(不能使用NaN, Infinity, -Infinityundefined)。

  3. 字串必須使用雙引號表示,不能使用單引號。

  4. 物件的鍵名必須放在雙引號裡面。

  5. 陣列或物件最後一個成員的後面,不能有逗號。

  6. 數值前不能加0。

以下是合法的JSON格式示例:

["one", "two", "three"]

{
    "one": 1,
    "two": 2,
    "three": 3
}

{
    "names": [
        "張三",
        "李四"
    ]
}

[
    { "name": "張三" },
    { "name": "李四" }
]

下面這些就是不合法的:

{
    name: "張三",
    `age`: 32
} // 屬性名必須使用雙引號

[32, 64, 128, 0xFFF] // 不能使用十六進位制值

{
    "name": "張三",
    "age": undefined
} // 不能使用undefined

{
    "name": "張三",
    "birthday": new Date(`Fri, 26 Aug 2011 07:13:10 GMT`),
    "getName": function() {
        return this.name;
    }
} // 不能使用函式和日期物件

{
    "name": "李四",
    "age": 018
} // 數值前不能有0

以上程式碼中為了指出錯誤所在,使用了JavaScript的註釋法,實際JSON中是不能有註釋的。

不合法的JSON會在解析成JavaScript物件時,出現錯誤。

JSON和JavaScript物件的轉化

window.JSON

JSON作為一種資料交換格式,被寫入了ECMAScript 5,在IE8及之後的瀏覽器都提供了一個JSON物件,用於對JSON進行解析和序列化。

JSON.parse()

此方法接收一個JSON字串,返回解析後的JavaScript物件,通常為ObjectArray

// JSON資料
var humansData = `[{"name":"zs","age":28},{"name":"ls","age":26}]`;
// 解析為JavaScript物件
var humans = JSON.parse(humansData);

// 之後就可以訪問其元素或屬性了
humans[1].name; // ls
humans[1].age; // 26

如果傳入不合法的JSON,則會在JSON.parse時報錯。

為什麼我們在ajax請求中,即使請求的資料為JSON,我們不用解析就能直接使用呢?

// test.text內容
/*
[{
    "name":"zs",
    "age":28
},{
    "name":"ls",
    "age":26
}]
*/

$.ajax({
    url: `./test/test.text`,
    dataType: `JSON`
}).done(function(data){
    console.dir(data); // Array[2]
    console.log(data[0].name); // zs
    // 這裡沒有轉化為js物件就能訪問其屬性?!    
});

這裡實際是因為指定了dataType為JSON,從而進行了自動轉化,所以能直接在成功回撥中使用其屬性。如果去掉dataType的指定,就不能直接訪問其屬性了,因為未轉化時,其本身是一個字串。第一行輸出為test.text的內容,第二行輸出undefined

JSON.stringify()

此方法可接收一個JavaScript值將轉化為JSON字串,此字串可被JSON.parse還原。

var humans = [{
    "name": "zs",
    "age": 28,
    "birth": new Date()
}, {
    "name": "ls",
    "age": 26,
    "birth": new Date()
}];

// 轉化為JSON字串
JSON.stringify(humans);
// "[{"name":"zs","age":28,"birth":"2016-10-25T07:24:11.701Z"},{"name":"ls","age":26,"birth":"2016-10-25T07:24:11.701Z"}]"

前面我們講到了JSON中並非支援所有的JavaScript型別,因此此方法對一些JSON不可接受的值有所處理:原始物件中,如果有一個成員的值是undefined、函式或XML物件,這個成員會被省略。如果陣列的成員是undefined、函式或XML物件,則這些值被轉成null。

我們還發現,JSON中是不支援Date物件的,而上述轉化為字串的結果中正確包含了birth的值,其為一個日期格式的字串。這是因為在Date物件上有一個名為toJSON的方法,JSON.stringify在序列化時,實際是呼叫了這個方法來輸出結果的。

如果一個被序列化的物件擁有 toJSON 方法,那麼該 toJSON 方法就會覆蓋該物件預設的序列化行為:不是那個物件被序列化,而是呼叫 toJSON 方法後的返回值會被序列化,例如:

var obj = {
    foo: `foo`,
    toJSON: function() {
        return `bar`;
    }
};

JSON.stringify(obj); // `"bar"` 
JSON.stringify({x: obj}); // `{"x":"bar"}`

瞭解即可,詳見: MDN:JSON.stringify(){.doc-link}

我們可能經常看到的是JSON.stringify( obj , null , 4),這樣是什麼意思呢?

對於上例,替換最後一句,結果如下所示:

JSON.stringify(humans, null, 4);
// "[
//     {
//         "name": "zs",
//         "age": 28,
//         "birth": "2016-10-25T07:24:11.701Z"
//     },
//     {
//         "name": "ls",
//         "age": 26,
//         "birth": "2016-10-25T07:24:11.701Z"
//     }
// ]"

其實沒什麼變化,不過是加上了空格縮排,使得可讀性更高了。

其餘兩個引數的說明如下:

  • 第二個引數可指定序列化時的操作。

    • 如果該引數是一個函式,則在序列化過程中,被序列化的值的每個屬性都會經過該函式的轉換和處理;

    • 如果該引數是一個陣列,則只有包含在這個陣列中的屬性名才會被序列化到最終的 JSON 字串中;

    • 如果該引數為null或者未提供,則物件所有的屬性都會被序列化;

  • 第三個引數用於指定縮排用的空白字串,用於美化輸出(pretty-print);

    • 如果引數是個數字,它代表有多少的空格;上限為10。該值若小於1,則意味著沒有空格;

    • 如果該引數為字串(字串的前十個字母),該字串將被作為空格;

    • 如果該引數沒有提供(或者為null)將沒有空格。

替代方法

window.JSON物件下雖然提供了完整的JSON字串和JavaScript物件的轉換方法。但是在IE8(相容模式)以及更低版本的IE下沒有提供這個物件,因此我們需要一些替代方案。

  • jQuery中提供了parseJSON這樣一個方法來替代JSON.parse,它接收一個標準格式的JSON字串,返回一個解析後的JavaScript物件。

  • 使用http://www.json.org/提供了一個json.js,這樣ie8(相容模式),ie7和ie6就可以支援JSON物件以及其stringify()parse()方法; 可以在https://github.com/douglascrockford/JSON-js上獲取到這個js,一般現在用json2.js。

  • 還可以使用 eval(`(` + jsonstr + `)`) ; 來將json字串轉換成json物件,注意需要在json字元外包裹一對小括號。但最好不要使用這種方式,因為這種方式不安全,eval會將JSON字串作為JavaScript語句來執行,JSON中的風險程式碼將被執行。

參考連結

JSON

JSON物件

根本沒有“JSON物件”這回事!

JavaScript標準庫-JSON物件

js物件與JSON字串互轉

我的部落格 – JavaScript 原型鏈

相關文章