從實現後臺商品屬性程式碼說起,聊聊相關的思維!JS、模擬資料、橋樑

JF_sander發表於2019-02-28

## 前言

原始碼地址

  • 這裡實現了一個後臺商品屬性規格新增,修改,刪除等功能,因為當時做這個功能的時候,本不是需要我完成這個工作,而是一個後臺妹子,需要在後臺實現一個類似修改商品屬性等的功能,所以就找了我幫忙去寫這個功能,於是我剛好手頭也沒有很緊急的事情,於是就爽快接了下來,雖然幫到後面結果就是:假設某天后臺介面資料變了,需要我來重新改程式碼,變成我自己的事來維護這個功能了,你們懂的!

  • 雖然最後功能確實是實現了,但是想起來,還是有一些不足的:1、當時以為這個功能應該蠻簡單的,而且本身功能需求也比較單一,說白了就是寫個功能,所以也沒有去用框架,就直接用jquery+原生js寫,這樣帶來的後果就是有很多dom更新、頁面更新的操作,需要我自己去寫,而不是靠框架來完成,其實這也就是無可厚非,無非相對來說多寫了很多程式碼,但是因為過多地自己實現dom的繪製操作,導致大腦沒有集中到真正的主題(這個功能實現的核心)上,所以繞了很多彎子;2、我覺得最主要的就是其中用了很多for迴圈去遍歷資料,有的時候還多層迴圈,真的很煩,而且程式碼很難看,所以現在要我來優化這個功能的js指令碼的話,我肯定會想到從for迴圈出發來優化!

實現的功能

  • 商品屬性新增、修改、刪除等!

  • 設定商品庫存、價格、編碼等!

  • 批量修改商品庫存、價格、編碼!

  • 還可以顯示商品的預設規格、庫存、價格、編碼等!

值得借鑑的地方

雖然這裡的程式碼總是有那麼一點差強人意,可以在大師那裡看來是該刪的程式碼,但是他確實有它值得思量的地方,下面從幾個點來說明:

  • 1、模擬資料 這裡的模擬資料有多種理解哈:

1)我在實現寫這個功能的時候,後臺並沒有給我明確的介面資料,所以我也並不知道後臺會傳給我什麼資料,那該怎麼做呢?其實到這裡,你完全不需要考慮後臺的介面,而你只需要的是,你實現的這個功能需要什麼資料才能足以考慮到功能的方方面面!然後你模擬一個這樣的資料,就好比,我在在實現這個功能的時候,雖然後臺給我的是一大串很複雜的json資料,但是其實我並不完全需要這樣一個資料,因為從幾個方面來說,我並不直接需要後臺給我的資料,第一,後臺返回資料一般都是很複雜,並不是跟構建的dom匹配;第二,從解耦的思路來說,你需要的資料不能太直接依賴後臺的資料,這樣很容把你的功能寫死。那麼最後怎麼跟後臺的介面聯絡起來呢?ok,你只需要構建一個“橋樑”,來建立這層關係,就像一條的河流的兩岸一樣,只要能構建好橋樑,它們之間就能通過了!好,我們來看程式碼:

    //首先後臺返回的介面資料
    var interface_data_arr = [
    {
        "id": 4,
        "model_name": "大小",
        "sortIndex": "255",
        "attr_value": [
            {
                "is_active": false,
                "alternative_name": "XL",
                "id": 3
            },
            {
                "is_active": true,
                "alternative_name": "L",
                "id": 5
            },
            {
                "is_active": false,
                "alternative_name": "M",
                "id": 7
            },
            {
                "is_active": false,
                "alternative_name": "S",
                "id": 15
            }
        ],
        "is_currentUse": true
    },
    {
        "id": 5,
        "model_name": "顏色",
        "sortIndex": "256",
        "attr_value": [
            {
                "is_active": true,
                "alternative_name": "紅色",
                "id": 33
            },
            {
                "is_active": false,
                "alternative_name": "黑色",
                "id": 34
            },
            {
                "is_active": false,
                "alternative_name": "藍色",
                "id": 35
            },
            {
                "is_active": false,
                "alternative_name": "綠色",
                "id": 36
            },
            {
                "is_active": false,
                "alternative_name": "黃色",
                "id": 37
            }
        ],
        "is_currentUse": true
    },
    {
        "id": 6,
        "model_name": "物件",
        "sortIndex": "257",
        "attr_value": [
            {
                "is_active": false,
                "alternative_name": "成人",
                "id": 83
            },
            {
                "is_active": false,
                "alternative_name": "兒童男",
                "id": 97
            },
            {
                "is_active": false,
                "alternative_name": "兒童女",
                "id": 98
            },
            {
                "is_active": false,
                "alternative_name": "綠色",
                "id": 36
            },
            {
                "is_active": false,
                "alternative_name": "黃色",
                "id": 37
            }
        ],
        "is_currentUse": false
    },
    {
        "id": 7,
        "model_name": "花色",
        "sortIndex": "258",
        "attr_value": [
            {
                "is_active": true,
                "alternative_name": "紅花",
                "id": 100
            },
            {
                "is_active": false,
                "alternative_name": "百花",
                "id": 120
            },
            {
                "is_active": false,
                "alternative_name": "粉花",
                "id": 130
            },
            {
                "is_active": false,
                "alternative_name": "綠色",
                "id": 36
            },
            {
                "is_active": false,
                "alternative_name": "黃色",
                "id": 37
            }
        ],
        "is_currentUse": true
    }
];複製程式碼

而我實現功能並不需要這樣的資料的,我只需要下面的資料:

    var alternative_models_data = [
        //尺寸
         {
             model_name:`尺寸`,
             alternative_data:[`X`,`S`,`M`],
             is_currentUse:false    
         },
        //顏色
         {
             model_name:`顏色`,
             alternative_data:[`紅色`,`綠色`,`藍色`],
            is_currentUse:false
         },
        //記憶體
         {
             model_name:`記憶體`,
             alternative_data:[`8G`,`16G`],
             is_currentUse:false
         }

    ];複製程式碼

我們可以通過實現一個“橋樑”,來把後臺的資料變成我們需要的資料:

    var alternative_models_data = [];
    (function(){
        for(var i = 0,len = interface_data_arr.length;i<len;i++){
            var obj = {};
            obj.model_name = interface_data_arr[i].model_name;
            obj.alternative_data = [];
            // obj.is_currentUse = false;
            obj.is_currentUse = interface_data_arr[i].is_currentUse;
            for(var j = 0,len1 = interface_data_arr[i].attr_value.length;j<len1;j++){
                obj.alternative_data.push(interface_data_arr[i].attr_value[j].alternative_name);
            };
            alternative_models_data.push(obj);
        };
    })();複製程式碼

我們這樣是不是把後臺給的資料換成我們理想的資料了呢?所以我們根本不用太在意後臺的介面資料!導致你就能單純的實現你的功能,不用受後臺介面資料的影響了!

2) 模擬資料還有一種理解就是根據需要我們可以定義一個資料用來表示某種狀態或者某種結果,比如我們可以定義一個值為1表示成功、通過等,用0表示失敗,未通過等!當然,儘量用一個數字表示來模擬這種資料,這樣有一個好處就是,你可以計算了,最後再通過這個最終值,來判斷是否達到某種狀態,來看一個例子吧,我們實現一個答題的功能,最後根據使用者的答題來給使用者打分,滿分是100,總共5道單選題,那麼就這個邏輯來說,我們可以這樣出發:

    //定義一個陣列儲存使用者的成績,grade_item代表題的序號,grade分表代表這道題的通過情況
    //1就是通過,0通過
    //當然了,這裡user_grade可以預設為空陣列,我們可以在使用者答完一道題之後向陣列裡面推送一個
    //成績資料
    var user_grade = [
        {
            grade_item:0,
            grade : 0
        },
        {
            grade_item:1,
            grade : 0
        },
        {
            grade_item:2,
            grade : 0
        },
        {
            grade_item:3,
            grade : 0
        },
        {
            grade_item:4,
            grade : 0
        }
    ];


    //答題程式碼省略


    //假設成績答完, 現在user_grade變成了,如下:

    var user_grade = [
        {
            grade_item:0,
            grade : 1
        },
        {
            grade_item:1,
            grade : 0
        },
        {
            grade_item:2,
            grade : 1
        },
        {
            grade_item:3,
            grade : 1
        },
        {
            grade_item:4,
            grade : 0
        }
    ];
    //於是我們看到使用者答對了3道題,所以得分應為60分,那麼我們可以這樣計算出來


    //算出每道題答對之後的應得分
    var shouldScore = 100/5;
    //那麼最後使用者的得分應該是每道題的總分 * 答對題數
    var userScore = (function(){
        var num = 0;
        for(var i = 0,len = user_grade.length;i<len;i++){
            if(user_grade[i].grade == 1){
                num += shouldScore;
            };
        };
        return num;
    })();

    console.log(userScore);複製程式碼

說了這麼多,你可能會問:我是不是在廢話呀,我們平時開發不是都這樣做的嗎?我想說明什麼? 其實我就是想說的是一種思想或者說思維:任何我們現實中存在的事物,在我們的程式碼中都可以用一種資料來對應它,這樣我們才能實現在程式碼中對事物的某種操作!事物跟程式碼的關係在某種意義上是一種對等關係!雖然本質上沒有任何關係,理解到這一點很重要,因為當你真正理解之後你會發現最終無非在做一些數學計算!

  • 2、在寫程式碼的時候,你會發現,我大量使用了function,這是javascript函式程式設計的一大亮點,其實這樣做好處是有很多的!1) 首先你重用了你的程式碼;2)減小了你單個函式的函式體量,因為可以設想如果把一個功能寫在一個函式裡面,那麼可想而知,你這個函式是多麼的臃腫和難以閱讀;3)不知道你有沒有發現,你把一個大函式分解成很多小函式的,其實每個小函式的命名起到了很好的解釋和註釋作用!來看下下面這個例子:
    //最開始的大函式
    //實現先加後減
    function evaluation(a,b,c){
        var d = a + b;
        var e = d - c;
        return e;
    };
    evaluation(3,5,4);//4


    //再來看看另外一種方式實現同樣的功能
    function ev(a,b,c){
        var d = sum(a,b);
        var e = diff(e,c);
        return e;
    };
    function sum(a,b){
        return a+b;
    };
    function diff(a,b){
        return a-b;
    };
    ev(3,5,4);//4

    //首先說明一下,因為這裡程式碼量很小哈,所以可能好處並不明顯,
    //但是直觀地看第二種方式有幾個好處,
    //1、sum和diff可以在其它地方複用
    //2、sum和diff的名稱是不是讓你一眼就知道它們的功能?
    //3、ev函式體裡面的程式碼是不是很容容易閱讀,只要會點英語,都能知道它幹嘛的複製程式碼

所以,應提倡用這種函數語言程式設計來優化你的指令碼!

  • 3、我們平時在前端開發過程中,可能大家更多的印象就是用js實現一下有關dom的操作,用一下ajax來對接資料等等,其實當我們實現一個稍微複雜的功能的時候,js也是完全可以上升到需要用“演算法”的層次的!當然了,說到演算法,這個是一個很有意義的概念,當然了,這方面我也是有很多不足的,在這裡提出這個概念,只是讓大家在開發的過程中應該始終有演算法這個概念,因為了,演算法有了,就相當於一個人有了靈魂了,它還需要的僅僅是一個肉體(程式碼)了!不說了,貼上上寫這個功能的時候用到的別人寫的一個關於陣列排列組合的演算法:
// Arbitrary base x number class 
var BaseX = function(initRadix){
    this.radix     = initRadix ? initRadix : 1;    
    this.value     = 0;
    this.increment = function(){
        return( (this.value = (this.value + 1) % this.radix) === 0);
    }
};

function combinations(input){
    var output    = [],    // Array containing the resulting combinations
        counters  = [],    // Array of counters corresponding to our input arrays
        remainder = false, // Did adding one cause the previous digit to rollover?
        temp;              // Holds one combination to be pushed into the output array

    // Initialize the counters
    for( var i = input.length-1; i >= 0; i-- ){
        counters.unshift(new BaseX(input[i].length));
    }

    // Get all possible combinations
    // Loop through until the first counter rolls over
    while( !remainder ){
        temp      = [];   // Reset the temporary value collection array
        remainder = true; // Always increment the last array counter

        // Process each of the arrays
        for( i = input.length-1; i >= 0; i-- ){
            temp.unshift(input[i][counters[i].value]); // Add this array`s value to the result

            // If the counter to the right rolled over, increment this one.
            if( remainder ){
                remainder = counters[i].increment();
            }
        }
        output.push(temp); // Collect the results.
    }

    return output;
};

// Input is an array of arrays
// console.log(combinations([[0,1], [0,1,2,3], [0,1,2]]));
// var r=allPossibleCases(allArrays);
// console.log(r);複製程式碼

相關文章