JavaScriptで優雅に条件判断を行う

14331 ワード

この文章は翻訳文です.
原文のリンク:https://dev.to/hellomeghna/tips-to-write-better-conditionals-in-javascript-2189
条件語句とは何ですか?
どんなプログラミング言語であっても、コードの中には判断が欠かせないし、入力した条件によって異なる操作を実行します.
例えば、あるゲームでは、プレイヤーのライフが0以下の場合、ゲームは終了します.天気予報アプリでは、朝に太陽の写真を表示し、夜に星と月の写真を表示します.本論文ではJavaScriptで類似の条件処理を行う方法を紹介します.
JavaScriptを書くと、多くの条件を含めたコードがたくさん書かれます.これらの条件語句は、最初はよく理解できたかもしれませんが、しばらくするとめちゃくちゃになります.実はif/elseよりも良い方法で条件判断を実現しています.
ここでは清潔で優雅な条件の判断についてのアドバイスがあります.
目次
  • Aray.includes
  • 早期終了し、
  • に戻る.
  • Switch式
  • をObjectで巡回またはMapで置換します.
  • は、デフォルトパラメータと構成解除値
  • を使用する.
  • Aray.everyおよびAray.someを使用して、すべておよび部分的条件判断
  • を実現する.
  • Use Optional Chining and Nullish Coalescing
  • 1.アラy.includes
    複数の条件があればArray.includesを使用することができる.
    たとえば:
    function printAnimals(animal) {
        if (animal === 'dog' || animal === 'cat') {
            console.log(`I have a ${animal}`);
        }
    }
    
    console.log(printAnimals('dog')); // I have a dog
    
    上のコードはよさそうです.二つの小動物を検査するだけです.しかし、私たちはユーザーが何を入力するかは分かりません.もし動物のタイプが多くなったら?||条件を拡張して判断し続けると、需要を満たすためにコードが維持されにくくなり、乱雑に見えるようになります.
    ソリューション:Array.includesを使って上記のコードを再構築できます.
    function printAnimals(animal) {
        const animals = ['dog', 'cat', 'hamster', 'turtle'];
    
        if (animals.includes(animal)) {
            console.log(`I have a ${animal}`);
        }
    }
    
    console.log(printAnimals('hamster')); // I have a hamster
    
    ここでは、配列保存動物を作成しました.このように条件を判断すれば、残りのコードと分離できます.今、条件を拡張し続けたいなら、行列に新しい要素を追加すればいいです.(はっきりしましたね)
    2.早期退出と返却
    これはとてもクールなテクニックです.コードをシンプルに見せてくれます.私は仕事の初日から教えられました.条件判断にはearly exitが必要です.
    前の例のために条件を追加します.アニメーションが単なるstringではなく、特定の属性を持つobjectである場合.
    だから今の需要は次のようになりました.
  • もしanimalがないならば、1つの誤りを投げます.
  • アニマルのタイプ
  • をプリントアウトしました.
  • アニマルの名前をプリントしました.
  • アニマルのタイプ
  • をプリントアウトしました.
    const printAnimalDetails = animal => {
        let result; // declare a variable to store the final value
    
        // condition 1: check if animal has a value
        if (animal) {
    
            // condition 2: check if animal has a type property
            if (animal.type) {
    
                // condition 3: check if animal has a name property
                if (animal.name) {
    
                    // condition 4: check if animal has a gender property
                    if (animal.gender) {
                        result = `${animal.name} is a ${animal.gender} ${animal.type};`;
                    } else {
                        result = "No animal gender";
                    }
                } else {
                    result = "No animal name";
                }
            } else {
                result = "No animal type";
            }
        } else {
            result = "No animal";
        }
    
        return result;
    };
    
    console.log(printAnimalDetails()); // 'No animal'
    
    console.log(printAnimalDetails({type: "dog", gender: "female"})); // 'No animal name'
    
    console.log(printAnimalDetails({type: "dog", name: "Lucy"})); // 'No animal gender'
    
    console.log(
        printAnimalDetails({type: "dog", name: "Lucy", gender: "female"})
    ); // 'Lucy is a female dog'
    
    上のコードについてどう思いますか?
    上のコードはバグがないですが、長すぎてメンテナンスが難しいです.新人は午前中にどの括弧を探しに来ますか?もう少し論理が複雑なら、if/elseはもっと多いです.上記のコードは?:$$演算子などで再構成できます.でも、私は(はははは…)ません.上のコードを再構築するために何度もreturnを使いました.
    const printAnimalDetails = ({type, name, gender } = {}) => {
        if(!type) return 'No animal type';
        if(!name) return 'No animal name';
        if(!gender) return 'No animal gender';
    
    // Now in this line of code, we're sure that we have an animal with all //the three properties here.
    
        return `${name} is a ${gender} ${type}`;
    }
    
    console.log(printAnimalDetails()); // 'No animal type'
    
    console.log(printAnimalDetails({ type: dog })); // 'No animal name'
    
    console.log(printAnimalDetails({ type: dog, gender: female })); // 'No animal name'
    
    console.log(printAnimalDetails({ type: dog, name: 'Lucy', gender: 'female' })); // 'Lucy is a female dog'
    
    
    再構成版では、オブジェクトの構成値と関数パラメータのデフォルト値も使用されます.デフォルトの値の役割は、私たちが通信していなければ、エラーがないということです.
    別の例:
    function printVegetablesWithQuantity(vegetable, quantity) {
        const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];
    
        // condition 1: vegetable should be present
        if (vegetable) {
            // condition 2: must be one of the item from the list
            if (vegetables.includes(vegetable)) {
                console.log(`I like ${vegetable}`);
    
                // condition 3: must be large quantity
                if (quantity >= 10) {
                    console.log('I have bought a large quantity');
                }
            }
        } else {
            throw new Error('No vegetable from the list!');
        }
    }
    
    printVegetablesWithQuantity(null); //  No vegetable from the list!
    printVegetablesWithQuantity('cabbage'); // I like cabbage
    printVegetablesWithQuantity('cabbage', 20);
    // 'I like cabbage`
    // 'I have bought a large quantity'
    
    現在、上記の例には以下が含まれています.
  • ペアif/elseは、利用できない条件をフィルタリングするために使用される
  • です.
  • レベルネストif
  • 次に私達のコースを紹介します. .
    function printVegetablesWithQuantity(vegetable, quantity) {
    
        const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];
    
        // condition 1: throw error early
        if (!vegetable) throw new Error('No vegetable from the list!');
    
        // condition 2: must be in the list
        if (vegetables.includes(vegetable)) {
            console.log(`I like ${vegetable}`);
    
            // condition 3: must be a large quantity
            if (quantity >= 10) {
                console.log('I have bought a large quantity');
            }
        }
    }
    
    このように再構成した後に、私達は1階のifの入れ子が足りなくなりました.あなたの条件が比較的に長いと判断した時、このコードスタイルは特に使いやすいです.ifネストをさらに減らすことができます.条件を反転してreturnを採用します.以下は具体的な実現です.
    function printVegetablesWithQuantity(vegetable, quantity) {
    
        const vegetables = ['potato', 'cabbage', 'cauliflower', 'asparagus'];
    
        if (!vegetable) throw new Error('No vegetable from the list!');
        // condition 1: throw error early
    
        if (!vegetables.includes(vegetable)) return;
        // condition 2: return from the function is the vegetable is not in 
        //  the list 
    
    
        console.log(`I like ${vegetable}`);
    
        // condition 3: must be a large quantity
        if (quantity >= 10) {
            console.log('I have bought a large quantity');
        }
    }
    
    第二の条件を逆にすることで、コードにはifの入れ子が見えなくなりました.この技法は、多くの条件があるときに判断し、あるものを満たすときに、残りの論理処理を行わないようにするのに適しています.
    ですから、私たちの目標はネストを撲滅し、早めにリターンすることです.でも、リターンがいいです.「カップを貪る」こともできません.
    3.SwitchをObjectで巡回またはMapで置換する表式
    この例を見てみましょう.色に基づいて果物をプリントアウトしたいです.
    function printFruits(color) {
        // use switch case to find fruits by color
        switch (color) {
            case 'red':
                return ['apple', 'strawberry'];
            case 'yellow':
                return ['banana', 'pineapple'];
            case 'purple':
                return ['grape', 'plum'];
            default:
                return [];
        }
    }
    
    printFruits(null); // []
    printFruits('yellow'); // ['banana', 'pineapple']
    
    
    上のコードは間違っていません.ちょっと長いように見えます.私たちはObjectを使って同じ効果を実現できます.
    // use object literal to find fruits by color
    const fruitColor = {
        red: ['apple', 'strawberry'],
        yellow: ['banana', 'pineapple'],
        purple: ['grape', 'plum']
    };
    
    function printFruits(color) {
        return fruitColor[color] || [];
    }
    
    もちろんMapも使えます.
    // use Map to find fruits by color
    const fruitColor = new Map()
        .set('red', ['apple', 'strawberry'])
        .set('yellow', ['banana', 'pineapple'])
        .set('purple', ['grape', 'plum']);
    
    function printFruits(color) {
        return fruitColor.get(color) || [];
    }
    
    
    MapはES 2015(Es 6)の文法です.互換性に注意してください.Array.filterを用いても実現できる.
    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'strawberry', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'pineapple', color: 'yellow' },
        { name: 'grape', color: 'purple' },
        { name: 'plum', color: 'purple' }
    ];
    
    function printFruits(color) {
        return fruits.filter(fruit => fruit.color === color);
    }
    
    4.デフォルトのパラメータとプロファイルを使う
    私たちはJavaScriptを書く時、常にnull/undefinedをチェックして、パラメータに対してデフォルト値を賦課します.そうでないとエラーが発生します.
    function printVegetablesWithQuantity(vegetable, quantity = 1) {
    // if quantity has no value, assign 1
    
        if (!vegetable) return;
        console.log(`We have ${quantity} ${vegetable}!`);
    }
    
    //results
    printVegetablesWithQuantity('cabbage'); // We have 1 cabbage!
    printVegetablesWithQuantity('potato', 2); // We have 2 potato!
    
    
    もしvegetableがオブジェクトだったら?私たちはそれにデフォルトの割り当てができますか?
    function printVegetableName(vegetable) {
        if (vegetable && vegetable.name) {
            console.log (vegetable.name);
        } else {
            console.log('unknown');
        }
    }
    
    printVegetableName(undefined); // unknown
    printVegetableName({}); // unknown
    printVegetableName({ name: 'cabbage', quantity: 2 }); // cabbage
    
    上記の例では、vegetableに利用可能な値があれば、そのnameをプリントアウトします.そうでなければunknowをプリントします.if (vegetable && vegetable.name) {}の代わりに、デフォルト値と構成解除値を使用することができます.
    // destructing - get name property only
    // assign default empty object {}
    
    function printVegetableName({name} = {}) {
        console.log (name || 'unknown');
    }
    
    
    printVegetableName(undefined); // unknown
    printVegetableName({ }); // unknown
    printVegetableName({ name: 'cabbage', quantity: 2 }); // cabbage
    
    
    name属性が必要ですので、{name}を解凍して、name変数を使用できます.これでvegetable.nameを使う必要がなくなります.
    私たちも関数のパラメータにデフォルト値{}を設定しました.そうでないとprintVegeTable(undefined)を実行するとCannot destructure property name of undefined or nullにエラーが発生します.
    5.Aray.everyとAray.someを使って、すべてと一部の条件判断を実現する.
    私は配列のこれらの方法を使ってコードの行数を減らすことができます.下のコードの中で果物は全部赤いかどうかを判断したいです.
    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'grape', color: 'purple' }
    ];
    
    function test() {
        let isAllRed = true;
    
        // condition: all fruits must be red
        for (let f of fruits) {
            if (!isAllRed) break;
            isAllRed = (f.color == 'red');
        }
    
        console.log(isAllRed); // false
    }
    
    コードが長すぎます.私たちはAray.everyを変えてみてもいいです.
    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'grape', color: 'purple' }
    ];
    
    function test() {
        // condition: short way, all fruits must be red
        const isAllRed = fruits.every(f => f.color == 'red');
    
        console.log(isAllRed); // false
    }
    
    
    同じように、一部の果物が赤色であると判断したいなら、私達はアラy.someで実現できます.
    const fruits = [
        { name: 'apple', color: 'red' },
        { name: 'banana', color: 'yellow' },
        { name: 'grape', color: 'purple' }
    ];
    
    function test() {
        // condition: if any fruit is red
        const isAnyRed = fruits.some(f => f.color == 'red');
    
        console.log(isAnyRed); // true
    }
    
    6.Use Optional Chining and Nullish Coalescing
    この2つの機能はJavaScriptにおいて非常に有用である.しかし、今のところサポートがあまりよくないので、バベルを使ってコンパイルをしなければなりません.undefinedは、中間層をスキップして、ツリー構造がある属性を含むかどうかを確認することができるようにする.Optional chainingは、およびnullish coalescingは、変数にデフォルト値を割り当てて使用する.
    以下は例です.
    const car = {
        model: 'Fiesta',
        manufacturer: {
            name: 'Ford',
            address: {
                street: 'Some Street Name',
                number: '5555',
                state: 'USA'
            }
        }
    }
    
    // to get the car model
    const model = car && car.model || 'default model';
    
    // to get the manufacturer street
    const street = car && car.manufacturer && car.manufacturer.address &&
        car.manufacturer.address.street || 'default street';
    
    // request an un-existing property
    const phoneNumber = car && car.manufacturer && car.manufacturer.address
        && car.manufacturer.phoneNumber;
    
    console.log(model) // 'Fiesta'
    console.log(street) // 'Some Street Name'
    console.log(phoneNumber) // undefined
    
    
    ですから、もし車のメーカーが米国かどうかを考えたら、コードを書かなければなりません.
    const isManufacturerFromUSA = () => {
        if(car && car.manufacturer && car.manufacturer.address &&
            car.manufacturer.address.state === 'USA') {
            console.log('true');
        }
    }
    
    
    checkCarManufacturerState() // 'true'
    
    このようにコードを書くのはどんなに乱れているかを見ることができます.すでにいくつかの第三者ライブラリがあります.例えば、Optional chainingまたはlodashは自分の関数を持っています.この操作を簡単にします.例えばidxlodash.しかし、JavaScriptがそのままこの操作をサポートしてくれるといいです.
    次はその例です.
    // to get the car model
    const model = car?.model ?? 'default model';
    
    // to get the manufacturer street
    const street = car?.manufacturer?.address?.street ?? 'default street';
    
    // to check if the car manufacturer is from the USA
    const isManufacturerFromUSA = () => {
        if(car?.manufacturer?.address?.state === 'USA') {
            console.log('true');
        }
    }
    
    
    このコードはかなり綺麗に見えます.メンテナンスも簡単です.この特性はすでに_.getの提案の中にあります.私たちはあとで使えます.
    締め括りをつける
    これらの提案を使って、清潔でメンテナンスしやすいコードを書いてみましょう.長い条件判断のため、あと数ヶ月であなた自身が読めなくなります.