├── .gitattributes
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.md linguist-documentation=false
2 | *.md linguist-language=JavaScript
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Ryan McDermott
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # 🛁 کدنویسی تمیز در جاوا اسکریپت
5 |
6 | ### Original Repository: [ryanmcdermott/clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript#table-of-contents)
7 |
8 |
9 | ## فهرست موضوعات
10 |
11 | 1. [مقدمه](#مقدمه)
12 | 2. [متغیرها](#متغیرها)
13 | 3. [توابع](#توابع)
14 | 4. [آبجکت ها و ساختارهای داده](#آبجکت-ها-و-ساختارهای-داده)
15 | 5. [کلاس ها](#کلاس-ها)
16 | 6. [SOLID](#solid)
17 | 7. [تست](#تست)
18 | 8. [همزمانی](#همزمانی)
19 | 9. [ارور هندلینگ](#ارور-هندلینگ)
20 | 10. [قالب بندی](#قالب-بندی)
21 | 11. [کامنت گذاری](#کامنت-گذاری)
22 | 12. [ترجمه](#ترجمه)
23 |
24 | ## مقدمه
25 |
26 | 
28 |
29 | در این مطلب، اصول مهندسی نرم افزار برای جاوا اسکریپت از کتاب
30 | [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)
31 | اثر رابرت سی مارتین برگرفته شده و به عنوان راهنمایی برای نگارش نیست، بلکه راهنمایی برای ایجاد کدهای
32 | [خوانا, قابل استفاده مجدد و قابل تغییر](https://github.com/ryanmcdermott/3rs-of-software-architecture) به زبان جاوا اسکریپت هست.
33 |
34 | لازم نیست که هر اصلی که گفته شده حتما رعایت شود و حتی تعداد کمتری از این اصول مورد توافق همه توسعه دهدگان قرار خواهند گرفت. این موارد چیزی جز راهنمای مسیر نیستند و حاصل چندین سال تجربه جمعی نویسندگان _Clean Code_ هستند
35 | .
36 |
37 | با اینکه صنعت مهندسی نرم افزار کمی بیش از 50 سال قدمت دارد ولی ما هنوز چیزهای زیادی برای یادگیری داریم. شاید با بالا رفتن قدمت معماری نرم افزار به اندازه خود معماری ، قوانین سخت تری برای پیروی از آن داشته باشیم. الان اجازه دهید این دستورالعمل ها به عنوان یک معیار مهم برای کیفیت سنجی کدی که شما و تیم تان با جاوا اسکریپت تولید می کنید، باشد.
38 |
39 | مورد مهمی که باید در نظر داشته باشید ، اینکه دانستن این موارد باعث نمیشه که دیگر هیچ خطایی نداشته باشید و باور کنید که توسعه دهنده قوی تری هستید . برای قوی تر شدن بهتر است که بیشتر کد بزنید و تمرین کنید و فقط روی یک قطعه کد ساده و پیش نویس کار نکنید ، برای مثال خاک رس مرطوب به تنهایی چیز خاصی نیست ولی اگر با فوت کوزه گری به آن حالت بدید و به دفعات کارتان را باز بینی کنید ، قطعا محصول نهایی جذاب تری تولید می کنید.
40 |
41 | ## **متغیرها**
42 |
43 | ### از نام گذاری معنادار و قابل تلفظ استفاده کنید
44 |
45 |
46 |
47 | **بد**
48 |
49 | ```javascript
50 | const yyyymmdstr = moment().format("YYYY/MM/DD");
51 | ```
52 |
53 | **خوب**
54 |
55 | ```javascript
56 | const currentDate = moment().format("YYYY/MM/DD");
57 | ```
58 |
59 |
60 |
61 | **[⬆ برو بالا](#فهرست-موضوعات)**
62 |
63 | ### از یک کلمه مشابه برای همان نوع متغیر استفاده کنید
64 |
65 |
66 |
67 | **بد**
68 |
69 | ```javascript
70 | getUserInfo();
71 | getClientData();
72 | getCustomerRecord();
73 | ```
74 |
75 | **خوب**
76 |
77 | ```javascript
78 | getUser();
79 | ```
80 |
81 |
82 |
83 | **[⬆ برو بالا](#فهرست-موضوعات)**
84 |
85 | ### از نام های قابل جستجو استفاده کنید
86 |
87 | ما بیشتر از اینکه کد می زنیم ، کدها را می خوانیم. خوانا بودن و قابل جستجو بودن کد ما اهمیت بالایی دارد .پس با نام گذاری بی معنی و غیر قابل فهم متغیرها، کسانی که کدمان را میخوانند را آزار ندهیم. از نام های قابل جستوجو استفاده کنیم و برای این منظور ابزارهایی مانند
88 | [buddy.js](https://github.com/danielstjules/buddy.js) و
89 | [ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md)
90 | می توانند به شناسایی constant های بدون نام کمک کنند.
91 |
92 |
93 |
94 | **بد**
95 |
96 | ```javascript
97 | // What the heck is 86400000 for?
98 | setTimeout(blastOff, 86400000);
99 | ```
100 |
101 | **خوب**
102 |
103 | ```javascript
104 | // Declare them as capitalized named constants.
105 | const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000;
106 |
107 | setTimeout(blastOff, MILLISECONDS_PER_DAY);
108 | ```
109 |
110 |
111 |
112 | **[⬆ برو بالا](#فهرست-موضوعات)**
113 |
114 | ### از متغیرهای توضیحی استفاده کنید
115 |
116 |
117 |
118 | **بد**
119 |
120 | ```javascript
121 | const address = "One Infinite Loop, Cupertino 95014";
122 | const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
123 | saveCityZipCode(
124 | address.match(cityZipCodeRegex)[1],
125 | address.match(cityZipCodeRegex)[2]
126 | );
127 | ```
128 |
129 | **خوب**
130 |
131 | ```javascript
132 | const address = "One Infinite Loop, Cupertino 95014";
133 | const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
134 | const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
135 | saveCityZipCode(city, zipCode);
136 | ```
137 |
138 |
139 |
140 | **[⬆ برو بالا](#فهرست-موضوعات)**
141 |
142 | ### از نگاشت ذهنی خودداری کنید
143 |
144 | متغیرهای صریح بهتر از متغیرهای ضمنی است.
145 |
146 |
147 |
148 | **بد**
149 |
150 | ```javascript
151 | const locations = ["Austin", "New York", "San Francisco"];
152 | locations.forEach(l => {
153 | doStuff();
154 | doSomeOtherStuff();
155 | // ...
156 | // ...
157 | // ...
158 | // Wait, what is `l` for again?
159 | dispatch(l);
160 | });
161 | ```
162 |
163 | **خوب**
164 |
165 | ```javascript
166 | const locations = ["Austin", "New York", "San Francisco"];
167 | locations.forEach(location => {
168 | doStuff();
169 | doSomeOtherStuff();
170 | // ...
171 | // ...
172 | // ...
173 | dispatch(location);
174 | });
175 | ```
176 |
177 |
178 |
179 | **[⬆ برو بالا](#فهرست-موضوعات)**
180 |
181 | ### نیاز به تکرار نام شی در متغیرها مرتبط به آن نیست
182 |
183 | اگر نامی برای کلاس یا آبجکت خودتان انتخاب کردید از آنها درنامگذاری متغیر ها استفاده نکنید.
184 |
185 |
186 |
187 | **بد**
188 |
189 | ```javascript
190 | const Car = {
191 | carMake: "Honda",
192 | carModel: "Accord",
193 | carColor: "Blue"
194 | };
195 |
196 | function paintCar(car, color) {
197 | car.carColor = color;
198 | }
199 | ```
200 |
201 | **خوب**
202 |
203 | ```javascript
204 | const Car = {
205 | make: "Honda",
206 | model: "Accord",
207 | color: "Blue"
208 | };
209 |
210 | function paintCar(car, color) {
211 | car.color = color;
212 | }
213 | ```
214 |
215 |
216 |
217 | **[⬆ برو بالا](#فهرست-موضوعات)**
218 |
219 | ### به جای اتصال کوتاه یا شرطی، از آرگومان های پیش فرض استفاده کنید
220 |
221 | آرگومان های پیش فرض اغلب از اتصال کوتاه تمیزترند . توجه داشته باشید که اگر از آنها استفاده کنید، تابع شما فقط مقادیر پیش فرض آرگومان های `undefined` را ارائه می دهد. سایر مقادیر
222 | "falsy" مثل `''`، `""`، `false`، `null`، `0`، و
223 | `NaN`، با مقدار پیش فرض جایگزین نخواهند شد.
224 |
225 |
226 |
227 | **بد**
228 |
229 | ```javascript
230 | function createMicrobrewery(name) {
231 | const breweryName = name || "Hipster Brew Co.";
232 | // ...
233 | }
234 | ```
235 |
236 | **خوب**
237 |
238 | ```javascript
239 | function createMicrobrewery(name = "Hipster Brew Co.") {
240 | // ...
241 | }
242 | ```
243 |
244 |
245 |
246 | **[⬆ برو بالا](#فهرست-موضوعات)**
247 |
248 | ## **توابع**
249 |
250 | ### آرگومان های تابع ( 2 تا یا کمتر در حالت ایده آل)
251 |
252 | محدود کردن تعداد آرگومان های یک تابع بسیار اهمیت دارد، چون تست کردن تابع را برایتان آسان تر می کند. داشتن بیش از سه مورد آرگومان باعث سخت تر شدن پروسه تست کدها می شود.
253 |
254 | استفاده از یک یا دو آرگومان ایده آل است و در صورت امکان ،در استفاده از سه آرگومان باید خودداری کرد و باید از ادغام کردن استفاده کرد. معمولاً، اگر بیش از دو آرگومان دارید، تابع شما سعی می کند کارهای زیادی را انجام دهد. در مواردی که اینطور نیست ، بیشتر اوقات یک آبجکت سطح بالاتر برای آرگومان کافی است.
255 |
256 | از آنجا که ، جاوا اسکریپت به شما این امکان را می دهد، بدون استفاده از کدهای اضافی که برای ساخت کلاس استفاده می شوند، به راحتی آبجکت بسازید. در صورت نیاز به آرگومان های زیاد در یک تابع می توانید از یک آبجکت استفاده کنید و همه آنها را داخل آن بگذارید.
257 |
258 | برای اینکه مشخص شود تابع مورد نظر چه ویژگی هایی را مدنظر دارد، می توانید از destructuring syntax در ES2015 / ES6 استفاده کنید، که چند مزیت اصلی دارد:
259 |
260 | 1. وقتی کسی به ساختار تابع نگاه می کند، فورا روشن می شود که چه property هایی درون تابع استفاده می شود.
261 | 2. می توان از آن برای شبیه سازی named parameters استفاده کرد.
262 | 3. Destructuring همچنین از primitive value های مشخص شده از آبجکت که به عنوان آرگومان به تابع پاس داده شده یک کپی ایجاد می کند. با کمک آن ها می توان از side effectsها جلوگیری کرد و نکته مهم اینکه ،آبجکت ها و آرایه هایی که از آبجکت درون آرگومان یک تابع destructured می شوند ، یک کپی به حساب نمی آیند.
263 | 4. Linter هایی مثل eslint در مورد property های استفاده نشده هشدار می دهند که بدون destructuring غیر ممکن است.
264 |
265 |
266 |
267 | **بد**
268 |
269 | ```javascript
270 | function createMenu(title, body, buttonText, cancellable) {
271 | // ...
272 | }
273 |
274 | createMenu("Foo", "Bar", "Baz", true);
275 |
276 | ```
277 |
278 | **خوب**
279 |
280 | ```javascript
281 | function createMenu({ title, body, buttonText, cancellable }) {
282 | // ...
283 | }
284 |
285 | createMenu({
286 | title: "Foo",
287 | body: "Bar",
288 | buttonText: "Baz",
289 | cancellable: true
290 | });
291 | ```
292 |
293 |
294 |
295 | **[⬆ برو بالا](#فهرست-موضوعات)**
296 |
297 | ### توابع باید یک کار را انجام دهند
298 |
299 | این قانون با فاصله زیاد با اهمیت ترین قانون در مهندسی نرم افزار است. وقتی توابع بیش از یک کار انجام می دهند، نوشتن، تست کردن و استدلالشان، سخت تر می شود. وقتی می توانید یک تابع را فقط برای انجام یک کار به صورت ایزوله بنویسید، می توان به راحتی آن را تغییر داد و کد شما بسیار تمیزتر خواهد شد. اگر از کل این راهنما فقط همین یک مورد را استفاده کنید شما از تعداد زیادی از توسعه دهندگان جلو خواهید زد.
300 |
301 |
302 |
303 | **بد**
304 |
305 | ```javascript
306 | function emailClients(clients) {
307 | clients.forEach(client => {
308 | const clientRecord = database.lookup(client);
309 | if (clientRecord.isActive()) {
310 | email(client);
311 | }
312 | });
313 | }
314 | ```
315 |
316 | **خوب**
317 |
318 | ```javascript
319 | function emailActiveClients(clients) {
320 | clients.filter(isActiveClient).forEach(email);
321 | }
322 |
323 | function isActiveClient(client) {
324 | const clientRecord = database.lookup(client);
325 | return clientRecord.isActive();
326 | }
327 | ```
328 |
329 |
330 |
331 | **[⬆ برو بالا](#فهرست-موضوعات)**
332 |
333 | ### نام تابع باید بگوید که چه کاری انجام می دهد
334 |
335 |
336 |
337 | **بد**
338 |
339 | ```javascript
340 | function addToDate(date, month) {
341 | // ...
342 | }
343 |
344 | const date = new Date();
345 |
346 | // It's hard to tell from the function name what is added
347 |
348 | addToDate(date, 1);
349 | ```
350 |
351 | **خوب**
352 |
353 | ```javascript
354 | function addMonthToDate(month, date) {
355 | // ...
356 | }
357 |
358 | const date = new Date();
359 | addMonthToDate(1, date);
360 | ```
361 |
362 |
363 |
364 | **[⬆ برو بالا](#فهرست-موضوعات)**
365 |
366 | ### توابع فقط باید در یک سطح abstraction باشند
367 |
368 | زمانی که بیش از یک سطح abstraction داشته باشید، معمولا از تابع شما کار زیادی کشیده می شود . تقسیم کردن توابع به قابل استفاده مجدد بودن و تست کردن آسان تر آن منجر می شود.
369 |
370 |
371 |
372 | **بد**
373 |
374 | ```javascript
375 | function parseBetterJSAlternative(code) {
376 | const REGEXES = [
377 | // ...
378 | ];
379 |
380 | const statements = code.split(" ");
381 | const tokens = [];
382 | REGEXES.forEach(REGEX => {
383 | statements.forEach(statement => {
384 | // ...
385 | });
386 | });
387 |
388 | const ast = [];
389 | tokens.forEach(token => {
390 | // lex...
391 | });
392 |
393 | ast.forEach(node => {
394 | // parse...
395 | });
396 | }
397 | ```
398 |
399 | **خوب**
400 |
401 | ```javascript
402 | function parseBetterJSAlternative(code) {
403 | const tokens = tokenize(code);
404 | const syntaxTree = parse(tokens);
405 | syntaxTree.forEach(node => {
406 | // parse...
407 | });
408 | }
409 |
410 | function tokenize(code) {
411 | const REGEXES = [
412 | // ...
413 | ];
414 |
415 | const statements = code.split(" ");
416 | const tokens = [];
417 | REGEXES.forEach(REGEX => {
418 | statements.forEach(statement => {
419 | tokens.push(/* ... */);
420 | });
421 | });
422 |
423 | return tokens;
424 | }
425 |
426 | function parse(tokens) {
427 | const syntaxTree = [];
428 | tokens.forEach(token => {
429 | syntaxTree.push(/* ... */);
430 | });
431 |
432 | return syntaxTree;
433 | }
434 | ```
435 |
436 |
437 |
438 | **[⬆ برو بالا](#فهرست-موضوعات)**
439 |
440 | ### کد تکراری را حذف کنید
441 |
442 | تمام سعی خود را بکنید تا از نوشتن کدهای تکراری خودداری کنید. وجود کد تکراری خوب نیست، چون به این معنی است که وقتی نیاز به تغییر منطق در برنامه باشد، نیاز به تغییر در بیش از یک نقطه از برنامه را داریم.
443 |
444 | تصور کنید در صورتی که یک رستوران را اداره می کنید و موجودی انبار خود از جمله تمام گوجه فرنگی ها ، پیاز ها ، سیر ، ادویه جات و … را پیگیری می کنید و چند لیست دارید که این کار را در آن انجام می دهید و باید همه ی این موجودی ها بعد از سرو یک غذا درست شده با گوجه فرنگی، به روز شوند. در صورتی که اگر یک لیست داشته باشید فقط و فقط یک جا برای آپدیت کردن هست و نه بیشتر.
445 |
446 | اغلب اوقات به این علت کدهای تکراری دارید که دو یا چند چیز با تفاوت خیلی جزئی دارید که اشتراکات فراوانی با هم دارند. ولی این تفاوت ها ، شما را مجبور می کند که دو یا چند تابع جداگانه با عملکرد مشابه بسازید. حذف کد تکراری به معنی ایجاد یک abstraction است که می تواند مجموعه ای از موارد مختلف را فقط با یک تابع یا ماژول یا کلاس مدیریت کند.
447 |
448 | درست گرفتن abstraction بسیار با اهمیت است، به همین دلیل باید از اصول SOLID مندرج در بخش Class ها پیروی کنید. abstraction های بد ممکن است از کد تکراری بدتر باشد، پس مراقب باشید! با در نظر گرفتن این گفته، اگر می توانید abstraction خوبی انجام دهید، آن را انجام دهید! کدتان را مجددا تکرار نکنید، در غیر این صورت هر زمان که بخواهید یک چیز را ویرایش کنید، باید بخش های مختلفی را تغییر دهید.
449 |
450 |
451 |
452 | **بد**
453 |
454 | ```javascript
455 | function showDeveloperList(developers) {
456 | developers.forEach(developer => {
457 | const expectedSalary = developer.calculateExpectedSalary();
458 | const experience = developer.getExperience();
459 | const githubLink = developer.getGithubLink();
460 | const data = {
461 | expectedSalary,
462 | experience,
463 | githubLink
464 | };
465 |
466 | render(data);
467 | });
468 | }
469 |
470 | function showManagerList(managers) {
471 | managers.forEach(manager => {
472 | const expectedSalary = manager.calculateExpectedSalary();
473 | const experience = manager.getExperience();
474 | const portfolio = manager.getMBAProjects();
475 | const data = {
476 | expectedSalary,
477 | experience,
478 | portfolio
479 | };
480 |
481 | render(data);
482 | });
483 | }
484 | ```
485 |
486 | **خوب**
487 |
488 | ```javascript
489 | function showEmployeeList(employees) {
490 | employees.forEach(employee => {
491 | const expectedSalary = employee.calculateExpectedSalary();
492 | const experience = employee.getExperience();
493 |
494 | const data = {
495 | expectedSalary,
496 | experience
497 | };
498 |
499 | switch (employee.type) {
500 | case "manager":
501 | data.portfolio = employee.getMBAProjects();
502 | break;
503 | case "developer":
504 | data.githubLink = employee.getGithubLink();
505 | break;
506 | }
507 |
508 | render(data);
509 | });
510 | }
511 | ```
512 |
513 |
514 |
515 | **[⬆ برو بالا](#فهرست-موضوعات)**
516 |
517 | ### آبجکت های پیش فرض را با Object.assign تنظیم کنید
518 |
519 |
520 |
521 | **بد**
522 |
523 | ```javascript
524 | const menuConfig = {
525 | title: null,
526 | body: "Bar",
527 | buttonText: null,
528 | cancellable: true
529 | };
530 |
531 | function createMenu(config) {
532 | config.title = config.title || "Foo";
533 | config.body = config.body || "Bar";
534 | config.buttonText = config.buttonText || "Baz";
535 | config.cancellable =
536 | config.cancellable !== undefined ? config.cancellable : true;
537 | }
538 |
539 | createMenu(menuConfig);
540 | ```
541 |
542 | **خوب**
543 |
544 | ```javascript
545 | const menuConfig = {
546 | title: "Order",
547 | // User did not include 'body' key
548 | buttonText: "Send",
549 | cancellable: true
550 | };
551 |
552 | function createMenu(config) {
553 | let finalConfig = Object.assign(
554 | {
555 | title: "Foo",
556 | body: "Bar",
557 | buttonText: "Baz",
558 | cancellable: true
559 | },
560 | config
561 | );
562 | return finalConfig
563 | // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
564 | // ...
565 | }
566 |
567 | createMenu(menuConfig);
568 | ```
569 |
570 |
571 |
572 | **[⬆ برو بالا](#فهرست-موضوعات)**
573 |
574 | ### از flagها به عنوان پارامترهای تابع استفاده نکنید
575 |
576 | flagها به کاربر شما می گویند که این تابع بیش از یک کار انجام می دهد. توابع باید یک کار انجام دهند. اگر توابع شما براساس مسیرهای وابسته به boolean هستند، توابع خود را به صورت جدا از هم بنویسید .
577 |
578 |
579 |
580 | **بد**
581 |
582 | ```javascript
583 | function createFile(name, temp) {
584 | if (temp) {
585 | fs.create(`./temp/${name}`);
586 | } else {
587 | fs.create(name);
588 | }
589 | }
590 | ```
591 |
592 | **خوب**
593 |
594 | ```javascript
595 | function createFile(name) {
596 | fs.create(name);
597 | }
598 |
599 | function createTempFile(name) {
600 | createFile(`./temp/${name}`);
601 | }
602 | ```
603 |
604 |
605 |
606 | **[⬆ برو بالا](#فهرست-موضوعات)**
607 |
608 | ### از Side Effect ها بپرهیزید (بخش 1)
609 |
610 | یک تابع در صورتی side effect تولید می کند که کاری به غیر از گرفتن یک مقدار و برگرداندن یک مقدار یا مقادیر دیگر نکند.
611 | یک side effect می تواند برای یک فایل نوشته شود
612 | یا برای اصلاح چند متغیر global باشد یا به طرز عجیبی یک مسیر از تمام پول های داخل حسابتان به یک شخص غریبه ایجاد کند.
613 |
614 | حالا اگر تحت یک موقعیت که مجبور به داشتن side effect ها در یک برنامه
615 | بودید مثل مثال قبل که برای یک فایل نوشته بودید، کاری که نیاز هست انجام بدید این هست که به جایی که در آن هستید متمرکز شوید.
616 | از کلاس ها و توابع متعدد در نوشتن یک فایل استفاده نکنید و فقط و فقط یک سرویس بنویسید که این کار را انجام دهد.
617 |
618 | نکته اصلی اینجاست که باید از اشتراک گذاری state بین آبجکت های بدون ساختار و استفاده از data type های قابل تغییر که می توانند توسط هر چیزی نوشته شوند و همچنین متمرکز نبودن در جایی که side effect ها رخ می دهند خودداری کرد. اگر شما بتوانید این کارها را انجام بدید، نسبت به خیلی از برنامه نویسان خوشحال تر خواهید بود.
619 |
620 |
621 |
622 | **بد**
623 |
624 | ```javascript
625 | // متغیر global که توسط این تابع ارجاع داده شده
626 | // اگر ما یک تابع دیگر داشته باشیم که از این نام استفاده کند، این نام تبدیل به یک آرایه خواهد شد و می تواند کد ما را break کند
627 |
628 | let name = "Ryan McDermott";
629 |
630 | function splitIntoFirstAndLastName() {
631 | name = name.split(" ");
632 | }
633 |
634 | splitIntoFirstAndLastName();
635 |
636 | console.log(name); // ['Ryan', 'McDermott'];
637 | ```
638 |
639 | **خوب**
640 |
641 | ```javascript
642 | function splitIntoFirstAndLastName(name) {
643 | return name.split(" ");
644 | }
645 |
646 | const name = "Ryan McDermott";
647 | const newName = splitIntoFirstAndLastName(name);
648 |
649 | console.log(name); // 'Ryan McDermott';
650 | console.log(newName); // ['Ryan', 'McDermott'];
651 | ```
652 |
653 |
654 |
655 | **[⬆ برو بالا](#فهرست-موضوعات)**
656 |
657 | ### از Side Effect ها بپرهیزید (بخش 2)
658 |
659 | در جاوااسکریپت ، بعضی مقادیر قابل تغییر هستند و بعضی ها هم غیر قابل تغییرند. آبجکت ها و آرایه ها، 2 نوع قابل تغییر هستند و با احتیاط مدیریت کردن آنها در زمان پاس دادن به عنوان آرگومان به یک تابع بسیار با اهمیت هست. یک تابع جاوااسکریپت می تواند property های یک آبجکت یا محتویات یک آرایه را تغییر دهد که این موضوع باعث ایجاد باگ هایی در جاهای دیگر می شود.
660 |
661 | فرض کنید که یک تابع داریم که با گرفتن یک آرایه به عنوان آرگومان یک سبد خرید ایجاد می کند، برای مثال اگر تابع با اضافه کردن یک آیتم جدید برای خرید در آرایه سبد خرید تغییر کند، هر تابع دیگری که از همین آرایه `cart` استفاده می کند، تغییر می کند.این تغییر همانطور که می تواند خوب باشد به همان اندازه می تواند بد باشد. تصور کنید که در موقعیت بد قرار داریم:
662 |
663 | کاربر روی دکمه "Purchase" می زند که تابع `purchase` را که یک درخواست به شبکه را ایجاد می کند و آرایه `cart` را به سرور می فرستد فراخوانی می کند. بدلیل یک درخواست شبکه بد، تابع `purchase` مجبور به تلاش مجدد برای ارسال درخواست می شود.حالا اگر کاربر در همین حین لحظه روی دکمه "Add to cart" به طور اتفاقی برای محصولی که اصلا نمی خواهد قبل از اینکه درخواست شبکه شروع به ارسال کند بزند ، چه اتفاقی می افتد؟ اگر این اتفاق بیافتد و درخواست شبکه شروع شود، آن وقت تابع خرید به صورت تصادفی آیتم اضافه شده را می فرستد ، چون آرایه `cart` تغییر کرده است.
664 |
665 | یک راه حل عالی می تواند تابع `addItemToCart` برای کپی کردن `cart`
666 | و ویرایش آ ن و در نهایت بازگرداندن آن کپی باشد. این کار تضمین می کند که توابعی که هنوز از سبد خرید قدیمی استفاده می کنند ، تغییرات در آن ها اعمال نمی شود.
667 |
668 | دو هشدار درمورد این نگرش:
669 |
670 | 1. شاید مواردی باشد که شما واقعا بخواهید که آبجکت ورودی را تغییر بدهید، ولی وقتی شما طبق این نگرش برنامه نویسی پیش می روید آن موارد را خیلی کمیاب تلقی می کنید. در اکثر موارد می توان تغییراتی در آنها ایجاد کرد که هیچ side effect نداشته باشند!
671 |
672 | 2. کپی کردن آبجکت های بزرگ می تواند در performance برای ما گران تمام شوند، خوشبختانه در عمل این مشکل بزرگی نیست و [کتابخانه های خوبی](https://facebook.github.io/immutable-js/) وجود دارند که به شما اجازه می دهند این نگرش برای شما سریع و با مصرف مموری کمتر و امکان کپی کردن دستی آبجکت ها و آرایه ها را فراهم می کند.
673 |
674 |
675 |
676 | **بد**
677 |
678 | ```javascript
679 | const addItemToCart = (cart, item) => {
680 | cart.push({ item, date: Date.now() });
681 | };
682 | ```
683 |
684 | **خوب**
685 |
686 | ```javascript
687 | const addItemToCart = (cart, item) => {
688 | return [...cart, { item, date: Date.now() }];
689 | };
690 | ```
691 |
692 |
693 |
694 | **[⬆ برو بالا](#فهرست-موضوعات)**
695 |
696 | ### توابع global ننویسید
697 |
698 | شلوغکاری با توابع global کار خوبی در جاوااسکریپت نیست چون امکان تقابل با یک کتابخانه دیگر هست و کاربر استفاده کننده از API شما را تا زمان دریافت یک استثنا در production کلافه می کند.بیاید با هم یک مثال را بررسی کنیم:
699 |
700 | چی می شود اگر بخواهیم متد آرایه های native در جاوااسکریپت را با یک متد `diff` گسترش بدهیم که می تواند اختلاف بین 2 آرایه را نشان دهد؟
701 | شما می توانید یک تابع جدید با `Array.prototype` ایجاد کنید ولی با یک کتابخانه دیگر که سعی می کند کار مشابه ای را انجام دهد تداخل می کند.چه می شود اگر آن کتابخانه فقط از `diff` برای پیدا کردن اختلاف بین المان های اول و آخر آرایه استفاده کند؟ به همین خاطر هست که بهتر هست که از کلاس در ES2015/ES6 استفاده کنیم و به آسانی `Array` global را گستربش بدیم.
702 |
703 |
704 |
705 | **بد**
706 |
707 | ```javascript
708 | Array.prototype.diff = function diff(comparisonArray) {
709 | const hash = new Set(comparisonArray);
710 | return this.filter(elem => !hash.has(elem));
711 | };
712 | ```
713 |
714 | **خوب**
715 |
716 | ```javascript
717 | class SuperArray extends Array {
718 | diff(comparisonArray) {
719 | const hash = new Set(comparisonArray);
720 | return this.filter(elem => !hash.has(elem));
721 | }
722 | }
723 | ```
724 |
725 |
726 |
727 | **[⬆ برو بالا](#فهرست-موضوعات)**
728 |
729 | ### برنامه نویسی functional را به برنامه نویسی imperative ترجیح دهید
730 |
731 | جاوااسکریپت در مقایسه با زبان برنامه نویسی haskell یک زبان functional به حساب نمی آید ولی خیلی تمایل به functional بودن دارد.
732 | زبان های functional می توانند بسیار تمیزتر و آسانتر برای تست کردن باشند. اگر می توانید دوستدار این استایل برنامه نویسی باشید.
733 |
734 |
735 |
736 | **بد**
737 |
738 | ```javascript
739 | const programmerOutput = [
740 | {
741 | name: "Uncle Bobby",
742 | linesOfCode: 500
743 | },
744 | {
745 | name: "Suzie Q",
746 | linesOfCode: 1500
747 | },
748 | {
749 | name: "Jimmy Gosling",
750 | linesOfCode: 150
751 | },
752 | {
753 | name: "Gracie Hopper",
754 | linesOfCode: 1000
755 | }
756 | ];
757 |
758 | let totalOutput = 0;
759 |
760 | for (let i = 0; i < programmerOutput.length; i++) {
761 | totalOutput += programmerOutput[i].linesOfCode;
762 | }
763 | ```
764 |
765 | **خوب**
766 |
767 | ```javascript
768 | const programmerOutput = [
769 | {
770 | name: "Uncle Bobby",
771 | linesOfCode: 500
772 | },
773 | {
774 | name: "Suzie Q",
775 | linesOfCode: 1500
776 | },
777 | {
778 | name: "Jimmy Gosling",
779 | linesOfCode: 150
780 | },
781 | {
782 | name: "Gracie Hopper",
783 | linesOfCode: 1000
784 | }
785 | ];
786 |
787 | const totalOutput = programmerOutput.reduce(
788 | (totalLines, output) => totalLines + output.linesOfCode,
789 | 0
790 | );
791 | ```
792 |
793 |
794 |
795 | **[⬆ برو بالا](#فهرست-موضوعات)**
796 |
797 | ### دستورات شرطی را کپسوله سازی کنید
798 |
799 |
800 |
801 | **بد**
802 |
803 | ```javascript
804 | if (fsm.state === "fetching" && isEmpty(listNode)) {
805 | // ...
806 | }
807 | ```
808 |
809 | **خوب**
810 |
811 | ```javascript
812 | function shouldShowSpinner(fsm, listNode) {
813 | return fsm.state === "fetching" && isEmpty(listNode);
814 | }
815 |
816 | if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
817 | // ...
818 | }
819 | ```
820 |
821 |
822 |
823 | **[⬆ برو بالا](#فهرست-موضوعات)**
824 |
825 | ### از دستورات شرطی منفی استفاده نکنید
826 |
827 |
828 |
829 | **بد**
830 |
831 | ```javascript
832 | function isDOMNodeNotPresent(node) {
833 | // ...
834 | }
835 |
836 | if (!isDOMNodeNotPresent(node)) {
837 | // ...
838 | }
839 | ```
840 |
841 | **خوب**
842 |
843 | ```javascript
844 | function isDOMNodePresent(node) {
845 | // ...
846 | }
847 |
848 | if (isDOMNodePresent(node)) {
849 | // ...
850 | }
851 | ```
852 |
853 |
854 |
855 | **[⬆ برو بالا](#فهرست-موضوعات)**
856 |
857 | ### از شرطی شدن اجتناب کنید
858 |
859 | کار غیر ممکنی به نظر می رسد.در حالی که اولین بار که این را می شنویم ، بیشتر مردم می گویند : "من چطور باید با دستور شرطی `if` کار کنم؟" در جواب باید گفت شما می توانید از polymorphism در اکثر موارد برای انجام این کار استفاده کنید؟ سوال دوم معمولا این است که "این که عالیه ولی من چرا باید بخواهم که با آن کار کنم؟" جواب در مفهوم کد تمیزی هست که اخیرا یاد گرفتیم : اینکه یک تابع باید فقط یک کار انجام دهد وقتی شما کلاس ها یا توابعی که `if` دارند را دارید، به کاربرتان می گویید که تابع شما بیش از یک کار انجام می دهد. فراموش نکنید که با تابع فقط یک کار انجام دهید.
860 |
861 |
862 |
863 | **بد**
864 |
865 | ```javascript
866 | class Airplane {
867 | // ...
868 | getCruisingAltitude() {
869 | switch (this.type) {
870 | case "777":
871 | return this.getMaxAltitude() - this.getPassengerCount();
872 | case "Air Force One":
873 | return this.getMaxAltitude();
874 | case "Cessna":
875 | return this.getMaxAltitude() - this.getFuelExpenditure();
876 | }
877 | }
878 | }
879 | ```
880 |
881 | **خوب**
882 |
883 | ```javascript
884 | class Airplane {
885 | // ...
886 | }
887 |
888 | class Boeing777 extends Airplane {
889 | // ...
890 | getCruisingAltitude() {
891 | return this.getMaxAltitude() - this.getPassengerCount();
892 | }
893 | }
894 |
895 | class AirForceOne extends Airplane {
896 | // ...
897 | getCruisingAltitude() {
898 | return this.getMaxAltitude();
899 | }
900 | }
901 |
902 | class Cessna extends Airplane {
903 | // ...
904 | getCruisingAltitude() {
905 | return this.getMaxAltitude() - this.getFuelExpenditure();
906 | }
907 | }
908 | ```
909 |
910 |
911 |
912 | **[⬆ برو بالا](#فهرست-موضوعات)**
913 |
914 | ### از type-checking خودداری کنید (بخش 1)
915 | جاوااسکریپت، زیان برنامه نویسی است که در type نداریم، به این معنی که تابع شما می تواند هر نوع داده ای را به عنوان آرگومان بگیرد. گاهی اوقات شما از این آزادی ضربه می خورید و مایلید که از type-checking در تابع خود استفاده کنید. راه های زیادی برای خودداری از این کار هست که اولینش API های سازگار هستند.
916 |
917 |
918 |
919 | **بد**
920 |
921 | ```javascript
922 | function travelToTexas(vehicle) {
923 | if (vehicle instanceof Bicycle) {
924 | vehicle.pedal(this.currentLocation, new Location("texas"));
925 | } else if (vehicle instanceof Car) {
926 | vehicle.drive(this.currentLocation, new Location("texas"));
927 | }
928 | }
929 | ```
930 |
931 | **خوب**
932 |
933 | ```javascript
934 | function travelToTexas(vehicle) {
935 | vehicle.move(this.currentLocation, new Location("texas"));
936 | }
937 | ```
938 |
939 |
940 |
941 | **[⬆ برو بالا](#فهرست-موضوعات)**
942 |
943 | ### از type-checking خودداری کنید (بخش 2)
944 |
945 | اگر شما با مقادیر primitive پایه مثل string ها و integer ها و شما نمی توانیداز polymorphism استفاده کنید و نیاز به type-checking را احساس می کنید، باید از تایپ اسکریپت استفاده کنید. یک جایگزین عالی برای جاوااسکریپت معمولی است که برای شما typing را به صورت پیش فرض بر اساس سینتکس جاوااسکریپت اصلی مهیا کرده است. مشکلی که type-checking دستی در جاوااسکریپت دارد این هست که برای انجام درست آن به توابع و کدهای اضافی احتیاج دارد به طوری که type-safety ساختگی شما خوانایی از دست رفته را جبران نمی کند. کدهای جاوا اسکریپت خود را تمیز نگه دارید، تست های خوبی بنویسید و به خوبی بازنگری کنید. در غیر این صورت با TypeScript که یک گزینه ی خوب می باشد همه ی این کارها را انجام دهید.
946 |
947 |
948 |
949 | **بد**
950 |
951 | ```javascript
952 | function combine(val1, val2) {
953 | if (
954 | (typeof val1 === "number" && typeof val2 === "number") ||
955 | (typeof val1 === "string" && typeof val2 === "string")
956 | ) {
957 | return val1 + val2;
958 | }
959 |
960 | throw new Error("Must be of type String or Number");
961 | }
962 | ```
963 |
964 | **خوب**
965 |
966 | ```javascript
967 | function combine(val1, val2) {
968 | return val1 + val2;
969 | }
970 | ```
971 |
972 |
973 |
974 | **[⬆ برو بالا](#فهرست-موضوعات)**
975 |
976 | ### بیش از حد بهینه سازی نکنید
977 |
978 | مرورگرهای مدرن بهینه سازی زیادی در بطن خود دارند که در زمان اجرا انجام می دهند. بیشتر اوقات ، بهینه سازی شما چیزی جز اتلاف وقت نیست. [منابع خوبی](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers)
979 | برای بهینه سازی در وجود دارد که از آنها در زمان درست استفاده کنید
980 |
981 |
982 |
983 | **بد**
984 |
985 | ```javascript
986 | // On old browsers, each iteration with uncached `list.length` would be costly
987 | // because of `list.length` recomputation. In modern browsers, this is optimized.
988 | for (let i = 0, len = list.length; i < len; i++) {
989 | // ...
990 | }
991 | ```
992 |
993 | **خوب**
994 |
995 | ```javascript
996 | for (let i = 0; i < list.length; i++) {
997 | // ...
998 | }
999 | ```
1000 |
1001 |
1002 |
1003 | **[⬆ برو بالا](#فهرست-موضوعات)**
1004 |
1005 | ### کد مرده را حذف کنید
1006 |
1007 | کد مرده به اندازه کد تکراری بد است. دلیلی برای نگهداری آن در کدهایتان ندارید. اگر فراخوانی نمی شود، از شر آن خلاص شوید! کماکان در تارخچه ورژن های شما وجود خواهد داشت و اگر نیاز داشتید براحتی به آن دسترسی خواهید داشت.
1008 |
1009 |
1010 |
1011 | **بد**
1012 |
1013 | ```javascript
1014 | function oldRequestModule(url) {
1015 | // ...
1016 | }
1017 |
1018 | function newRequestModule(url) {
1019 | // ...
1020 | }
1021 |
1022 | const req = newRequestModule;
1023 | inventoryTracker("apples", req, "www.inventory-awesome.io");
1024 | ```
1025 |
1026 | **خوب**
1027 |
1028 | ```javascript
1029 | function newRequestModule(url) {
1030 | // ...
1031 | }
1032 |
1033 | const req = newRequestModule;
1034 | inventoryTracker("apples", req, "www.inventory-awesome.io");
1035 | ```
1036 |
1037 |
1038 |
1039 | **[⬆ برو بالا](#فهرست-موضوعات)**
1040 |
1041 | ## **آبجکت ها و ساختارهای داده**
1042 |
1043 | ### از getters و setters استفاده کنید
1044 |
1045 | استفاده از getters و setters برای دسترسی به داده های داخل آبجکت می تواند بهتر از دسترسی مستقیم به اعضای آن آبجکت باشد. شاید بپرسید چرا؟ در اینجا به لیستی از دلایل اشاره می کنیم:
1046 |
1047 | - زمانی که می خواهید کارهایی فراتر از به دست آوردن یک property در آبجکت را دارید، نیازی نیست به جستجو و تغییر دسترسی نیست.
1048 |
1049 | - ساده سازی اضافه کردن اعتبارسنجی در زمان `set` کردن.
1050 | - ارائه های داخلی را کپسوله سازی می کند.
1051 | - به آسانی error handling و logging اضافه کنید.
1052 | - در زمان دریافت اطلاعات از سرور می توانید به property های آبجکت خود ویژگی lazy loading اضافه کنید
1053 |
1054 |
1055 |
1056 | **بد**
1057 |
1058 | ```javascript
1059 | function makeBankAccount() {
1060 | // ...
1061 |
1062 | return {
1063 | balance: 0
1064 | // ...
1065 | };
1066 | }
1067 |
1068 | const account = makeBankAccount();
1069 | account.balance = 100;
1070 | ```
1071 |
1072 | **خوب**
1073 |
1074 | ```javascript
1075 | function makeBankAccount() {
1076 | // this one is private
1077 | let balance = 0;
1078 |
1079 | // a "getter", made public via the returned object below
1080 | function getBalance() {
1081 | return balance;
1082 | }
1083 |
1084 | // a "setter", made public via the returned object below
1085 | function setBalance(amount) {
1086 | // ... validate before updating the balance
1087 | balance = amount;
1088 | }
1089 |
1090 | return {
1091 | // ...
1092 | getBalance,
1093 | setBalance
1094 | };
1095 | }
1096 |
1097 | const account = makeBankAccount();
1098 | account.setBalance(100);
1099 | ```
1100 |
1101 |
1102 |
1103 | **[⬆ برو بالا](#فهرست-موضوعات)**
1104 |
1105 | ### در آبجکت هایتان اعضای private بسازید
1106 |
1107 | این ویژگی را می توان از طریق closure ها (برای ES5 و نسخه های پایین تر) ایجاد کرد.
1108 |
1109 |
1110 |
1111 | **بد**
1112 |
1113 | ```javascript
1114 | const Employee = function(name) {
1115 | this.name = name;
1116 | };
1117 |
1118 | Employee.prototype.getName = function getName() {
1119 | return this.name;
1120 | };
1121 |
1122 | const employee = new Employee("John Doe");
1123 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
1124 | delete employee.name;
1125 | console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
1126 | ```
1127 |
1128 | **خوب**
1129 |
1130 | ```javascript
1131 | function makeEmployee(name) {
1132 | return {
1133 | getName() {
1134 | return name;
1135 | }
1136 | };
1137 | }
1138 |
1139 | const employee = makeEmployee("John Doe");
1140 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
1141 | delete employee.name;
1142 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
1143 | ```
1144 |
1145 |
1146 |
1147 | **[⬆ برو بالا](#فهرست-موضوعات)**
1148 |
1149 | ## **کلاس ها**
1150 |
1151 | ### کلاس های ES2015 / ES6 را به توابع ساده ES5 ترجیح دهید
1152 |
1153 | گرفتن ارث بری خوانا ، ساختار و تعاریف متدها با استفاده از کلاس های ES5 سخت هست .اگر به ارث بری نیاز دارید(توجه داشته باشید که ممکن هست اصلا نیاز نداشته باشید) از کلاس های ES6 استفاده کنید. توابع کوچک را به کلاس ترجیح دهید، اما زمانی که احساس کردید به آبجکت های بزرگتر و پیچیده نیاز دارید از کلاس استفاده کنید.
1154 |
1155 |
1156 |
1157 | **بد**
1158 |
1159 | ```javascript
1160 | const Animal = function(age) {
1161 | if (!(this instanceof Animal)) {
1162 | throw new Error("Instantiate Animal with `new`");
1163 | }
1164 |
1165 | this.age = age;
1166 | };
1167 |
1168 | Animal.prototype.move = function move() {};
1169 |
1170 | const Mammal = function(age, furColor) {
1171 | if (!(this instanceof Mammal)) {
1172 | throw new Error("Instantiate Mammal with `new`");
1173 | }
1174 |
1175 | Animal.call(this, age);
1176 | this.furColor = furColor;
1177 | };
1178 |
1179 | Mammal.prototype = Object.create(Animal.prototype);
1180 | Mammal.prototype.constructor = Mammal;
1181 | Mammal.prototype.liveBirth = function liveBirth() {};
1182 |
1183 | const Human = function(age, furColor, languageSpoken) {
1184 | if (!(this instanceof Human)) {
1185 | throw new Error("Instantiate Human with `new`");
1186 | }
1187 |
1188 | Mammal.call(this, age, furColor);
1189 | this.languageSpoken = languageSpoken;
1190 | };
1191 |
1192 | Human.prototype = Object.create(Mammal.prototype);
1193 | Human.prototype.constructor = Human;
1194 | Human.prototype.speak = function speak() {};
1195 | ```
1196 |
1197 | **خوب**
1198 |
1199 | ```javascript
1200 | class Animal {
1201 | constructor(age) {
1202 | this.age = age;
1203 | }
1204 |
1205 | move() {
1206 | /* ... */
1207 | }
1208 | }
1209 |
1210 | class Mammal extends Animal {
1211 | constructor(age, furColor) {
1212 | super(age);
1213 | this.furColor = furColor;
1214 | }
1215 |
1216 | liveBirth() {
1217 | /* ... */
1218 | }
1219 | }
1220 |
1221 | class Human extends Mammal {
1222 | constructor(age, furColor, languageSpoken) {
1223 | super(age, furColor);
1224 | this.languageSpoken = languageSpoken;
1225 | }
1226 |
1227 | speak() {
1228 | /* ... */
1229 | }
1230 | }
1231 | ```
1232 |
1233 |
1234 |
1235 | **[⬆ برو بالا](#فهرست-موضوعات)**
1236 |
1237 | ### از chain کردن متد ها استفاده کنید
1238 |
1239 | این الگو در جاوااسکریپت بسیار مفید هست و در بسیاری از کتابخانه ها مثل jQuery و Lodash دیده می شود.این کار به شما اجازه می دهد تا کد خوانا با حجم کمتری داشته باشید. به این دلیل است که می گوییم، از chain کردن توابع استفاده کنید و و دریابید که کد های شما چقدر تمیز خواهند بود. در توابع کلاس خود به راحتی دستور `this` را می توانید پایان هر تابع بگذارید و متدهای کلاس بیشتری را می توانید chain کنید.
1240 |
1241 |
1242 |
1243 | **بد**
1244 |
1245 | ```javascript
1246 | class Car {
1247 | constructor(make, model, color) {
1248 | this.make = make;
1249 | this.model = model;
1250 | this.color = color;
1251 | }
1252 |
1253 | setMake(make) {
1254 | this.make = make;
1255 | }
1256 |
1257 | setModel(model) {
1258 | this.model = model;
1259 | }
1260 |
1261 | setColor(color) {
1262 | this.color = color;
1263 | }
1264 |
1265 | save() {
1266 | console.log(this.make, this.model, this.color);
1267 | }
1268 | }
1269 |
1270 | const car = new Car("Ford", "F-150", "red");
1271 | car.setColor("pink");
1272 | car.save();
1273 | ```
1274 |
1275 | **خوب**
1276 |
1277 | ```javascript
1278 | class Car {
1279 | constructor(make, model, color) {
1280 | this.make = make;
1281 | this.model = model;
1282 | this.color = color;
1283 | }
1284 |
1285 | setMake(make) {
1286 | this.make = make;
1287 | // NOTE: Returning this for chaining
1288 | return this;
1289 | }
1290 |
1291 | setModel(model) {
1292 | this.model = model;
1293 | // NOTE: Returning this for chaining
1294 | return this;
1295 | }
1296 |
1297 | setColor(color) {
1298 | this.color = color;
1299 | // NOTE: Returning this for chaining
1300 | return this;
1301 | }
1302 |
1303 | save() {
1304 | console.log(this.make, this.model, this.color);
1305 | // NOTE: Returning this for chaining
1306 | return this;
1307 | }
1308 | }
1309 |
1310 | const car = new Car("Ford", "F-150", "red").setColor("pink").save();
1311 | ```
1312 |
1313 |
1314 |
1315 | **[⬆ برو بالا](#فهرست-موضوعات)**
1316 |
1317 | ### ترکیب کردن را بر ارث بری ترجیح دهید
1318 |
1319 | همانطور که در [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) توسط Gang of Four مطرح شد، در صورت امکان باید ترکیب کردن را به ارث بری ترجیح دهید. دلایل زیاد خوبی هم برای استفاده از ارث بری و هم برای ترکیب کردن وجود دارد . دلیل اصلی برای این کار این هست که اگر ذهنتان به طور غریزی به سمت ارث بری رفت ، به این فکر کنید که آیا ترکیب کردن می تواند بهتر مشکل شما را مدل سازی کند.در برخی موارد این کار شدنی است.
1320 |
1321 | شاید به این فکر بیافتید که "کی باید از ارث بری استفاده کنم؟" این سوال به این بستگی دارد که با چه مشکلی در حال حاضر روبرو هستید .
1322 | در اینجا به لیست خوبی از موقعیت هایی اشاره شده که در آن ارث بری نسبت به ترکیب کردن معقولانه تر است:
1323 |
1324 |
1325 | 1. ارث بری شما به یک رابطه ی "is-a" اشاره دارد و نه یک رابطه ی "has-a" (انسان->حیوان vs. کاربر->جزئیات کاربر)
1326 |
1327 | 2. می وانید از کدها متعلق به کلاس های اصلی استفاده کنید (انسان می تواند مثل همه حیوانات حرکت کند).
1328 |
1329 | 3. زمانی که می خواهید تغییرات کلی در کلاس های وابسته از طریق تغییر در کلاس اصلی ایجاد کنید. (مصرف کالری همه حیوانات را زمانی که حرکت می کنند تغییر دهید).
1330 |
1331 |
1332 |
1333 | **بد**
1334 |
1335 | ```javascript
1336 | class Employee {
1337 | constructor(name, email) {
1338 | this.name = name;
1339 | this.email = email;
1340 | }
1341 |
1342 | // ...
1343 | }
1344 |
1345 | // Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
1346 | class EmployeeTaxData extends Employee {
1347 | constructor(ssn, salary) {
1348 | super();
1349 | this.ssn = ssn;
1350 | this.salary = salary;
1351 | }
1352 |
1353 | // ...
1354 | }
1355 | ```
1356 |
1357 | **خوب**
1358 |
1359 | ```javascript
1360 | class EmployeeTaxData {
1361 | constructor(ssn, salary) {
1362 | this.ssn = ssn;
1363 | this.salary = salary;
1364 | }
1365 |
1366 | // ...
1367 | }
1368 |
1369 | class Employee {
1370 | constructor(name, email) {
1371 | this.name = name;
1372 | this.email = email;
1373 | }
1374 |
1375 | setTaxData(ssn, salary) {
1376 | this.taxData = new EmployeeTaxData(ssn, salary);
1377 | }
1378 | // ...
1379 | }
1380 | ```
1381 |
1382 |
1383 |
1384 | **[⬆ برو بالا](#فهرست-موضوعات)**
1385 |
1386 | ## **SOLID**
1387 |
1388 | ### اصل تک مسئولیتی یا (SRP)
1389 |
1390 | همانطور که در Clean Code مطرح شد، "هرگز نباید بیش از یک دلیل برای یک کلاس در تغییر کردن باشد". پر کردن یک کلاس با تعداد زیادی از عملکرد وسوسه بر انگیز هست، مثل زمانی که شما فقط یک چمدان برای پرواز با هواپیما برمیدارد. مشکل این موضوع، این هست که کلاس شما مفهوم منسجمی نخواهد داشت و دلایل زیادی برای تغییر ایجاد میکند به حداقل رساندن تعداد دفعات تغییر دادن یک کلاس بسیار با اهمیت .است. با اهمیت به این دلیل که اگر عملکرد های بسیار زیادی در یک کلاس باشد و شما یک قسمت کوچک آن را تغییر دهید، فهمیدن اینکه این تغییر چطور بر سایر اجزای مستقل در کد شما تاثیر می گذارد سخت می شود.
1391 |
1392 |
1393 |
1394 | **بد**
1395 |
1396 | ```javascript
1397 | class UserSettings {
1398 | constructor(user) {
1399 | this.user = user;
1400 | }
1401 |
1402 | changeSettings(settings) {
1403 | if (this.verifyCredentials()) {
1404 | // ...
1405 | }
1406 | }
1407 |
1408 | verifyCredentials() {
1409 | // ...
1410 | }
1411 | }
1412 | ```
1413 |
1414 | **خوب**
1415 |
1416 | ```javascript
1417 | class UserAuth {
1418 | constructor(user) {
1419 | this.user = user;
1420 | }
1421 |
1422 | verifyCredentials() {
1423 | // ...
1424 | }
1425 | }
1426 |
1427 | class UserSettings {
1428 | constructor(user) {
1429 | this.user = user;
1430 | this.auth = new UserAuth(user);
1431 | }
1432 |
1433 | changeSettings(settings) {
1434 | if (this.auth.verifyCredentials()) {
1435 | // ...
1436 | }
1437 | }
1438 | }
1439 | ```
1440 |
1441 |
1442 |
1443 | **[⬆ برو بالا](#فهرست-موضوعات)**
1444 |
1445 | ### اصل باز/بسته یا (OCP)
1446 |
1447 | همانطور که توسط Bertrand Meyer بیان شد، "موجودیت های نرم افزار (کلاس ها ، ماژول ها، توابع و غیره) باید برای گسترده شدن باز باشند ولی برای تغییرات بسته باشند." حالا این یعنی چه؟ این اصل اساسا بیان می کند که باید به کاربران این اجازه را بدهید که عملکرد های جدید بدون تغییر کد فعلی اضافه کنند.
1448 |
1449 |
1450 |
1451 | **بد**
1452 |
1453 | ```javascript
1454 | class AjaxAdapter extends Adapter {
1455 | constructor() {
1456 | super();
1457 | this.name = "ajaxAdapter";
1458 | }
1459 | }
1460 |
1461 | class NodeAdapter extends Adapter {
1462 | constructor() {
1463 | super();
1464 | this.name = "nodeAdapter";
1465 | }
1466 | }
1467 |
1468 | class HttpRequester {
1469 | constructor(adapter) {
1470 | this.adapter = adapter;
1471 | }
1472 |
1473 | fetch(url) {
1474 | if (this.adapter.name === "ajaxAdapter") {
1475 | return makeAjaxCall(url).then(response => {
1476 | // transform response and return
1477 | });
1478 | } else if (this.adapter.name === "nodeAdapter") {
1479 | return makeHttpCall(url).then(response => {
1480 | // transform response and return
1481 | });
1482 | }
1483 | }
1484 | }
1485 |
1486 | function makeAjaxCall(url) {
1487 | // request and return promise
1488 | }
1489 |
1490 | function makeHttpCall(url) {
1491 | // request and return promise
1492 | }
1493 | ```
1494 |
1495 | **خوب**
1496 |
1497 | ```javascript
1498 | class AjaxAdapter extends Adapter {
1499 | constructor() {
1500 | super();
1501 | this.name = "ajaxAdapter";
1502 | }
1503 |
1504 | request(url) {
1505 | // request and return promise
1506 | }
1507 | }
1508 |
1509 | class NodeAdapter extends Adapter {
1510 | constructor() {
1511 | super();
1512 | this.name = "nodeAdapter";
1513 | }
1514 |
1515 | request(url) {
1516 | // request and return promise
1517 | }
1518 | }
1519 |
1520 | class HttpRequester {
1521 | constructor(adapter) {
1522 | this.adapter = adapter;
1523 | }
1524 |
1525 | fetch(url) {
1526 | return this.adapter.request(url).then(response => {
1527 | // transform response and return
1528 | });
1529 | }
1530 | }
1531 | ```
1532 |
1533 |
1534 |
1535 | **[⬆ برو بالا](#فهرست-موضوعات)**
1536 |
1537 | ### اصل جانشینی لیسکوو یا (LSP)
1538 |
1539 | این یک اصطلاح ترسناک برای یک مفهوم ساده است.
1540 | به صورت رسمی اینگونه تعریف می شود که، "اگر S یک subtype برای T باشد، آن وقت آبجکت های نوع T ممکن است جایگزین آبجکت های نوع S شوند بدون اینکه تغییری در property های آن برنامه ایجاد کنند (اصلاحیه، تسک اجرا شده است). که این خودش تعریف ترسناکتری است."
1541 |
1542 | بهترین توضیحش این است که اگر یک کلاس اصلی دارید و یک کلاس فرزند ، کلاس اصلی و کلاس فرزند می توانند بدون خروجی و نتایج اشتباه بجای هم استفاده شوند. شاید هنوز گیج کننده باشد، بذارید به مثال کلاسیک مربع-مستطیل نگاهی بیاندازیم. از دیدگاه ریاضی ، مربع یک مستطیل است، اما اگر شما آن را با استفاده از رابطه ی "is-a" با ارث بری مدلسازی کنید، شما سریعا به مشکل بر می خورید.
1543 |
1544 |
1545 |
1546 | **بد**
1547 |
1548 | ```javascript
1549 | class Rectangle {
1550 | constructor() {
1551 | this.width = 0;
1552 | this.height = 0;
1553 | }
1554 |
1555 | setColor(color) {
1556 | // ...
1557 | }
1558 |
1559 | render(area) {
1560 | // ...
1561 | }
1562 |
1563 | setWidth(width) {
1564 | this.width = width;
1565 | }
1566 |
1567 | setHeight(height) {
1568 | this.height = height;
1569 | }
1570 |
1571 | getArea() {
1572 | return this.width * this.height;
1573 | }
1574 | }
1575 |
1576 | class Square extends Rectangle {
1577 | setWidth(width) {
1578 | this.width = width;
1579 | this.height = width;
1580 | }
1581 |
1582 | setHeight(height) {
1583 | this.width = height;
1584 | this.height = height;
1585 | }
1586 | }
1587 |
1588 | function renderLargeRectangles(rectangles) {
1589 | rectangles.forEach(rectangle => {
1590 | rectangle.setWidth(4);
1591 | rectangle.setHeight(5);
1592 | const area = rectangle.getArea(); // بد Returns 25 for Square. Should be 20.
1593 | rectangle.render(area);
1594 | });
1595 | }
1596 |
1597 | const rectangles = [new Rectangle(), new Rectangle(), new Square()];
1598 | renderLargeRectangles(rectangles);
1599 | ```
1600 |
1601 | **خوب**
1602 |
1603 | ```javascript
1604 | class Shape {
1605 | setColor(color) {
1606 | // ...
1607 | }
1608 |
1609 | render(area) {
1610 | // ...
1611 | }
1612 | }
1613 |
1614 | class Rectangle extends Shape {
1615 | constructor(width, height) {
1616 | super();
1617 | this.width = width;
1618 | this.height = height;
1619 | }
1620 |
1621 | getArea() {
1622 | return this.width * this.height;
1623 | }
1624 | }
1625 |
1626 | class Square extends Shape {
1627 | constructor(length) {
1628 | super();
1629 | this.length = length;
1630 | }
1631 |
1632 | getArea() {
1633 | return this.length * this.length;
1634 | }
1635 | }
1636 |
1637 | function renderLargeShapes(shapes) {
1638 | shapes.forEach(shape => {
1639 | const area = shape.getArea();
1640 | shape.render(area);
1641 | });
1642 | }
1643 |
1644 | const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
1645 | renderLargeShapes(shapes);
1646 | ```
1647 |
1648 |
1649 |
1650 | **[⬆ برو بالا](#فهرست-موضوعات)**
1651 |
1652 | ### اصل تفکیک Interface یا (ISP)
1653 |
1654 | جاوااسکریپت Interfaces ندارد، پس این اصل به طور قطع مانند دیگر اصول صدق نمی کند. با این حال این اصل مهم و مرتبط است حتی با وجود کمبود سیستم type در جاوااسکریپت.
1655 |
1656 | ISP بیان می کند که "کلاینت ها نباید به وابستگی به interface هایی که استفاده نمی کنند مجبور شوند". Interface ها بدلیل duck typing ها قراردادهای ضمنی در جاوااسکریپت هستند.
1657 |
1658 | یک مثال خوب که این اصل را در جاوااسکریپت نمایش می دهد، برای کلاس هایی است که تنظیمات بزرگی برای آبجکت ها نیاز دارند. نیاز نداشتن به کلاینت ها برای تنظیم گزینه های زیاد با منفعت است، چون اکثر اوقات آنها به همه تنظیمات نیاز پیدا نمی کنند. دلخواه کردن آنها، از داشتن یک "fat interface" جلو گیری می کند.
1659 |
1660 |
1661 |
1662 | **بد**
1663 |
1664 | ```javascript
1665 | class DOMTraverser {
1666 | constructor(settings) {
1667 | this.settings = settings;
1668 | this.setup();
1669 | }
1670 |
1671 | setup() {
1672 | this.rootNode = this.settings.rootNode;
1673 | this.settings.animationModule.setup();
1674 | }
1675 |
1676 | traverse() {
1677 | // ...
1678 | }
1679 | }
1680 |
1681 | const $ = new DOMTraverser({
1682 | rootNode: document.getElementsByTagName("body"),
1683 | animationModule() {} // Most of the time, we won't need to animate when traversing.
1684 | // ...
1685 | });
1686 | ```
1687 |
1688 | **خوب**
1689 |
1690 | ```javascript
1691 | class DOMTraverser {
1692 | constructor(settings) {
1693 | this.settings = settings;
1694 | this.options = settings.options;
1695 | this.setup();
1696 | }
1697 |
1698 | setup() {
1699 | this.rootNode = this.settings.rootNode;
1700 | this.setupOptions();
1701 | }
1702 |
1703 | setupOptions() {
1704 | if (this.options.animationModule) {
1705 | // ...
1706 | }
1707 | }
1708 |
1709 | traverse() {
1710 | // ...
1711 | }
1712 | }
1713 |
1714 | const $ = new DOMTraverser({
1715 | rootNode: document.getElementsByTagName("body"),
1716 | options: {
1717 | animationModule() {}
1718 | }
1719 | });
1720 | ```
1721 |
1722 |
1723 |
1724 | **[⬆ برو بالا](#فهرست-موضوعات)**
1725 |
1726 | ### اصل وارونگی وابستگی یا (DIP)
1727 |
1728 | این اصل دو چیز اساسی را بیان می کند:
1729 |
1730 | 1. ماژول های سطح بالا نباید به ماژول های سطح پایین وابسته باشند، هر دو باید به Abstraction ها بستگی داشته باشند.
1731 | 2. Abstraction ها نباید به جزئیات بستگی داشته باشد. جزئیات باید به Abstraction ها بستگی داشته باشد.
1732 |
1733 | این موضوع در نگاه اول ممکن است سخت بنظر برسد، اما اگر شما با AngularJS کار کرده باشید، پیاده سازی این اصل در قالب Dependency
1734 | Injection (DI) را دیده اید. درحالی که آنها مفاهیم یکسانی نیستند ، DIP ماژول های سطح بالا را از مطلع شدن از جزئیات ماژول های سطح پایین مخفی نگه می دارد. این کار می تواند بوسیله DI محقق شود. بزرگترین منفعت این کار کاهش coupling بین ماژول هاست. coupling یک الگوی بد برای توسعه هست، چون قابلیت تغییر کدها را سخت می کند.
1735 |
1736 | همانطور که بیان شد، جاوااسکریپت interfaces ندارد، پس abstraction هایی که به آن وابسته هستند ، قراردادهای ضمنی هستند. گفتنی است که متدها و property ها که یک آبجکت یا کلاس را در معرض آبجکت یا کلاسی دیگر قرار می دهند. در مثال زیر، قرارداد ضمنی چیزی است که هر ماژول درخواستی برای ی `InventoryTracker` یک متد `requestItems` خواهد داشت.
1737 |
1738 |
1739 |
1740 | **بد**
1741 |
1742 | ```javascript
1743 | class InventoryRequester {
1744 | constructor() {
1745 | this.REQ_METHODS = ["HTTP"];
1746 | }
1747 |
1748 | requestItem(item) {
1749 | // ...
1750 | }
1751 | }
1752 |
1753 | class InventoryTracker {
1754 | constructor(items) {
1755 | this.items = items;
1756 |
1757 | // بد We have created a dependency on a specific request implementation.
1758 | // We should just have requestItems depend on a request method: `request`
1759 | this.requester = new InventoryRequester();
1760 | }
1761 |
1762 | requestItems() {
1763 | this.items.forEach(item => {
1764 | this.requester.requestItem(item);
1765 | });
1766 | }
1767 | }
1768 |
1769 | const inventoryTracker = new InventoryTracker(["apples", "bananas"]);
1770 | inventoryTracker.requestItems();
1771 | ```
1772 |
1773 | **خوب**
1774 |
1775 | ```javascript
1776 | class InventoryTracker {
1777 | constructor(items, requester) {
1778 | this.items = items;
1779 | this.requester = requester;
1780 | }
1781 |
1782 | requestItems() {
1783 | this.items.forEach(item => {
1784 | this.requester.requestItem(item);
1785 | });
1786 | }
1787 | }
1788 |
1789 | class InventoryRequesterV1 {
1790 | constructor() {
1791 | this.REQ_METHODS = ["HTTP"];
1792 | }
1793 |
1794 | requestItem(item) {
1795 | // ...
1796 | }
1797 | }
1798 |
1799 | class InventoryRequesterV2 {
1800 | constructor() {
1801 | this.REQ_METHODS = ["WS"];
1802 | }
1803 |
1804 | requestItem(item) {
1805 | // ...
1806 | }
1807 | }
1808 |
1809 | // By constructing our dependencies externally and injecting them, we can easily
1810 | // substitute our request module for a fancy new one that uses WebSockets.
1811 | const inventoryTracker = new InventoryTracker(
1812 | ["apples", "bananas"],
1813 | new InventoryRequesterV2()
1814 | );
1815 | inventoryTracker.requestItems();
1816 | ```
1817 |
1818 |
1819 |
1820 | **[⬆ برو بالا](#فهرست-موضوعات)**
1821 |
1822 | ## **تست**
1823 |
1824 | تست کد بسیار مهمتر از انتقال کد می باشد. زمانی که شما هیچ تستی ندارید یا به اندازه کافی تست ندارید، در هر بار انتقال کد به سرور نمی توانید مطمئن شوید که کد شما break نمی شود تصمیم گیری اینکه برا چه اساسی میزان تست نوشته شده کافی است به تیم شما بستگی دارد و ولی پوشش 100% (تمام statement ها و branche ها ) که اعتماد بالا و آرامش خاطر توسعه دهندگان را به همراه دارد این به این معنی است که شما علاوه بر داشتن یک فریمورک تست خوب نیاز به یک [ابزار پوششی خوب](https://gotwarlost.github.io/istanbul/) هم دارید .
1825 |
1826 | هیچ عذر برای ننوشتن تست ها نیست.
1827 | [تعداد زیادی فریمورک تست نویسی برای جاوااسکریپت](https://jstherightway.org/#testing-tools) وجود دارد، یکی از آنهایی که تیم شما ترجیح می دهد را پیدا کنید. وقتی شما فریمورک تستی را پیدا می کنید که در تیم شما کارایی دارد ،هدف گذاری کنید که برای هر feature یا module جدید که معرفی می کنید تست نوشته شود. اگر متد ترجیح داده توسط شما روش Test Driven Development یا (TDD) می باشد ، بسیار عالی است ، اما هدف اصلی اطمینان حاصل کردن از رسیدن و پوشش اهداف تعیین شده قبل از ارائه هر feature و یا تغییر دادن feature های موجود می باشد.
1828 |
1829 |
1830 | ### یک مفهوم در هر تست
1831 |
1832 |
1833 |
1834 | **بد**
1835 |
1836 | ```javascript
1837 | import assert from "assert";
1838 |
1839 | describe("MomentJS", () => {
1840 | it("handles date boundaries", () => {
1841 | let date;
1842 |
1843 | date = new MomentJS("1/1/2015");
1844 | date.addDays(30);
1845 | assert.equal("1/31/2015", date);
1846 |
1847 | date = new MomentJS("2/1/2016");
1848 | date.addDays(28);
1849 | assert.equal("02/29/2016", date);
1850 |
1851 | date = new MomentJS("2/1/2015");
1852 | date.addDays(28);
1853 | assert.equal("03/01/2015", date);
1854 | });
1855 | });
1856 | ```
1857 |
1858 | **خوب**
1859 |
1860 | ```javascript
1861 | import assert from "assert";
1862 |
1863 | describe("MomentJS", () => {
1864 | it("handles 30-day months", () => {
1865 | const date = new MomentJS("1/1/2015");
1866 | date.addDays(30);
1867 | assert.equal("1/31/2015", date);
1868 | });
1869 |
1870 | it("handles leap year", () => {
1871 | const date = new MomentJS("2/1/2016");
1872 | date.addDays(28);
1873 | assert.equal("02/29/2016", date);
1874 | });
1875 |
1876 | it("handles non-leap year", () => {
1877 | const date = new MomentJS("2/1/2015");
1878 | date.addDays(28);
1879 | assert.equal("03/01/2015", date);
1880 | });
1881 | });
1882 | ```
1883 |
1884 |
1885 |
1886 | **[⬆ برو بالا](#فهرست-موضوعات)**
1887 |
1888 | ## **همزمانی**
1889 |
1890 | ### بجای callback ها از promise ها استفاده کنید
1891 |
1892 | callback ها تمیز نیستند، و باعث ایجاد تو در تویی بیش از حد می شود.
1893 | در ES2015/ES6 ویژگی Promise به عنوان global type از پیش ساخته شده می باشد، از آنها استفاده کنید!
1894 |
1895 |
1896 |
1897 | **بد**
1898 |
1899 | ```javascript
1900 | import { get } from "request";
1901 | import { writeFile } from "fs";
1902 |
1903 | get(
1904 | "https://en.wikipedia.org/wiki/Robert_Cecil_Martin",
1905 | (requestErr, response, body) => {
1906 | if (requestErr) {
1907 | console.error(requestErr);
1908 | } else {
1909 | writeFile("article.html", body, writeErr => {
1910 | if (writeErr) {
1911 | console.error(writeErr);
1912 | } else {
1913 | console.log("File written");
1914 | }
1915 | });
1916 | }
1917 | }
1918 | );
1919 | ```
1920 |
1921 | **خوب**
1922 |
1923 | ```javascript
1924 | import { get } from "request-promise";
1925 | import { writeFile } from "fs-extra";
1926 |
1927 | get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
1928 | .then(body => {
1929 | return writeFile("article.html", body);
1930 | })
1931 | .then(() => {
1932 | console.log("File written");
1933 | })
1934 | .catch(err => {
1935 | console.error(err);
1936 | });
1937 | ```
1938 |
1939 |
1940 |
1941 | **[⬆ برو بالا](#فهرست-موضوعات)**
1942 |
1943 | ### Async / Await حتی تمیزتر از Promiseها
1944 |
1945 | Promise جایگزین های تمیزی برای callback ها هستندو ولی در ES2017/ES8 ویژگی Async / Await اضافه شد که می تواند راه حل تمیزتری را پیشنهاد کند. تمام چیزی که نیاز دارید یک تابع با کلمه کلیدی به عنوان پیشوند `async` می باشد و سپس می توانید منطق کد خودرا بدون زنجیره `then` بنویسید. اگر می توانید همین امروز از منفعت ویژگی های ES2017/ES8 بهره مند شوید.
1946 |
1947 |
1948 |
1949 | **بد**
1950 |
1951 | ```javascript
1952 | import { get } from "request-promise";
1953 | import { writeFile } from "fs-extra";
1954 |
1955 | get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
1956 | .then(body => {
1957 | return writeFile("article.html", body);
1958 | })
1959 | .then(() => {
1960 | console.log("File written");
1961 | })
1962 | .catch(err => {
1963 | console.error(err);
1964 | });
1965 | ```
1966 |
1967 | **خوب**
1968 |
1969 | ```javascript
1970 | import { get } from "request-promise";
1971 | import { writeFile } from "fs-extra";
1972 |
1973 | async function getCleanCodeArticle() {
1974 | try {
1975 | const body = await get(
1976 | "https://en.wikipedia.org/wiki/Robert_Cecil_Martin"
1977 | );
1978 | await writeFile("article.html", body);
1979 | console.log("File written");
1980 | } catch (err) {
1981 | console.error(err);
1982 | }
1983 | }
1984 |
1985 | getCleanCodeArticle()
1986 | ```
1987 |
1988 |
1989 |
1990 | **[⬆ برو بالا](#فهرست-موضوعات)**
1991 |
1992 | ## **ارور هندلینگ**
1993 |
1994 | خطاهای ایجاد شده چیز خوبی هستند! آنها به این معنی هستند که در زمان اجرا مشکلی در برنامه بوجود آمده را با موفقیت شناسایی کرده است و با متوقف کردن اجرای تابع و از بین بردن process در Node ، شمارا در جریان می گذارد و با stack trace در کنسول به شما خبر می دهد.
1995 |
1996 | ### ارور های دریافتی توسط catch را نادیده نگیرید
1997 |
1998 | از کنار خطا هایی که با catch گرفته شدند گذشتن به شما توانایی بطرف کردن یا واکنش به ارور ایجاد شده را نمی دهد. ورود خطا به کنسول (console.log) خیلی خوب نیست، زیرا اغلب اوقات ممکن است در دریایی از چیزهای چاپ شده در کنسول گم شود. اگر هر بیتی از کد را در try / catch قرار دهید، به این معنی است که فکر می کنید ممکن است در آنجا خطایی رخ دهد و بنابراین باید برنامه ای تنظیم کنید یا یک مسیر کد برای زمان بروز آن ایجاد کنید.
1999 |
2000 |
2001 |
2002 | **بد**
2003 |
2004 | ```javascript
2005 | try {
2006 | functionThatMightThrow();
2007 | } catch (error) {
2008 | console.log(error);
2009 | }
2010 | ```
2011 |
2012 | **خوب**
2013 |
2014 | ```javascript
2015 | try {
2016 | functionThatMightThrow();
2017 | } catch (error) {
2018 | // One option (more noisy than console.log):
2019 | console.error(error);
2020 | // Another option:
2021 | notifyUserOfError(error);
2022 | // Another option:
2023 | reportErrorToService(error);
2024 | // OR do all three!
2025 | }
2026 | ```
2027 |
2028 |
2029 |
2030 | ### promise های رد شده را نادیده نگیرید
2031 |
2032 | به همین دلیل نباید از ارورهای گرفته شده با try / catch چشم پوشی کنید.
2033 |
2034 |
2035 |
2036 | **بد**
2037 |
2038 | ```javascript
2039 | getdata()
2040 | .then(data => {
2041 | functionThatMightThrow(data);
2042 | })
2043 | .catch(error => {
2044 | console.log(error);
2045 | });
2046 | ```
2047 |
2048 | **خوب**
2049 |
2050 | ```javascript
2051 | getdata()
2052 | .then(data => {
2053 | functionThatMightThrow(data);
2054 | })
2055 | .catch(error => {
2056 | // One option (more noisy than console.log):
2057 | console.error(error);
2058 | // Another option:
2059 | notifyUserOfError(error);
2060 | // Another option:
2061 | reportErrorToService(error);
2062 | // OR do all three!
2063 | });
2064 | ```
2065 |
2066 |
2067 |
2068 | **[⬆ برو بالا](#فهرست-موضوعات)**
2069 |
2070 | ## **قالب بندی**
2071 |
2072 | قالب بندی یک عمل ذهنی است. مانند بسیاری از قوانین در اینجا، هیچ قانون سخت و سریعی وجود ندارد که شما باید از آن پیروی کنید. نکته اصلی این است که در مورد قالب بندی بحث نکنید. [ابزارهای زیادی](https://standardjs.com/rules.html) برای خودکار کردن این کار وجود دارد. یکی از آن ها را استفاده کنید! بحث و جدال مهندسان درباره قالب بندی، اتلاف وقت و هزینه است.
2073 |
2074 | برای مواردی که تحت عنوان قالب بندی خودکار قرار نمی گیرند. (تورفتگی، tabها در مقابل spaceها، ” یا ‘ و …)
2075 |
2076 | ### از حروف بزرگ استفاده کنید
2077 |
2078 | جاوااسکریپت type ندارد، پس با حروف بزرگ نوشتن به شما چیزهای زیادی در مورد متغیرها و توابع و غیر می گوید. این قوانین ذهنی هستند، پس تیم شما می تواند هر آنچه را که می خواهد انتخاب کند. مهم نیست که همه شما چه چیزی را انتخاب می کنید، فقط باید در این موضوع ثابت قدم باشید.
2079 |
2080 |
2081 |
2082 | **بد**
2083 |
2084 | ```javascript
2085 | const DAYS_IN_WEEK = 7;
2086 | const daysInMonth = 30;
2087 |
2088 | const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
2089 | const Artists = ["ACDC", "Led Zeppelin", "The Beatles"];
2090 |
2091 | function eraseDatabase() {}
2092 | function restore_database() {}
2093 |
2094 | class animal {}
2095 | class Alpaca {}
2096 | ```
2097 |
2098 | **خوب**
2099 |
2100 | ```javascript
2101 | const DAYS_IN_WEEK = 7;
2102 | const DAYS_IN_MONTH = 30;
2103 |
2104 | const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
2105 | const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"];
2106 |
2107 | function eraseDatabase() {}
2108 | function restoreDatabase() {}
2109 |
2110 | class Animal {}
2111 | class Alpaca {}
2112 | ```
2113 |
2114 |
2115 |
2116 | **[⬆ برو بالا](#فهرست-موضوعات)**
2117 |
2118 | ### توابع تماس گیرنده(callers) و تماس گرفته شده (callees) باید به هم نزدیک باشند
2119 |
2120 | وقتی یک تابع تابع دیگری را صدا می زند، آن دو تابع را به صورت عمودی درون فایل سورس نزدیک هم نگه دارید. به طور ایده آل ، تابع caller را بالای تابع callee نگه دارید.
2121 | ما بیشتر مایلیم کد را از بالا به پایین بخوانیم مثل یک روزنامه . بخاطر این موضوع، کد شما هم همانطور خوانده می شود.
2122 |
2123 |
2124 |
2125 | **بد**
2126 |
2127 | ```javascript
2128 | class PerformanceReview {
2129 | constructor(employee) {
2130 | this.employee = employee;
2131 | }
2132 |
2133 | lookupPeers() {
2134 | return db.lookup(this.employee, "peers");
2135 | }
2136 |
2137 | lookupManager() {
2138 | return db.lookup(this.employee, "manager");
2139 | }
2140 |
2141 | getPeerReviews() {
2142 | const peers = this.lookupPeers();
2143 | // ...
2144 | }
2145 |
2146 | perfReview() {
2147 | this.getPeerReviews();
2148 | this.getManagerReview();
2149 | this.getSelfReview();
2150 | }
2151 |
2152 | getManagerReview() {
2153 | const manager = this.lookupManager();
2154 | }
2155 |
2156 | getSelfReview() {
2157 | // ...
2158 | }
2159 | }
2160 |
2161 | const review = new PerformanceReview(employee);
2162 | review.perfReview();
2163 | ```
2164 |
2165 | **خوب**
2166 |
2167 | ```javascript
2168 | class PerformanceReview {
2169 | constructor(employee) {
2170 | this.employee = employee;
2171 | }
2172 |
2173 | perfReview() {
2174 | this.getPeerReviews();
2175 | this.getManagerReview();
2176 | this.getSelfReview();
2177 | }
2178 |
2179 | getPeerReviews() {
2180 | const peers = this.lookupPeers();
2181 | // ...
2182 | }
2183 |
2184 | lookupPeers() {
2185 | return db.lookup(this.employee, "peers");
2186 | }
2187 |
2188 | getManagerReview() {
2189 | const manager = this.lookupManager();
2190 | }
2191 |
2192 | lookupManager() {
2193 | return db.lookup(this.employee, "manager");
2194 | }
2195 |
2196 | getSelfReview() {
2197 | // ...
2198 | }
2199 | }
2200 |
2201 | const review = new PerformanceReview(employee);
2202 | review.perfReview();
2203 | ```
2204 |
2205 |
2206 |
2207 | **[⬆ برو بالا](#فهرست-موضوعات)**
2208 |
2209 | ## **کامنت گذاری**
2210 |
2211 | ### فقط چیزهایی را کامنت کنید که منطق بیزنسی پیچیده ای دارند
2212 |
2213 | کامنت ها یک ضرورت نیستند. یک کد خوب در اکثر مواقع خودش را داکیومنت می کند و نیازی به کامنت ندارد.
2214 |
2215 |
2216 |
2217 | **بد**
2218 |
2219 | ```javascript
2220 | function hashIt(data) {
2221 | // The hash
2222 | let hash = 0;
2223 |
2224 | // Length of string
2225 | const length = data.length;
2226 |
2227 | // Loop through every character in data
2228 | for (let i = 0; i < length; i++) {
2229 | // Get character code.
2230 | const char = data.charCodeAt(i);
2231 | // Make the hash
2232 | hash = (hash << 5) - hash + char;
2233 | // Convert to 32-bit integer
2234 | hash &= hash;
2235 | }
2236 | }
2237 | ```
2238 |
2239 | **خوب**
2240 |
2241 | ```javascript
2242 | function hashIt(data) {
2243 | let hash = 0;
2244 | const length = data.length;
2245 |
2246 | for (let i = 0; i < length; i++) {
2247 | const char = data.charCodeAt(i);
2248 | hash = (hash << 5) - hash + char;
2249 |
2250 | // Convert to 32-bit integer
2251 | hash &= hash;
2252 | }
2253 | }
2254 | ```
2255 |
2256 |
2257 |
2258 | **[⬆ برو بالا](#فهرست-موضوعات)**
2259 |
2260 | ### کد های کامنت شده را از کد اصلی خود حذف کنید
2261 |
2262 | ورژن کنترل به این دلیل وجود دارد. کدهای قدیمی را از تاریخچه دنبال کنید.
2263 |
2264 |
2265 |
2266 | **بد**
2267 |
2268 | ```javascript
2269 | doStuff();
2270 | // doOtherStuff();
2271 | // doSomeMoreStuff();
2272 | // doSoMuchStuff();
2273 | ```
2274 |
2275 | **خوب**
2276 |
2277 | ```javascript
2278 | doStuff();
2279 | ```
2280 |
2281 |
2282 |
2283 | **[⬆ برو بالا](#فهرست-موضوعات)**
2284 |
2285 | ### کامنت های ژورنالی نذارید
2286 |
2287 | به یاد داشته باشید که از ورژن کنترل استفاده کنید! نیازی به کد مرده ، کدهای کامنت شده و کامنت های ژورنالی نیست. از `git log` برای اطلاع از تاریخچه استفاده کنید.
2288 |
2289 |
2290 |
2291 | **بد**
2292 |
2293 | ```javascript
2294 | /**
2295 | * 2016-12-20: Removed monads, didn't understand them (RM)
2296 | * 2016-10-01: Improved using special monads (JP)
2297 | * 2016-02-03: Removed type-checking (LI)
2298 | * 2015-03-14: Added combine with type-checking (JR)
2299 | */
2300 | function combine(a, b) {
2301 | return a + b;
2302 | }
2303 | ```
2304 |
2305 | **خوب**
2306 |
2307 | ```javascript
2308 | function combine(a, b) {
2309 | return a + b;
2310 | }
2311 | ```
2312 |
2313 |
2314 |
2315 | **[⬆ برو بالا](#فهرست-موضوعات)**
2316 |
2317 | ### از نشانگرهای موقعیتی خودداری کنید
2318 |
2319 | آنها معمولا فقط کدمان را شلوغ می کنند. اجازه دهید که نام توابع و متغیرها دارای تورفتگی و فرمت مناسب باشند تا ساختار دیداری مناسبی را در کد شما ایجاد کنند.
2320 |
2321 |
2322 |
2323 | **بد**
2324 |
2325 | ```javascript
2326 | ////////////////////////////////////////////////////////////////////////////////
2327 | // Scope Model Instantiation
2328 | ////////////////////////////////////////////////////////////////////////////////
2329 | $scope.model = {
2330 | menu: "foo",
2331 | nav: "bar"
2332 | };
2333 |
2334 | ////////////////////////////////////////////////////////////////////////////////
2335 | // Action setup
2336 | ////////////////////////////////////////////////////////////////////////////////
2337 | const actions = function() {
2338 | // ...
2339 | };
2340 | ```
2341 |
2342 | **خوب**
2343 |
2344 | ```javascript
2345 | $scope.model = {
2346 | menu: "foo",
2347 | nav: "bar"
2348 | };
2349 |
2350 | const actions = function() {
2351 | // ...
2352 | };
2353 | ```
2354 |
2355 |
2356 |
2357 | **[⬆ برو بالا](#فهرست-موضوعات)**
2358 |
2359 | ## ترجمه
2360 |
2361 | همچنین ترجمه این مطلب به سایر زبان ها در دسترس است:
2362 |
2363 |
2364 |
2365 | -  **Armenian**: [hanumanum/clean-code-javascript/](https://github.com/hanumanum/clean-code-javascript)
2366 | -  **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/)
2367 | -  **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript)
2368 | -  **Simplified Chinese**:
2369 | - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js)
2370 | - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript)
2371 | -  **Traditional Chinese**: [AllJointTW/clean-code-javascript](https://github.com/AllJointTW/clean-code-javascript)
2372 | -  **French**: [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr)
2373 | -  **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript)
2374 | -  **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/)
2375 | -  **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/)
2376 | -  **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/)
2377 | -  **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko)
2378 | -  **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl)
2379 | -  **Russian**:
2380 | - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/)
2381 | - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript)
2382 | -  **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript)
2383 | -  **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es)
2384 | -  **Serbian**: [doskovicmilos/clean-code-javascript/](https://github.com/doskovicmilos/clean-code-javascript)
2385 | -  **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript/tree/turkish-translation)
2386 | -  **Ukrainian**: [mindfr1k/clean-code-javascript-ua](https://github.com/mindfr1k/clean-code-javascript-ua)
2387 | -  **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/)
2388 |
2389 | -  **Persian**: [hamettio/clean-code-javascript](https://github.com/hamettio/clean-code-javascript)
2390 |
2391 |
2392 |
2393 | **[⬆ برو بالا](#فهرست-موضوعات)**
2394 |
2395 |
2396 |
--------------------------------------------------------------------------------