Heim > Web-Frontend > js-Tutorial > Hauptteil

Bringen Sie Ihnen bei, wie Sie kleine und übersichtliche JavaScript-Funktionen schreiben

怪我咯
Freigeben: 2017-04-05 14:57:36
Original
1002 Leute haben es durchsucht

In diesem Artikel wird anhand von JavaScript erläutert, wie Funktionen optimiert werden können, um sie klarer, leichter lesbar, effizienter und stabiler zu machen.

Die Komplexität von Software nimmt weiter zu. Die Qualität des Codes ist sehr wichtig, um die Zuverlässigkeit und Skalierbarkeit der Anwendung sicherzustellen.

Allerdings hat fast jeder Entwickler, mich eingeschlossen, im Laufe seiner Karriere Code von geringer Qualität gesehen. Dieses Ding ist eine Falle. Code von geringer Qualität weist die folgenden äußerst schädlichen Eigenschaften auf:

  • Die Funktionen sind sehr lang und voller chaotischer Funktionen aller Art.

  • Funktionen haben normalerweise einige Nebenwirkungen, die nicht nur schwer zu verstehen, sondern sogar unmöglich zu debuggen sind.

  • Mehrdeutige Funktions- und Variablenbenennung.

  • Fragiler Code: Eine kleine Änderung kann unerwartet andere Anwendungskomponenten beschädigen.

  • Codeabdeckung fehlt.

Sie klingen im Grunde wie folgt: „Ich habe keine Ahnung, wie dieser Code funktioniert“, „Dieser Code ist nur ein Durcheinander“, „Dieser Teil des Codes muss geändert werden.“ ist wirklich schwierig“ usw.

Ich bin einmal auf eine solche Situation gestoßen. Einer meiner Kollegen hat gekündigt, weil er nicht weiter eine Ruby-basierte REST-API erstellen konnte. Dieses Projekt übernahm er vom bisherigen Entwicklungsteam.

Bestehende Fehler beheben, dann neue Fehler einführen, neue Funktionen hinzufügen, eine Reihe neuer Fehler hinzufügen und so weiter (sogenannter fragiler Code). Die Kunden wollten nicht die gesamte Anwendung mit einem besseren Design umgestalten, und die Entwickler trafen die kluge Entscheidung, den Status Quo beizubehalten.

Bringen Sie Ihnen bei, wie Sie kleine und übersichtliche JavaScript-Funktionen schreiben

Nun, das passiert ständig und es ist ziemlich schlimm. Was können wir also tun?

Zuallererst müssen Sie bedenken: Nur die Anwendung zum Laufen zu bringen und die Qualität des Codes sicherzustellen, sind zwei völlig verschiedene Dinge. Einerseits müssen Sie Produktanforderungen umsetzen. Aber andererseits sollten Sie sich die Zeit nehmen, sicherzustellen, dass Ihre Funktionen einfach sind, lesbare Variablen- und Funktionsnamen verwenden, Funktionsnebeneffekte vermeiden usw.

Funktionen (einschließlich Objektmethoden) sind die Zahnräder, die Anwendungen zum Laufen bringen. Zunächst sollten Sie sich auf deren Struktur und Gesamtlayout konzentrieren. Dieser Artikel enthält einige großartige Beispiele, die zeigen, wie man Funktionen schreibt, die klar, leicht zu verstehen und zu testen sind.

1. Funktionen sollten klein, sehr klein sein

Vermeiden Sie die Verwendung großer Funktionen, die eine große Anzahl von Funktionen enthalten, und teilen Sie ihre Funktionen in mehrere kleinere Funktionen auf. Große Black-Box-Funktionen sind schwer zu verstehen, zu ändern und vor allem schwer zu testen.

Angenommen, Sie müssen ein solches Szenario implementieren, um die Gewichtung eines Arrays, einer Karte oder eines gewöhnlichen JavaScript-Objekts zu berechnen. Das Gesamtgewicht kann durch Berechnen des Gewichts jedes Mitglieds ermittelt werden:

  • null oder undefinierte Variablen zählen 1 Punkt.

  • Grundtyp zählt 2 Punkte.

  • Objekt oder Funktion zählt 4 Punkte.

Zum Beispiel wird die Gewichtung des Arrays [null, 'Hello World', {}] wie folgt berechnet: 1 (null) + 2 (String ist der Grundtyp) + 4 (Objekt) = 7 .

Schritt 0: Die erste große Funktion

Wir beginnen mit dem schlechtesten Beispiel. Die gesamte Logik ist in der Funktion getCollectionWeight() codiert:

function getCollectionWeight(collection) {  
  letcollectionValues;
  if (collectioninstanceof Array) {
    collectionValues = collection;
  } else if (collectioninstanceof Map) {
    collectionValues = [...collection.values()];
  } else {
    collectionValues = Object.keys(collection).map(function (key) {
      return collection[key];
    });
  }
  return collectionValues.reduce(function(sum, item) {
    if (item == null) {
      return sum + 1;
    } 
    if (typeof item === 'object' || typeof item === 'function') {
      return sum + 4;
    }
    return sum + 2;
  }, 0);
}
letmyArray = [null, { }, 15];  
letmyMap = new Map([ ['functionKey', function() {}] ]);  
letmyObject = { 'stringKey': 'Hello world' };  
getCollectionWeight(myArray);  // => 7 (1 + 4 + 2)   getCollectionWeight(myMap);    // => 4   getCollectionWeight(myObject); // => 2
Nach dem Login kopieren

Das Problem liegt auf der Hand, die Funktion getCollectionWeight() ist superlang und sieht aus wie eine Blackbox voller „Unfälle“. Vielleicht haben Sie auch festgestellt, dass Sie nicht auf den ersten Blick erkennen können, was es bewirkt. Stellen Sie sich noch einmal vor, dass die Anwendung viele solcher Funktionen enthält.

Die Begegnung mit solchem ​​Code bei der Arbeit ist Zeit- und Energieverschwendung. Andererseits verursacht qualitativ hochwertiger Code keine Unannehmlichkeiten. In hochwertigem Code sind gut geschriebene, selbstdokumentierte Funktionen leicht zu lesen und zu verstehen.

Bringen Sie Ihnen bei, wie Sie kleine und übersichtliche JavaScript-Funktionen schreiben

Schritt 1: Berechnen Sie das Gewicht je nach Typ und lassen Sie diese „mysteriösen Zahlen“ hinter sich.

Unser Ziel ist es nun, diese riesige Funktion in eine Reihe kleinerer, unabhängiger und wiederverwendbarer Funktionen aufzuteilen. Der erste Schritt besteht darin, den Code zu extrahieren, der das Gewicht je nach Typ berechnet. Diese neue Funktion heißt getWeight().

Werfen wir einen Blick auf diese „geheimnisvollen Zahlen“: 1, 2, 4. Ohne den Kontext der gesamten Geschichte zu kennen, liefern diese Zahlen allein keine nützlichen Informationen. Glücklicherweise können Sie mit ES2015 statische schreibgeschützte Referenzen definieren, sodass Sie einfach ein paar Konstanten erstellen und diese „mysteriösen Zahlen“ durch aussagekräftige Namen ersetzen können. (Mir gefällt besonders der Begriff „mysteriöse Zahl“: D)

Erstellen wir eine neue kleinere Funktion getWeightByType() und verwenden sie zur Verbesserung von getCollectionWeight():

// Code extracted into getWeightByType() function getWeightByType(value) {  
  const WEIGHT_NULL_UNDEFINED  = 1;
  const WEIGHT_PRIMITIVE      = 2;
  const WEIGHT_OBJECT_FUNCTION = 4;
  if (value == null) {
    return WEIGHT_NULL_UNDEFINED;
  } 
  if (typeof value === 'object' || typeof value === 'function') {
    return WEIGHT_OBJECT_FUNCTION;
  }
  return WEIGHT_PRIMITIVE;
} function getCollectionWeight(collection) {  
  letcollectionValues;
  if (collectioninstanceof Array) {
    collectionValues = collection;
  } else if (collectioninstanceof Map) {
    collectionValues = [...collection.values()];
  } else {
    collectionValues = Object.keys(collection).map(function (key) {
      return collection[key];
    });
  }
  return collectionValues.reduce(function(sum, item) {
    return sum + getWeightByType(item);
  }, 0);
}
letmyArray = [null, { }, 15];  
letmyMap = new Map([ ['functionKey', function() {}] ]);  
letmyObject = { 'stringKey': 'Hello world' };  
getCollectionWeight(myArray);  // => 7 (1 + 4 + 2)   getCollectionWeight(myMap);    // => 4   getCollectionWeight(myObject); // => 2
Nach dem Login kopieren

Sieht besser aus, oder? Die Funktion getWeightByType() ist eine unabhängige Komponente und wird nur zur Bestimmung des Gewichtswerts jedes Typs verwendet. Und es ist wiederverwendbar, Sie können es für jede andere Funktion verwenden.

getCollectionWeight() ist etwas schlanker.

WEIGHT_NULL_UNDEFINED, WEIGHT_PRIMITIVE und WEIGHT_OBJECT_FUNCTION sind alles Konstanten mit Selbstdokumentationsfunktionen. Die Gewichte verschiedener Typen können anhand ihrer Namen gesehen werden. Sie müssen nicht raten, was die Zahlen 1, 2 und 4 bedeuten.

Schritt 2: Weiter segmentieren und skalierbar machen

Diese aktualisierte Version weist jedoch immer noch Mängel auf. Angenommen, Sie planen, die Gewichtsberechnung für einen Satz oder sogar für andere benutzerdefinierte Sätze zu implementieren. getCollectionWeight() kann sich schnell aufblähen, da es eine Reihe spezifischer Logik zum Erhalten von Gewichten enthält.

Lassen Sie uns den Code zum Abrufen der Gewichtungen der Karten in getMapValues() extrahieren und den Code zum Abrufen der Gewichtungen des grundlegenden JavaScript-Objekts in getPlainObjectValues() einfügen. Schauen Sie sich die verbesserte Version an.

function getWeightByType(value) {  
  const WEIGHT_NULL_UNDEFINED = 1;
  const WEIGHT_PRIMITIVE = 2;
  const WEIGHT_OBJECT_FUNCTION = 4;
  if (value == null) {
    return WEIGHT_NULL_UNDEFINED;
  } 
  if (typeof value === 'object' || typeof value === 'function') {
    return WEIGHT_OBJECT_FUNCTION;
  }
  return WEIGHT_PRIMITIVE;
} // Code extracted into getMapValues() function getMapValues(map) {  
  return [...map.values()];
} // Code extracted into getPlainObjectValues() function getPlainObjectValues(object) {  
  return Object.keys(object).map(function (key) {
    return object[key];
  });
} function getCollectionWeight(collection) {  
  letcollectionValues;
  if (collectioninstanceof Array) {
    collectionValues = collection;
  } else if (collectioninstanceof Map) {
    collectionValues = getMapValues(collection);
  } else {
    collectionValues = getPlainObjectValues(collection);
  }
  return collectionValues.reduce(function(sum, item) {
    return sum + getWeightByType(item);
  }, 0);
}
letmyArray = [null, { }, 15];  
letmyMap = new Map([ ['functionKey', function() {}] ]);  
letmyObject = { 'stringKey': 'Hello world' };  
getCollectionWeight(myArray);  // => 7 (1 + 4 + 2)   getCollectionWeight(myMap);    // => 4   getCollectionWeight(myObject); // => 2
Nach dem Login kopieren

Schauen Sie sich nun die Funktion getCollectionWeight() an. Sie werden feststellen, dass deren Mechanismus leichter zu verstehen ist und wie eine interessante Geschichte aussieht.

Jede Funktion ist einfach und klar. Sie müssen sich nicht lange mit dem Code befassen, um zu verstehen, wie er funktioniert. So sollte frischer Code aussehen.

Schritt 3: Optimierung hört nie auf

Auch auf dieser Ebene gibt es noch viel Raum für Optimierung!

Sie können eine eigenständige Funktion getCollectionValues() erstellen und if/else-Anweisungen verwenden, um die Typen in der Sammlung zu unterscheiden:

function getCollectionValues(collection) {  
  if (collectioninstanceof Array) {
    return collection;
  }
  if (collectioninstanceof Map) {
    return getMapValues(collection);
  }
  return getPlainObjectValues(collection);
}
Nach dem Login kopieren

Dann sollte getCollectionWeight() extrem rein werden, weil es ist Einzige Aufgabe: Holen Sie sich die Werte in der Sammlung mit getCollectionValues ​​() und rufen Sie dann nacheinander den Summierungsakkumulator auf.

你也可以创建一个独立的累加器函数:

function reduceWeightSum(sum, item) {  
  return sum + getWeightByType(item);
}
Nach dem Login kopieren

理想情况下 getCollectionWeight() 函数中不应该定义函数。

最后,最初的巨型函数,已经被转换为如下一组小函数:

function getWeightByType(value) {  
  const WEIGHT_NULL_UNDEFINED = 1;
  const WEIGHT_PRIMITIVE = 2;
  const WEIGHT_OBJECT_FUNCTION = 4;
  if (value == null) {
    return WEIGHT_NULL_UNDEFINED;
  } 
  if (typeof value === 'object' || typeof value === 'function') {
    return WEIGHT_OBJECT_FUNCTION;
  }
  return WEIGHT_PRIMITIVE;
} function getMapValues(map) {  
  return [...map.values()];
} function getPlainObjectValues(object) {  
  return Object.keys(object).map(function (key) {
    return object[key];
  });
} function getCollectionValues(collection) {  
  if (collectioninstanceof Array) {
    return collection;
  }
  if (collectioninstanceof Map) {
    return getMapValues(collection);
  }
  return getPlainObjectValues(collection);
} function reduceWeightSum(sum, item) {  
  return sum + getWeightByType(item);
} function getCollectionWeight(collection) {  
  return getCollectionValues(collection).reduce(reduceWeightSum, 0);
}
letmyArray = [null, { }, 15];  
letmyMap = new Map([ ['functionKey', function() {}] ]);  
letmyObject = { 'stringKey': 'Hello world' };  
getCollectionWeight(myArray);  // => 7 (1 + 4 + 2)   getCollectionWeight(myMap);    // => 4   getCollectionWeight(myObject); // => 2
Nach dem Login kopieren

这就是编写简单精美函数的艺术!

除了这些代码质量上的优化之外,你也得到不少其他的好处:

  • 通过代码自文档,getCollectionWeight() 函数的可读性得到很大提升。

  • getCollectionWeight() 函数的长度大幅减少。

  • 如果你打算计算其他类型的权重值,getCollectionWeight() 的代码不会再剧烈膨胀了。

  • 这些拆分出来的函数都是低耦合、高可复用的组件,你的同事可能希望将他们导入其他项目中,而你可以轻而易举的实现这个要求。

  • 当函数偶发错误的时候,调用栈会更加详细,因为栈中包含函数的名称,甚至你可以立马发现出错的函数。

  • 这些小函数更简单、易测试,可以达到很高的代码覆盖率。与其穷尽各种场景来测试一个大函数,你可以进行结构化测试,分别测试每一个小函数。

  • 你可以参照 CommonJS 或 ES2015 模块格式,将拆分出的函数创建为独立的模块。这将使得你的项目文件更轻、更结构化。

这些建议可以帮助你,战胜应用的复杂性。

Bringen Sie Ihnen bei, wie Sie kleine und übersichtliche JavaScript-Funktionen schreiben

原则上,你的函数不应当超过 20 行——越小越好。

现在,我觉得你可能会问我这样的问题:“我可不想将每一行代码都写为函数。有没有什么准则,告诉我何时应当停止拆分?”。这就是接下来的议题了。

2. 函数应当是简单的

让我们稍微放松一下,思考下应用的定义到底是什么?

每一个应用都需要实现一系列需求。开发人员的准则在于,将这些需求拆分为一些列较小的可执行组件(命名空间、类、函数、代码块等),分别完成指定的工作。

一个组件又由其他更小的组件构成。如果你希望编写一个组件,你只能从抽象层中低一级的组件中,选取需要的组件用于创建自己的组件。

换言之,你需要将一个函数分解为若干较小的步骤,并且保证这些步骤都在抽象上,处于同一级别,而且只向下抽象一级。这非常重要,因为这将使得函数变得简单,做到“做且只做好一件事”。

为什么这是必要的?因为简单的函数非常清晰。清晰就意味着易于理解和修改。

我们来举个例子。假设你需要实现一个函数,使数组仅保留 素数 (2, 3, 5, 7, 11 等等),移除非素数(1, 4, 6, 8 等等)。函数的调用方式如下:

getOnlyPrime([2, 3, 4, 5, 6, 8, 11]); // => [2, 3, 5, 11]
Nach dem Login kopieren

如何用低一级抽象的若干步骤实现 getOnlyPrime() 函数呢?我们这样做:

为了实现 getOnlyPrime() 函数, 我们用 isPrime() 函数来过滤数组中的数字。

非常简单,只需要对数字数组执行一个过滤函数 isPrime() 即可。

你需要在当前抽象层实现 isPrime() 的细节吗?不,因为 getOnlyPrime() 函数会在不同的抽象层实现一些列步骤。否则,getOnlyPrime() 会包含过多的功能。

在头脑中谨记简单函数的理念,我们来实现 getOnlyPrime() 函数的函数体:

function getOnlyPrime(numbers) {  
  return numbers.filter(isPrime);
}
getOnlyPrime([2, 3, 4, 5, 6, 8, 11]); // => [2, 3, 5, 11]
Nach dem Login kopieren

如你所见, getOnlyPrime() 非常简单,它仅仅包含低一级抽象层的步骤:数组的 .filter() 方法和 isPrime() 函数。

现在该进入下一级抽象。

数组的 .filter() 方法由 JavaScript 引擎提供,我们直接使用即可。当然,标准已经 准确描述 了它的行为。

现在你可以深入如何实现 isPrime() 的细节中了:

为了实现 isPrime() 函数检查一个数字 n 是否为素数,只需要检查 2 到 Math.sqrt(n) 之间的所有整数是否均不能整除n。

有了这个算法(不算高效,但是为了简单起见,就用这个吧),我们来为 isPrime() 函数编码:

function isPrime(number) {  
  if (number === 3 || number === 2) {
    return true;
  }
  if (number === 1) {
    return false;
  }
  for (letpisor = 2; pisor <= Math.sqrt(number); pisor++) {
    if (number % pisor === 0) {
      return false;
    }
  }
  return true;
} function getOnlyPrime(numbers) {  
  return numbers.filter(isPrime);
}
getOnlyPrime([2, 3, 4, 5, 6, 8, 11]); // => [2, 3, 5, 11]
Nach dem Login kopieren

getOnlyPrime() 很小也很清晰。它只从更低一级抽象中获得必要的一组步骤。

只要你按照这些规则,将函数变的简洁清晰,复杂函数的可读性将得到很大提升。将代码进行精确的抽象分级,可以避免出现大块的、难以维护的代码。

3. 使用简练的函数名称

函数名称应该非常简练:长短适中。理想情况下,名称应当清楚的概括函数的功用,而不需要读者深入了解函数的实现细节。

对于使用 骆驼风格 的函数名称,以小写字母开始:  addItem(), saveToStore() 或者  getFirstName() 之类。

由于函数都是某种操作,因此名称中至少应当包含一个动词。例如 deletePage(), verifyCredentials() 。需要 get 或 set 属性的时候,请使用 标准的  set 和 get 前缀: getLastName() 或  setLastName() 。

避免在生产代码中出现有误导性的名称,例如 foo(), bar(), a(), fun() 等等。这样的名称没有任何意义。

如果函数都短小清晰,命名简练:代码读起来就会像诗一样迷人。

4. 总结

当然了,这里假定的例子都非常简单。现实中的代码更加复杂。你可能要抱怨,编写清晰的函数,只在抽象上一级一级下降,实在太没劲了。但是如果从项目一开始就开始你的实践,就远没有想象中复杂。

如果应用中已经存在一些功能繁杂的函数,希望对它们进行重构,你可能会发现困难重重。而且在很多情况下,在合理的时间内是不可能完成的。但千里之行始于足下:在力所能及的前提下,先拆分一部分出来。

Natürlich sollte die richtigste Lösung darin bestehen, die Anwendung von Beginn des Projekts an richtig umzusetzen. Neben der Implementierung sollten Sie sich auch die Zeit nehmen, Ihre Funktionen richtig zu strukturieren: Halten Sie sie, wie wir empfehlen, kurz und klar.

Bringen Sie Ihnen bei, wie Sie kleine und übersichtliche JavaScript-Funktionen schreiben

ES2015 implementiert ein großartiges Modulsystem, das eindeutig empfiehlt, dass kleine Funktionen eine gute technische Praxis sind.

Denken Sie daran, dass sauberer, gut organisierter Code normalerweise einen erheblichen Zeitaufwand erfordert . Möglicherweise fällt Ihnen dies schwer. Es kann viele Versuche erfordern und Sie können eine Funktion viele Male iterieren und ändern.

Allerdings ist nichts schmerzhafter als unordentlicher Code, es lohnt sich also!

Das obige ist der detaillierte Inhalt vonBringen Sie Ihnen bei, wie Sie kleine und übersichtliche JavaScript-Funktionen schreiben. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage