国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Javascript元編程(一)

mo0n1andin / 1471人閱讀

摘要:中,便是兩個(gè)可以用來(lái)進(jìn)行元編程的特性。這也就是元編程的優(yōu)點(diǎn)之一,程序可以根據(jù)傳入?yún)?shù)對(duì)象的不同,動(dòng)態(tài)地生成對(duì)應(yīng)的程序,從而減少大量冗余的代碼。

首發(fā)于知乎專欄:http://zhuanlan.zhihu.com/starkwang

這幾天把一年多前買(mǎi)的《松本行弘的程序世界》重新看了看,很多當(dāng)時(shí)不能理解的東西現(xiàn)在再去看真是茅塞頓開(kāi)呀,看到元編程那一段真是把我震撼到了,后來(lái)發(fā)現(xiàn) Javascript 里其實(shí)也是有一些支持元編程的特性的,今天就用一個(gè) DEMO 示范一下吧。

什么元編程

“元編程”這個(gè)名字看起來(lái)高端大氣上檔次,它的含義也是相當(dāng)高端:“寫(xiě)一段自動(dòng)寫(xiě)程序的程序”,不要誤會(huì),我們做的可不是人工智能。

言簡(jiǎn)意賅地說(shuō),元編程就是將代碼視作數(shù)據(jù),直接用字符串 or AST or 其他任何形式去操縱代碼,以此獲得一些維護(hù)性、效率上的好處。

Javascript 中,evalnew Function()便是兩個(gè)可以用來(lái)進(jìn)行元編程的特性。

原始示例

現(xiàn)在我們有一堆用戶的數(shù)據(jù),具體字段有name,sex,age,address等等,通過(guò)類似 /get_name?id=123456 來(lái)拉取數(shù)據(jù)

那么我們很容易寫(xiě)出這樣的代碼:

class User {
    constructor(userID) {
        this.id = userID;
    }

    get_name() {
        return $.ajax(`/get_name?id=${this.id}`);
    }

    get_sex() {
        return $.ajax(`/get_sex?id=${this.id}`);
    }

    //下面是get_age、get_address......
}

這段代碼的問(wèn)題在哪呢?

首先,用戶數(shù)據(jù)有多少個(gè)字段,我們就要定義多少個(gè) get_something 方法,更可怕的是這些方法里邏輯都是重復(fù)的,都是一個(gè)簡(jiǎn)單的 ajax。

進(jìn)階(一)

我們可以把拉取數(shù)據(jù)的邏輯封裝到 __fetchData 里:

class User {
    constructor(userID) {
        this.id = userID;
    }
    
    __fetchData(key) {
        //這是一個(gè)private方法,直接調(diào)用類似__fetchData("age")是不被允許的
        return $.ajax(`/get_${key}?id=${this.id}`)
    }

    get_name() {
        return this.__fetchData("name");
    }

    get_sex() {
        return this.__fetchData("sex");
    }

    //下面是get_age、get_address......
}

然后,冗余的問(wèn)題可以通過(guò)registerProperties來(lái)解決:

class User {
    constructor(userID) {
        this.id = userID;
        this.registerProperties(["name", "age", "sex", "address"]);
    }

    registerProperties(keyArray) {
        keyArray.forEach(key => {
            this[`get_${key}`] = () => this.__fetchData(key);
        })
    }

    __fetchData(key) {
        //這是一個(gè)private方法,直接調(diào)用類似__fetchData("age")是不被允許的
        return $.ajax(`/get_${key}?id=${this.id}`)
    }
}
進(jìn)階(三)

到目前為止我們都沒(méi)有涉及到任何元編程的概念,下面我們加上更高的需求:

在拉去數(shù)據(jù)之后,我們要對(duì)部分?jǐn)?shù)據(jù)進(jìn)行一定的處理,比如對(duì) name 我們要去掉首尾的空格,對(duì) age 我們要加上一個(gè) 字。具體的處理方法定義在 __handle_something 里面。

這里我們便可以通過(guò) new Function() 來(lái)動(dòng)態(tài)生成函數(shù),元編程開(kāi)始顯現(xiàn)威力:

class User {
    constructor(userID) {
        this.id = userID;
        this.registerProperties(["name", "age", "sex", "address"]);
    }

    registerProperties(keyArray) {
        keyArray.forEach(key => {
            //注意這里的fnBody內(nèi)部依然采用ES5的寫(xiě)法,因?yàn)閎abel目前不會(huì)編譯函數(shù)字符串。
            var fnBody = `return this.__fetchData("/get_${key}?id=${this.id}")
                    .then(function(data){
                        return this.__handle_${key}?_this.handle_${key}(data):data;
                    })`;
            this[`get_${key}`] = new Function(fnBody);
        })
    }

    __handle_name(name) {
        //do somthing with name...
        return name;
    }

    __handle_age(age) {
        //do somthing with age...
        return age;
    }

    __fetchData(key) {
        //這是一個(gè)private方法,直接調(diào)用類似__fetchData("age")是不被允許的
        return $.ajax(`/get_${key}?id=${this.id}`)
    }
}
進(jìn)階(四)

下面我們讓需求更加{{BANNED}}一點(diǎn):

數(shù)據(jù)并非通過(guò) ajax 直接拉取,而是通過(guò)一個(gè)別人封裝好的 UserDataBase 里的方法來(lái)拉取;

數(shù)據(jù)的字段并非只有name,sex,age,address四個(gè),而是要根據(jù) UserDataBase 里給你的方法決定。給你1000個(gè)get不同字段的方法,User類里也要有對(duì)應(yīng)的1000個(gè)方法。

class UserDataBase {
    constructor() {}
    get_name(id) {}
    get_age(id) {}
    get_address(id) {}
    get_sex(id) {}
    get_anything_else1(id) {}
    get_anything_else2(id) {}
    get_anything_else3(id) {}
    get_anything_else4(id) {}
    //......
}

這里我們就需要用到 JS 的反射機(jī)制來(lái)讀取所有拉取字段的方法,然后通過(guò)元編程的方式來(lái)動(dòng)態(tài)生成對(duì)應(yīng)的方法。

class User {
    constructor(userID, dataBase) {
        this.id = userID;
        this.__dataBase = dataBase;
        for (var method in dataBase) {
            //對(duì)每一個(gè)方法
            this.registerMethod(method);
        }
    }

    registerMethod(methodName) {
        //這里除去了前置的"get_"
        var propertyName = methodName.slice(4);
        
        //注意這里拉取數(shù)據(jù)的方法改為使用dataBase
        var fnBody = `return this.__dataBase.${methodName}()
                    .then(function(data){
                        return this.__handle_${propertyName}?_this.handle_${propertyName}(data):data;
                    })`;
        this[`get_${propertyName}`] = new Function(fnBody);
    }

    __handle_name(name) {
        //do somthing with name...
        return name;
    }

    __handle_age(age) {
        //do somthing with age...
        return age;
    }
}
var userDataBase = new UserDataBase();
var user = new User("123", userDataBase);

這樣即使用戶數(shù)據(jù)有一萬(wàn)種不同的屬性字段,只要保證 UserDataBase 中良好地定義了對(duì)應(yīng)的拉取方法,我們的 User 就能自動(dòng)生成對(duì)應(yīng)的方法。

這也就是元編程的優(yōu)點(diǎn)之一,程序可以根據(jù)傳入?yún)?shù)/對(duì)象的不同,動(dòng)態(tài)地生成對(duì)應(yīng)的程序,從而減少大量冗余的代碼。

進(jìn)階(五)

現(xiàn)在程序里還有點(diǎn)小瑕疵:

//用戶數(shù)據(jù)中不存在www字段,若這樣執(zhí)行會(huì)報(bào)錯(cuò):
user.get_www(); //user.get_www is not a function

現(xiàn)在我們要保證像上面那樣執(zhí)行任意的 user.get_xxxx() ,程序不會(huì)報(bào)錯(cuò),而是返回 false

//用戶數(shù)據(jù)中不存在www字段:
user.get_www(); // => false

Javascript 里缺少了 Ruby 中 method_missing 這樣黑科技的內(nèi)核方法,但是我們可以通過(guò) ES6 的 Proxy 特性來(lái)模擬:

function createUser(id, userDataBase) {
    return new Proxy(new User(id, userDataBase), {
        get: (target, property) => (typeof(target[property]) === "function" ? target[property] : () => false)
    })
}

var userDataBase = new UserDataBase();
var user = createUser("123", userDataBase);

user.get_name() => // fetch name data
user.get_wwwwww() // => false
總結(jié)

其實(shí)這里的 DEMO 只是元編程的一個(gè)小應(yīng)用,下一篇文章里我們會(huì)通過(guò)元編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的表單驗(yàn)證 DSL :

//類似
form.name["is not empty"]["length is between",1,20] // => true or false
參考

來(lái)來(lái)來(lái),咱么元編程入個(gè)門(mén)

元編程之javascript

JavaScript 元編程之ES6 Proxy

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87748.html

相關(guān)文章

  • Javascript 編程初探 [1]

    摘要:在這里講到的很多也許只和程序?qū)τ诠ぷ鳈C(jī)制的操作有關(guān),但是作為初探也許也就足夠了。一般情況下還有空字符串都會(huì)被判斷成。面向特征編程面向特征編程的全稱是。 引子 元編程會(huì)有如下的定義: 一種計(jì)算機(jī)程序的編寫(xiě)方式,它可以將其它程序(或者其本身)作為數(shù)據(jù)進(jìn)行編寫(xiě)和操作,或者在編譯時(shí)做一部分工作,在運(yùn)行的時(shí)候做另外一部分工作。 在這里講到的很多也許只和程序?qū)τ诠ぷ鳈C(jī)制的操作有關(guān),但是作為初...

    mcterry 評(píng)論0 收藏0
  • Javascript編程之Annotation

    摘要:事實(shí)上,實(shí)現(xiàn)元編程有多種方式,從語(yǔ)言本身來(lái)講,可以分為兩類增強(qiáng)型與新的語(yǔ)法實(shí)現(xiàn),前者的代表是反射,后者的代表為。在第二部分,我們嘗試在語(yǔ)言基礎(chǔ)上增加原生的元編程能力并介紹了該思路的實(shí)現(xiàn)框架。 語(yǔ)言的自由度 自由度這個(gè)概念在不同領(lǐng)域有不同的定義,我們借鑒數(shù)學(xué)中構(gòu)成一個(gè)空間的維數(shù)來(lái)表達(dá)其自由度的做法,在此指的是:解決同一個(gè)問(wèn)題彼此不相關(guān)的設(shè)計(jì)方法學(xué)數(shù)量。 例如,解決一個(gè)比如商品打折的問(wèn)題,...

    SegmentFault 評(píng)論0 收藏0
  • Javascript編程之Annotation

    摘要:事實(shí)上,實(shí)現(xiàn)元編程有多種方式,從語(yǔ)言本身來(lái)講,可以分為兩類增強(qiáng)型與新的語(yǔ)法實(shí)現(xiàn),前者的代表是反射,后者的代表為。在第二部分,我們嘗試在語(yǔ)言基礎(chǔ)上增加原生的元編程能力并介紹了該思路的實(shí)現(xiàn)框架。 語(yǔ)言的自由度 自由度這個(gè)概念在不同領(lǐng)域有不同的定義,我們借鑒數(shù)學(xué)中構(gòu)成一個(gè)空間的維數(shù)來(lái)表達(dá)其自由度的做法,在此指的是:解決同一個(gè)問(wèn)題彼此不相關(guān)的設(shè)計(jì)方法學(xué)數(shù)量。 例如,解決一個(gè)比如商品打折的問(wèn)題,...

    lifesimple 評(píng)論0 收藏0
  • javascript語(yǔ)言精髓與編程實(shí)踐摘要

    摘要:邏輯運(yùn)算一般語(yǔ)言中,邏輯運(yùn)算與布爾元算是等義的,其運(yùn)算元與目標(biāo)類型都是布爾值。除此之外,還有以下的兩條特性運(yùn)算符會(huì)將運(yùn)算元理解為布爾值,以進(jìn)行布爾運(yùn)算。運(yùn)算過(guò)程是支持布爾短路的。 邏輯運(yùn)算 一般語(yǔ)言中,邏輯運(yùn)算與布爾元算是等義的,其運(yùn)算元與目標(biāo)類型都是布爾值。JavaScript當(dāng)然支持這種純布爾運(yùn)算,不但如此,JavaScript還包括另外一種邏輯運(yùn)算,它的表達(dá)式結(jié)果是不確定的。 ...

    shiina 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<