Prototype源碼淺析——Object部分(三)之有關JSON

來源:互聯網
上載者:User

對JSON不甚瞭解的可以先看下這個:《JSON三分鐘課程》、《JSON入門課程》

對JSON的操作主要是解析JSON字串為一個對象和將一個對象轉換成JSON字串。

網上搜一下JSON解析,就會發現一堆文章和方法,當然這篇文章主要不是討論JSON的解析,不過也可以大致回顧一下:

第一、eval。eval('(' + jsonStr + ')');加個括弧是為了強製表達式運算,不然直接eval('{}'),具體原因可以去翻看一下《JS語言精髓與編程實踐》

第二、Function

第三、另外有一個JSON解析的庫,這個可以自己去下載

第四、除了IE6,7之外,現代瀏覽器基本都內建了JSON解析器

【說明,prototype本身也實現了一個evalJSON方法,不過也脫離不了上面幾種情況】

執行個體如下(沒有用JSON解析庫):

    (function(){
var obj = "{\"name\":\"xesam\"}";
console.log('eval:',eval('(' + obj + ')'));
console.log('Function',(new Function('return' + obj))());
console.log('JSON:',JSON.parse(obj));
})();
//結果:{name:'xesam'}

 

現在將上面的過程反過來,將一個對象編碼為一個JSON字串。同樣,如果瀏覽器帶有原生的JSON方法,有一個與parse對應的方法stringify

    (function(){
var obj = {name:'xesam'};
console.log('obj:',JSON.stringify(obj));//'{"name":"xesam"}'
})();

這種操作就不像解析那樣有那麼多方法,因此需要自己來寫。

 

寫之前,先來探討一下目標對象可能出現的情況,然後把它轉換成字串的形式。

    var object_1 = null;
var object_2 = undefined;
var object_3 = 12;
var object_4 = '12';
var object_5 = true;
var object_6 = [1,2,3];
var object_7 = {name :'xesam'};
var object_8 = {name : function(){}};

先硬性規定一下,null、undefined、true、false返回對應的'null'、'undefined'、'true'、'false'。數字直接量和字串直接量返回對應的數值。

下面主要是區分出Array類型和Function,主要操作就是遍曆每一項,碰到數組或者一般對象就依次展開,所以可以得到一個初始的版本:

    (function(){
function object2Str(object){
if(arguments.length != 0 && typeof object == 'undefined'){
return 'undefined';
}
switch (object) {
case null : return 'null';
case true : return 'true';
case false: return 'false';
}
var type = Object.prototype.toString.call(object);
switch(type){
case '[object Number]' : return String(object);
case '[object String]' : return String(object);
case '[object Array]' : {
var ret = [];
for(var i = 0,len = object.length; i < len; i++){
ret.push(object2Str(object[i]));
}
return '[' + ret.join(',') + ']';
}
case '[object Object]' :{
var ret = [];
for(var key in object){
if(object.hasOwnProperty(key)){
if(typeof object2Str(object[key]) != 'undefined'){
ret.push(String(key) + ':' + object2Str(object[key]));
}
}
}
return '{' + ret.join(',') + '}';
}
}
}
console.log(object2Str(null));
console.log(object2Str(undefined));
console.log(object2Str(true));
console.log(object2Str(12));
console.log(object2Str('34'));
console.log(object2Str({name : function(){}}));
var object = [1,2,3,4,[5,6,7,8,9],6];
console.log(object2Str(object));
var object = {
name:'xesam',
age : 24,
books:{
key_1 : 'book_1',
key_2 : 'book_2'
}
};
console.log(object2Str(object));
})();

看這個實現,{name:function(){}}這個並沒有顯式的提到,因為在預設情況下if(typeof object2Str(object[key]) != 'undefined')這一步就被處理了。

基本的架構得到了,我們的實現中還包括了undefiend的處理,Prototype的實現中並沒有這一步,需要注意。

下面要做的就是一些修繕工作,繼續補充:

在上面的例子中,我們忽略了var number_1 = 12與var number_2 = new Number(12)的區別,我們先填補這兩者之間的區別。

於是在處理每一個值之前先直接調用valueOf獲得變數的原始值。於是上面的例子變為:

    (function(){
function object2Str(object){
if(arguments.length != 0 && typeof object == 'undefined'){
return 'undefined';
}
var type = Object.prototype.toString.call(object);
switch (type) {
case '[object Number]' :
case '[object String]' :
case '[object Boolean]':
object = object.valueOf();
}
switch (object) {
case null : return 'null';
case true : return 'true';
case false: return 'false';
}
type = typeof object;
switch(type){
case 'number' : return String(object);
case 'string' : return String(object);
case 'object' : {
var ret = [];
if(Object.prototype.toString.call(object) == '[object Array]'){
for(var i = 0,len = object.length; i < len; i++){
ret.push(object2Str(object[i]));
}
return '[' + ret.join(',') + ']';
}else if(Object.prototype.toString.call(object) == '[object Function]'){
return '{}';
}else{
for(var key in object){
if(object.hasOwnProperty(key)){
if(typeof object2Str(object[key]) != 'undefined'){
ret.push(String(key) + ':' + object2Str(object[key]));
}
}
}
return '{' + ret.join(',') + '}';
}
}
}
}
console.log(object2Str(null));
console.log(object2Str(undefined));
console.log(object2Str(true));
console.log(object2Str(12));
console.log(object2Str('34'));
console.log(object2Str({name : function(){}}));
var object = [1,2,3,4,[5,6,7,8,9],6];
console.log(object2Str(object));
var object = {
name:'xesam',
age : 24,
books:{
key_1 : 'book_1',
key_2 : 'book_2'
}
};
console.log(object2Str(object));
})();

【說明:上面的實現中並未轉義",/,\和還有一些控制符在Prototype中,調用的是string.inspect這個方法來實現的,不過這一部分在後面的String部分,所以暫且這麼實現,瞭解即可】

現在回到Prototype中,在Prototype裡面,相似的方法叫做Str【這個函數是大寫開頭,說明是一個局部函數,另一個大寫開頭的是Type,這兩個方法並未對外公開】

對比object2Str和Str,除了上面說的特殊字元轉義之外,Str的實現還多了兩個參數就是key和stack。

這個stack的作用是什麼呢?為了避免迴圈使用導致棧溢出。

比如我有一個對象:

var obj = {name : this}。

去調用Prototype裡面的Str方法,就會報錯(typeError),如果直接調用object2Str就會報棧溢出。所以這是一個必要的檢測。

所以我們仿寫最終的版本是:

    (function(){
function object2Str(object,stack){
stack = stack || [];
if(arguments.length != 0 && typeof object == 'undefined'){
return 'undefined';
}
var type = Object.prototype.toString.call(object);
switch (type) {
case '[object Number]' :
case '[object String]' :
case '[object Boolean]':
object = object.valueOf();
}
switch (object) {
case null : return 'null';
case true : return 'true';
case false: return 'false';
}
type = typeof object;
switch(type){
case 'number' : return String(object);
case 'string' : return String(object);
case 'object' : {
for (var i = 0, length = stack.length; i < length; i++) {
if (stack[i] === object){
console.log('error');
throw new TypeError();
}
}
stack.push(object);

var ret = [];
if(Object.prototype.toString.call(object) == '[object Array]'){
for(var i = 0,len = object.length; i < len; i++){
ret.push(object2Str(object[i]));
}
ret = '[' + ret.join(',') + ']';
}else{
for(var key in object){
if(object.hasOwnProperty(key)){
if(typeof object2Str(object[key],stack) != 'undefined'){
ret.push(String(key) + ':' + object2Str(object[key],stack));
}
}
}
ret ='{' + ret.join(',') + '}';
}
stack.pop();
return ret;
}
}
}
console.log(object2Str(null));
console.log(object2Str(undefined));
console.log(object2Str(true));
console.log(object2Str(12));
console.log(object2Str('34'));
console.log(object2Str({name : function(){}}));
var object = [1,2,3,4,[5,6,7,8,9],6];
console.log(object2Str(object));
var object = {
name:'xesam',
age : 24,
books:{
key_1 : 'book_1',
key_2 : 'book_2'
}
};
console.log(object2Str(object));
var object = {
name : this
}
console.log(object2Str(object));
})();

 

另外,在Object中,Str是為toJSON來服務的,因此這個調用方法也是特別處理了的,其參數形式是Str(key, holder, stack)

由於在Prototype的Object中是實現了一個keys方法和一個values方法,所以在類似下面的代碼中

                        for(var key in object){
if(object.hasOwnProperty(key)){
if(typeof object2Str(object[key]) != 'undefined'){
ret.push(String(key) + ':' + object2Str(object[key]));
}
}
}

直接使用了keys函數,Prototype中的 具體實現是:

var keys = Object.keys(value);
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i], str = Str(key, value, stack);
if (typeof str !== "undefined") {
partial.push(key.inspect(true)+ ':' + str);
}
}

所以這麼一來,toJSON的調用形式也變成了:

  function toJSON(value) {
return Str('', { '': value }, []);
}

如果按照我們在例子中的寫法,可以直接是

  function toJSON(value) {
return object2Str(value, []);
}

 

說完Str,另一個用的多的就是inspect方法,顧名思義啊·檢查作用,可以用來調試。

一個最簡單的實現:

    (function(){
function inspect(object){
return object.toString();
}
})();

null和undefined沒有toString方法,這裡順便說明一下,typeof null == 'object',但是null又沒有toString方法,因此不能人為null是一個簡單的object

所以這個討論在前面已經討論過了,一次跳過。

其他就是一些檢測,最佳化的機制。就不算重點了。

 

 

至於keys(values)兩個方法是很簡單的,就是抄襲python,遍曆一個對象的key(value),然後裝到一個數組裡返回。

【toHTML,isHash,toQueryString】的具體實現跟後面關聯比較緊,因此在這裡就暫時不說。

 

轉載請註明來自小西山子【http://www.cnblogs.com/xesam/】
本文地址:http://www.cnblogs.com/xesam/archive/2011/12/22/2297781.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.