เขียนคำสั่งในภาษา JavaScript TypeScript ให้ยืดหยุ่น ด้วยการเรียก method property ผ่าน key name และการใช้งาน apply call function

อาทิตย์ที่แล้ว ผมได้มีโอกาสเข้าไปดู source code ของ moment.js library เลยเจอกับ code บางส่วนที่ช่วยเพิ่มความยืดหยุ่นในการเขียน program เพื่อรองรับการทำงานแบบ dynamic ด้วยวิธีดังต่อไปนี้

  • การเรียกใช้ object property method ด้วย string name
  • การเรียกใช้ method ด้วย apply และ call function

โดยในตัวอย่างนี้ผมจะใช้ code ตัวอย่างด้วยภาษา TypeScript ที่เป็นภาษา super set ของ JavaScript หมายความว่า

  • TypeScript รองรับทุก syntax และ features ที่ JavaScript มี
  • มีความสามารถนอกเหนือที่จาก JavaScript ไม่มี ที่สำคัญมีเรื่อง Type Safe, interface, abstract
  • เหมาะมากสำหรับการทำ project large scale หรือการทำงานร่วมกันเป็นทีม

โดยเราจะเริ่มตัวอย่างนี้ด้วยการสร้าง User class

User.ts

//ES6 export default to a User class 
export default class User {

    private _firstName: string;
    private _lastName: string;

    //property and method are public by default
    get firstName(): string {
        return this._firstName;
    }

    set firstName(value: string) {
        this._firstName = value;
    }

    get lastName(): string {
        return this._lastName;
    }

    set lastName(value: string) {
        this._lastName = value;
    }

    getFullName(): string {
        return `${this._firstName} ${this._lastName}`
    }

    saySomething(message: string, time: number): string[] {
        var totalMessages: string[] = [];
        for (let index = 0; index < time; index++) {
            totalMessages.push(message);
        }
        return totalMessages;
    }
}

สำหรับ code ตัวอย่างทดสอบการใช้งาน class User ผมขอยกตัวอย่างในรูปแบบของ unit test โดยใช้ test framework ที่ชื่่อ Jasmine โดยผู้อ่านสามารถศึกษาเพิ่มเติมได้จาก

การใช้งาน property และ method แบบปกติ

ตัวอย่างใช้งาน property และ method ของ User object แบบปกติ

UserSpect.ts

describe('with normal style', () => {
    it('should return a correct full name', () => {
        //given
        let user = new User();
        user.firstName = 'Anthony';
        user.lastName = 'CodeSanook';

        //when
        let fullName = user.getFullName();

        //then
        expect(fullName).toBe('Anthony CodeSanook');
    });
});

describe('with dynamic style', () => {
    it('should return a correct full name', () => {
        //given
        let user = new User();
        user['firstName'] = 'Anthony';
        user['lastName'] = 'CodeSanook';

        //when
        let fullName = user['getFullName']();

        //then
        expect(fullName).toBe('Anthony CodeSanook');
    });
});//end describe

ต่อไปเราจะทดสอบการเรียกใช้งาน JavaScript property method name ด้วยการเรียกชื่อผ่าน string key

describe('with dynamic style', () => {
    it('should return a correct full name', () => {
        //given
        let user = new User();
        user['firstName'] = 'Anthony';
        user['lastName'] = 'CodeSanook';

        //when
        let fullName = user['getFullName']();

        //then
        expect(fullName).toBe('Anthony CodeSanook');
    });
});//end describe

หรือ iterate all member names

it('should return all members', () => {
    //given
    let user = new User();
    let userKeys: string[] = [];

    //when
    for (let key in user) {
        userKeys.push(key);
    }

    //then
    expect(userKeys).toEqual(jasmine.arrayContaining(
        [
            'firstName',
            'lastName',
            'getFullName',
            'saySomething'
        ]));
});

การใช้งาน apply และ call function

สอง function นี้ความต่างกันที่ apply เราส่ง arguments เป็น array (a in apply == array) และ call เราส่ง argument เป็น comma separated values (c in call == comma)

ตัวใช้งาน apply function

describe('with apply function ', () => {
    it('should return a correct full name', () => {
        //given
        let userA = new User();

        let userB = new User();
        userB['firstName'] = 'Anthony';
        userB['lastName'] = 'CodeSanook';

        //when
        //use apply we pass arguments as array 
        let fullName = userA.getFullName.apply(userB);

        //then
        expect(fullName).toBe('Anthony CodeSanook');
    });

    it('should return correct total message count', () => {
        //given
        let userA = new User();

        let userB = new User();
        userB['firstName'] = 'Anthony';
        userB['lastName'] = 'CodeSanook';

        //when
        //use apply we pass arguments as array 
        let messages = userA.saySomething.apply(userB, ['Hello World with apply', 5]);
        //then
        expect(messages.length).toBe(5);
    });
});//end describe

ตัวอย่างใช้งาน call function

describe('with call function', () => {
    it('should return a correct full name', () => {
        //given
        let userA = new User();

        let userB = new User();
        userB['firstName'] = 'Anthony';
        userB['lastName'] = 'CodeSanook';

        //when
        //use apply we pass arguments as array 
        let fullName = userA.getFullName.call(userB);

        //then
        expect(fullName).toBe('Anthony CodeSanook');
    });

    it('should return correct total message count', () => {
        //given
        let userA = new User();
        let userB = new User();
        userB['firstName'] = 'Anthony';
        userB['lastName'] = 'CodeSanook';

        //when
        //use apply we pass arguments as comma separated values
        let messages = userA.saySomething.call(userB, 'Hello World with call', 10);

        //then
        expect(messages.length).toBe(10);
    });
});//end describe

ตัวอย่าง output ของ code ที่เราเขียนกัน

enter image description here

download source code ตัวอย่างได้จาก git hub เลยครับ

https://github.com/aaronamm/CodeSanook.Examples

ขั้นตอนการ setup project ในตัวอย่างนี้

  • clone project
  • cd to project directory
  • npm install
  • npm install -g gulp-cli
  • gulp watch