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