如何實現一個JSON.parse

行列發表於2018-08-29
之前收藏的一個把json字串轉物件的解析器,程式碼比較易懂,直接貼原始碼。對於剛接觸這塊的同學應該能起到入門的作用
var json = {}

json.parse = function(text) {
    var at = 0
    var ch = ' '

    var escapee = {
        '"': '"',
        '\\': '\\',
        '/': '/',
        b: '\b',
        f: '\f',
        n: '\n',
        r: '\r',
        t: '\t'
    }

    var error = function(m) {
        console.log(m);
        throw {
            name: 'SyntaxError',
            message: m,
            at: at,
            text: text
        }
    }

    var next = function(c) {
        if (c && c !== ch) {
            error("Expected '" + c + "' instead of '" + ch + "'")
        }
        ch = text.charAt(at)
        at = at + 1

        return ch
    }

    var white = function() {
        while (ch && ch <= ' ') {
            next()
        }
    }

    var number = function() {
        var number
        var string = ''

        if (ch === '-') {
            string = '-'
            next('-')
        }

        while (ch >= '0' && ch <= '9') {
            string += ch
            next()
        }

        if (ch === '.') {
            string += '.'
            while (next() && ch >= '0' && ch <= 9) {
                string += ch
            }
        }

        if (ch === 'e' || ch === 'E') {
            string += ch
            next()
            if (ch === '-' || ch === '+') {
                string += ch
                next()
            }
            while (ch >= '0' && ch <= '9') {
                string += ch
                next()
            }
        }

        number = string - 0
        if (!isFinite(number)) {
            error('Bad number')
        } else {
            return number
        }
    }

    var string = function() {
        var hex
        var i
        var string = ''
        var uffff

        if (ch === '"') {
            while (next()) {
                if (ch === '"') {
                    next()
                    return string // 空字串
                }

                if (ch === '\\') {
                    next()
                    if (ch === 'u') {
                        uffff = 0
                        for (var i = 0; i < 4; i += 1) {
                            hex = parseInt(next(), 16)
                            if (!isFinite(hex)) {
                                break
                            }

                            uffff = uffff * 16 + hex
                        }
                        string += String.fromCharCode(uffff)
                    } else if (typeof escapee[ch] === 'string') {
                        string += escapee[ch]
                    } else {
                        break
                    }
                } else {
                    string += ch
                }
            }
        }
        error('Bad string')
    }

    var word = function() {
        switch (ch) {
            case 't':
                next('t');
                next('r');
                next('u');
                next('e');
                return true;
            case 'f':
                next('f');
                next('a');
                next('l');
                next('s');
                next('e');
                return false;
            case 'n':
                next('n');
                next('u');
                next('l');
                next('l');
                return null;
        }

        error("Unexpected '" + ch + "'");
    }
    var value

    var array = function() {
        var array = []
        if (ch === '[') {
            next('[')
            white()

            if (ch === ']') {
                next(']')
                return array // 空陣列
            }

            while (ch) {
                array.push(value())
                white()
                if (ch === ']') {
                    next(']')
                    return array
                }
                next(',')
                white()
            }
        }

        error('Bad array')
    }

    var object = function() {
        var key
        var object = {}
        if (ch === '{') {
            next('{')
            white()

            if (ch === '}') {
                next('}')
                return object // 空物件
            }

            while (ch) {
                key = string()
                white()
                next(':')

                if (Object.hasOwnProperty.call(object, key)) {
                    error('Duplicate key "' + key + '"');
                }

                object[key] = value()
                white()
                if (ch === '}') {
                    next('}')
                    return object
                }
                next(',')
                white()
            }
        }

        error('Bad object')
    }

    value = function() {
        white()
        switch (ch) {
            case '{':
                return object()
            case '[':
                return array()
            case '"':
                return string()
            case '-':
                return number()
            default:
                return ch >= '0' && ch <= '9' ? number() : word()
        }
    }

    return value(text)
}

實現思路

json的結構分析

json的結構包含幾種元素:

object(name value pair object)

此處指狹義的物件,名值對形式。廣義上任何元素都是物件。

array

由[]符號包裹,元素用英文逗號分隔。

字面型別

字面型別最為簡單,不能再巢狀

number

boolean

true or false

null

下面放幾張json.org的圖,以直觀的形式展示json格式。

clipboard.png

clipboard.png

clipboard.png

object 內部的value和array內部的元素都可以是任意組成型別,可以存在任意層次的巢狀。因此用遞迴方式解析比較簡單。

相關文章