What is a magic number

A numeric constant or string that appears in code but is not interpreted. If you use magic numbers in a program, you probably won’t know what they mean months or years later.

Magic number, magic number, magic number

In the front-end, it is common to use some agreed numbers or strings to represent some business states when defining the interface with the background. For example, some systems may use 0 or 1 to refer to gender in the system. These values are often strongly correlated with business logic and occur repeatedly.

if (gender === '0') {
	// dosomething
}

if (gender === '1') {
    // dosomething
}
Copy the code

If the value is hardcoded into the code, it is difficult for programmers other than the author of the code to understand its purpose and meaning. If this logic is annotated with comments, it will need to be annotated each time it occurs repeatedly. There is also no association or hint from the editor when using literals, which can be difficult to debug if values are misspelled.

Do not use magic numbers

Magic numbers often represent bad coding practices in programming, and the drawbacks are obvious:

  1. The meaning of the values is difficult to understand.

  2. When the value needs to be changed, it may need to be changed in more than one place.

Use a constant

In the case of the above gender, magic numbers can be eliminated by defining constants.

const female = '0' / / women
const male = '1' / / men

if (gender === female) {
	// dosomething
}

if (gender === male) {
    // dosomething
}
Copy the code

A reasonable variable name is a good comment;

Modern editors can also make associations and hints when writing code to avoid errors. If variable names are misspelled, the program will directly throw exceptions. Even if you need a comment, you only need to comment it once when you define a constant, and editors provide the ability to jump to where a variable is defined.

Let’s take a more complicated example

The previous gender case is only a relatively simple scenario, in the actual project may be more complex, need to use magic number more scenarios; The following through an order management function, using Vue to achieve, for example, a complex point;

case

An order can have multiple states at different times. When defining interfaces, numbers (or strings) are used to define different states. Display different operation buttons according to order status.

state The interface values operation
For the payment 0 Go to the payment
To send the goods 1 Push the delivery
For the goods 2 The goods
To evaluate 3 To evaluate

To achieve a list of orders according to the status of the screening query function (enter the page default screening to evaluate the order).

<template>
  <div class="container">
    <div>
      <ElSelect v-model="form.status" style="margin-right: 20px">
        <ElOption label="To be paid" value="0"/>
        <ElOption label="To be shipped" value="1"/>
        <ElOption label="To be received" value="2"/>
        <ElOption label="To be evaluated" value="3"/>
      </ElSelect>
      <ElButton type="primary" @click="handleSearch">The query</ElButton>
    </div>
    <hr>

    <ElTable :data="orderList" type="index">
      <ElTableColumn prop="id" label="Order Number"/>
      <ElTableColumn prop="address" label="Shipping address"/>
      <ElTableColumn prop="phone" label="Mobile phone Number"/>
      <ElTableColumn label="Order Status">
        <template v-slot="{row}">
          <span v-if="row.status === '0'">For the payment</span>
          <span v-if="row.status === '1'">To send the goods</span>
          <span v-if="row.status === '2'">For the goods</span>
          <span v-if="row.status === '3'">To evaluate</span>
        </template>
      </ElTableColumn>

      <ElTableColumn label="Operation">
        <template v-slot="{row}">
          <ElButton v-if="row.status === '0'" type="text">Go to the payment</ElButton>

          <ElButton v-if="row.status === '1'" type="text">Push the delivery</ElButton>

          <ElButton v-if="row.status === '2'" type="text">The goods</ElButton>

          <ElButton v-if="row.status === '3'" type="text">To evaluate</ElButton>
        </template>
      </ElTableColumn>
    </ElTable>
  </div>
</template>

<script>

  export default {
    name: 'OrderList',

    data() {
      return {
        form: {
          status: '3'
        },

        orderList: []}},methods: {
      handleSearch() {
        // simulate asynchronous query
        setTimeout((a)= > {
          this.orderList = [
            {id: 202004000.address: 'Beijing'.phone: 1311112223.status: '0'},
            {id: 202004001.address: 'Shanghai'.phone: 1311112223.status: '1'},
            {id: 202004002.address: Tianjin City.phone: 1311112224.status: '2'},
            {id: 202004003.address: Chongqing Municipality.phone: 1311112225.status: '3'}
          ].filter(item= > item.status === this.form.status)
        }, 500)
      }
    },

    created() {
      this.handleSearch()
    }
  }
</script>

<style>
  .container {
    margin: 70px;
  }
</style>

Copy the code

In the above implementation, the code logic related to the order state is hardcoded, and the magic number is generated.

Although some logic can be realized by other better ways, such as the echo of order status in the list, can be extracted into JS logic, in Vue can be put into filters or functions for logical judgment (switch case can be chosen);

<template>.<ElTableColumn label="Order Status">
     	<template v-slot="{row}">
            {{ row.status | code2Text }}
        </template>
    </ElTableColumn>.</template>
Copy the code
. filters: {// The status code is converted to text
    code2Text(code) {
        switch (code) {
            case '0':
                return 'To be paid'
            case '1':
                return 'To be shipped'
            case '2':
                return 'To be received'
            case '3':
                return 'To be received'}}}...Copy the code

Whether it’s in a template or in js code, whether you use an if judgment or switch, there’s essentially no difference;

Problems with the current implementation

For the current implementation of the code, we first summarize the existing problems, and then step by step to solve;

  1. Recurring order status magic number: Render search boxoptions, in the data state in the list, in the display logic of the action button, including the default search criteriastatus: '3'Both are used; In some cases it may be easier to read with text, but if the project is internationalized or poorly readable in logic without text, the author may not remember what each code means after a few days. It would be verbose if you commented everything out;
  2. If one day the background convention changes the encoding corresponding to a state; Such as:For the goodsIs changed to the corresponding code of'5'There is a lot to change.

Start optimizing, eliminate magic numbers, define constants

  1. Define constants
const status = {
    pendingPayment: '0'./ / for payment
    waiting4Delivery: '1'./ / momentum
    waiting4Receipt: '2'./ / for the goods
    waiting4Evaluation: '3' / / for evaluation
};

export default {
    name: 'OrderList',

    data() {
      return {
       // ...
       status // Define it in data so that it can be used in the template}}}Copy the code
  1. Now change all parts of the code that use the order state to constants. Take the action button part for example:
<ElTableColumn label="Operation">
	<template v-slot="{row}">
		<ElButton v-if="row.status === status.pendingPayment" type="text">Go to the payment</ElButton>

		<ElButton v-if="row.status === status.waiting4Delivery" type="text">Push the delivery</ElButton>

		<ElButton v-if="row.status === status.waiting4Receipt" type="text">The goods</ElButton>

		<ElButton v-if="row.status === status.waiting4Evaluation" type="text">To evaluate</ElButton>
	</template>
</ElTableColumn>
Copy the code

Ps: The operation button part of the code can also be used in VueRender functionTo further optimize (Cn.vuejs.org/v2/guide/re…

Initial filter criteria (default to show orders to be evaluated) :

export default {
    name: 'OrderList',

    data() {
      return {
       // ...
        status, // Define it in data so that it can be used in the template
		form: {
          status: status.waiting4Evaluation
        }
      }
    }
}
Copy the code

The above problem is solved by changing to constants: variable names are more likely to show what the code actually means. Even if you know what the names mean, you can view comments for each constant by jumping to its definition. If you need to change the encoding of a state, just change the constant.

Can you go any further?

In this case, there is a need for the status code to be translated into Chinese in the options of the rendering search box and the data state in the list. And it’s essentially the same whether you put v-if in a template or whether you put it in JS logic; Further, we can define an object with the state encoding as key and the text as value (Ps: JS implements the policy mode in the same way, but the position of the value is a policy). When it needs to be converted, the corresponding text can be obtained through the state encoding.

Without further ado, first the code:

<template>
  <div class="container">
    <div>
      <ElSelect v-model="form.status" style="margin-right: 20px">
        <ElOption
            v-for="(label, code) in status2Text"
            :key="code"
            :value="code"
            :label="label"
        />
      </ElSelect>
      <ElButton type="primary" @click="handleSearch">The query</ElButton>
    </div>
    <hr>

    <ElTable :data="orderList" type="index">
      <ElTableColumn prop="id" label="Order Number"/>
      <ElTableColumn prop="address" label="Shipping address"/>
      <ElTableColumn prop="phone" label="Mobile phone Number"/>

      <template>
        <ElTableColumn label="Order Status">
          <template v-slot="{row}">
            {{ status2Text[row.status] }}
          </template>
        </ElTableColumn>
      </template>

      <ElTableColumn label="Operation">
        <template v-slot="{row}">
          <ElButton v-if="row.status === status.pendingPayment" type="text">Go to the payment</ElButton>

          <ElButton v-if="row.status === status.waiting4Delivery" type="text">Push the delivery</ElButton>

          <ElButton v-if="row.status === status.waiting4Receipt" type="text">The goods</ElButton>

          <ElButton v-if="row.status === status.waiting4Evaluation" type="text">To evaluate</ElButton>
        </template>
      </ElTableColumn>
    </ElTable>
  </div>
</template>

<script>
  // Order status code
  const status = {
    pendingPayment: '0'./ / for payment
    waiting4Delivery: '1'./ / momentum
    waiting4Receipt: '2'./ / for the goods
    waiting4Evaluation: '3' / / for evaluation
  };

  // Order status encoding mapping text
  const status2Text = {
    // This uses ES6 computed Property Name
    [status.pendingPayment]: 'To be paid',
    [status.waiting4Delivery]: 'To be shipped',
    [status.waiting4Receipt]: 'To be received',
    [status.waiting4Evaluation]: 'To be evaluated'
  }

  export default {
    name: 'OrderList',

    data() {
      return {
        status,
        status2Text,
        form: {
          status: status.waiting4Evaluation
        },

        orderList: []}},methods: {
      handleSearch() {
        // simulate asynchronous query
        setTimeout((a)= > {
          this.orderList = [
            {id: 202004000.address: 'Beijing'.phone: 1311112223.status: '0'},
            {id: 202004001.address: 'Shanghai'.phone: 1311112223.status: '1'},
            {id: 202004002.address: Tianjin City.phone: 1311112222.status: '2'},
            {id: 202004003.address: Chongqing Municipality.phone: 1311112222.status: '3'}
          ].filter(item= > item.status === this.form.status)
        }, 500)
      }
    },

    created() {
      this.handleSearch()
    }
  }
</script>

<style>
  .container {
    margin: 70px;
  }
</style>

Copy the code

A mapping object is defined above by calculating attribute names in ES6;

Use {{status2Text[row.status]}} to retrieve the corresponding text in the list, and optimize the branch statement for if judgment (the execution of the corresponding value by key is actually more efficient than the branch statement).

Options in the search box can render options directly by looping the mapping object directly through the template code; Using for-in loops in JS code has the same effect.

Other ways

The Vuex documentation also provides a solution for eliminating the magic number produced by Mutation names

Vuex.vuejs.org/zh/guide/mu… ] (vuex.vuejs.org/zh/guide/mu…).

In addition to using numbers or strings agreed in the interface with the background, the front-end business can also be solved by defining the Symbol type constant; With TS, you can eliminate magic numbers by defining enumerations;

conclusion

Through the above series of operations, it seems that compared to the hard-coded way, increased workload; In fact, by replacing magic numbers with constants, the readability and maintainability of the project are improved and the benefits are higher; The longer the project maintenance time, the more reflected; Adhere to good coding habits;