日常の開発では、リファクタリングの必要性に遭遇することがよくあります。VSCodeの[リファクタリング]メニューには、豊富な操作が用意されています。これは、リファクタリング作業をより効率的に完了するのに役立ちます。
ただし、このメニューの操作方法は毎回異なりますので、一時的に使用するとトラブルが発生します。したがって、このリファクタリング機能に触れない学生がよくいます。
ここでは、参考のために一般的に使用される操作をいくつか要約します。
まず、一般的な名前の変更、ウォームアップ!
名前を変更
名前を変更する理由:名前は、人々が理解できるほど明確ではありません。
手順:
- 変数名を選択するか、右クリックして選択する
重命名符号(Rename Symbol)
か、ショートカットキーを使用しますF2
。 - ポップアップボックスに変更する名前を入力します。
- VSCodeは、後続のすべての関連する名前を変更します。
ウォーミングアップは終了しました。要点を説明しましょう。
リファクタリング操作
-
再構築するコンテンツを選択するか、右クリックして選択する
重构(Refactor)
か、を使用しますCtrl + Shift + R
。 -
選択した内容に応じて、リファクタリング用に次のオプションが表示される場合があります。
-
インポート・エクスポート
- デフォルトのエクスポートを名前付きエクスポートに変換します
- 名前付きエクスポートをデフォルトのエクスポートに変換
- 名前空間のインポートを名前付きのエクスポートに変換します
- 名前付きインポートをnamepaceエクスポートに変換します
-
関数/クラス
- 新しいファイルに移動
-
変数/式
- 定数を抽出する
- 囲んでいるスコープに抽出された定数
- モジュールスコープに抽出された定数
- オプションのチェーン式に変換
- 未使用の宣言を削除する
- 未使用の宣言の前
-
ストリング
- テンプレート文字列に変換テンプレート文字列に変換
-
式/機能
- 抽出機能
- 現在の関数に抽出された内部関数
- モジュールスコープに抽出された関数
- グローバルスコープに抽出された関数
-
对象方法
- generate ‘get’ and ‘set’ accessors 生成get、set处理器
-
类
- generate ‘get’ and ‘set’ accessors 生成get、set处理器
- 将函数转换成 ES2015类
- 将所有函数转换成类
- 提取到 class 'xxx' 中的 Method
- 提取到 class 'xxx' 中的 readonly field
-
魔法数字
为什么要修改魔法数字?因为除进制数之外,数字的实际意义无法被人看懂。
目标:定义一个常量值,写清楚改数字的实际意义。
操作:
- 选中魔法数字进行重构。根据需要,推荐选择:
- 提取到封闭范围的 constant
- 提取到 Module/global 范围的 constant
- 代码抽取到新的变量中,并出现重命名的输入框;
- 使用全大写单词,单词使用“_”间隔。
例子:今年双十一持续13天,计算除双十一促销结束的时间。
function promotionEndDate() {
return new Date(new Date('2022-11-11').getTime() + 13 * 60 * 60 * 24 * 1000);
}
/**
* 修改后:
* 将开始时间 START_DATE,持续的天数 LASTING_DAYS 抽取出来做成变量
* 如果只有一处使用,则在使用到的函数内定义;
* 如果多处都有用,可以考虑放在函数外,模块内。
*/
function promotionEndDate() {
const START_DATE = '2022-11-11';
const LASTING_DAYS = 13;
return new Date(new Date(START_DATE).getTime() + LASTING_DAYS * 60 * 60 * 24 * 1000);
}
复制代码
复杂的逻辑条件
为什么要修改复杂逻辑?复杂的逻辑,往往条件判断繁多,阅读难度比较高。
操作:
- 选中复杂的逻辑条件进行重构。根据需要,选择:
- 提取到封闭范围的 constant
- 提取到当前函数里的 inner function
- 提取到 Module/global 范围的 function
- 代码抽离到一个新的变量/函数中,并出现重命名的输入框;
- 使用驼峰命名,使用 is/has 起头,每个单词首字母大写。
例子:返回指定的某个月有多少天
function monthDay(year, month) {
var day31 = [1, 3, 5, 7, 8, 10, 12];
var day30 = [4, 6, 9, 11];
if (day31.indexOf(month) > -1) {
return 31;
} else if (day30.indexOf(month) > -1) {
return 30;
} else {
if ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)) {
return 29;
} else {
return 28;
}
}
}
/**
* 修改后
* 是否闰年在日期处理函数中会经常使用,所以将其提取到当前模块的最外层了
*/
function monthDay(year, month) {
...
if (day31.indexOf(month) > -1) {
return 31;
} else if (day30.indexOf(month) > -1) {
return 30;
} else {
if (isLeapYear(year)) {
return 29;
} else {
return 28;
}
}
}
function isLeapYear(year) {
return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
}
复制代码
写了注释的代码片段
更推荐代码即注释的理念。我们写注释之前要想明白为什么需要注释?
- 如果代码本身已经很清晰,应该删除注释。
- 如果抽取代码片段,取个合适的名字,能让代码易于阅读,也可以删除注释。
目标:将代码片段抽取出来做成函数,函数以此代码块的具体功能做命名。
操作:
- 选择代码块,重构(Refactor)。选择:
- 提取到当前函数里的 inner function
例子:ajax 请求
function ajax(options) {
options = options || {};
options.type = (options.type || 'GET').toUpperCase();
options.dataType = options.dataType || 'json';
const READY_STATE = 4;
const NET_STATUS = {
OK: 200,
RIDERCT: 300
};
const params = this.formatAjaxParams(options.data);
let xhr;
// 创建 - 非IE6 - 第一步
if (window.XMLHttpRequest) {
xhr = new window.XMLHttpRequest();
} else { // IE6及其以下版本浏览器
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
// 连接 和 发送 - 第二步
if (options.type === 'GET') {
...
} else if (options.type === 'POST') {
...
}
// 接收 - 第三步
xhr.onreadystatechange = function () {
if (xhr.readyState === READY_STATE) {
...
}
};
}
// 修改后
function ajax(options) {
...
let xhr;
create();
connectAndSend();
recieve();
function create() {...}
function connectAndSend() {...}
function recieve() {...}
}
复制代码
过长的函数
功能拆分做成外部函数,再在内部调用。
操作:
- 选择代码块重构,选择:
- 提取到 Module/Global 范围的 function
- コードブロックは、必要なパラメーターを使用して関数を生成します
例:前の例では、ajaxの受信モジュールをモジュールの機能に分離できます
function ajax(options) {
...
create();
recieve();
connectAndSend(options, xhr, params);
}
function connectAndSend(options, xhr, params) {
if (options.type === 'GET') {
...
} else if (options.type === 'POST') {
...
}
}
复制代码
重複するコード/長すぎるファイル
操作:
- コードブロックのリファクタリングを選択し、[新しいファイルに移動]を選択します。
- コードは、現在の関数/クラスをファイル名としてファイルに移行されます。複数のクラス/関数がある場合は、最初のクラス/関数が指定されます
- エクスポートを使用して関数/クラスを公開します。
- importを使用して、元のファイルに関数/クラスをインポートします。
例:日付処理関数:
新しいファイルに移動した後:
index.jsでは、定義されたコードにジャンプすることもできますが、実際には導入されていません。
名前を変更し、インポート/エクスポートを修正します。
インポート・エクスポート
デフォルトと名前空間、名前空間と名前空間の変換。
// named
export function nextMonthDay(year, month) {}
// default
export default function nextMonthDay(year, month) {}
// namepace
import * as refactor from './refactor';
// named
import { nextMonthDay } from './refactor';
复制代码
オブジェクトメソッド
getおよびsetプロセッサを生成する
const person = {
age: 32
};
// 生成get、set处理器
const person = {
_age: 32,
get age() {
return this._age;
},
set age(value) {
this._age = value;
},
};
复制代码
テンプレート文字列
文字列の連結、テンプレート文字列にすばやく変換:
class Person{
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + ' ' + this.lastName;
}
}
// 模板字符串
class Person{
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return `${this.firstName} ${this.lastName}`;
}
}
复制代码
親切
オブジェクトメソッドの結果と同様に、getおよびsetハンドラーを生成します。
クラスxxxに抽出されたメソッドは、上記のコメントに記述され、繰り返されたコードから抽出されたコードに似ています。
ここでは繰り返されません。
ES 2015クラス変換を提供し、プロトタイプメソッド変換をサポートします。
const Person = function() {
this.age = 32;
};
Person.prototype.getAge = function() {
return this.age;
}
Person.prototype.setAge = function(value) {
return this.age = value;
}
// ES 2015 类
class Person {
constructor() {
this.age = 32;
}
getAge() {
return this.age;
}
setAge(value) {
return this.age = value;
}
}
复制代码
要約する
コードをリファクタリングする方法はたくさんありますが、そのうちのいくつかを次に示します。お役に立てば幸いです。
残りのコンテンツについては、コードをリファクタリングするときにリファクタリングメニューをクリックして、驚きがあるかどうかを確認できます。