المحتوى عن 'الأصناف'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • التجارة الإلكترونية
  • مقالات ريادة أعمال عامة

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 1 نتيجة

  1. ملاحظة: هذا الدليل يفترض أنك تستخدم Babel، كما يتطلّب استخدام babel-preset-airbnb أو ما يماثله. ويفترض أيضًا أنّك قمت بتثبيت babel-preset-airbnb في تطبيقك، عبر airbnb-browser-shims أو ما يماثله. الأنواع الأنواع الأساسية عندما تتعامل مع أحد الأنواع الأساسية فأنت تعمل مباشرةً على قيمته. String number boolean null undefined symbol const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9 الرموز Symbols لا يمكن أن تُدرج (polyfilled) بدقة، لذلك لا ينبغي استخدامها عند استهداف المتصفحات/البيئات التي لا تدعمها تلقائيًّا. الأنواع المركبة (Complex) عند التعامل مع نوع مركّب فأنت تعمل على مرجعٍ لقيمته. object Array function const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9 المراجع References استخدم const لجميع مراجعك. وتجنب استخدام var. لماذا؟ لأنّ هذا سيضمن لك ألّا تعيد تعيين مراجعك، والذي يمكن أن يؤدي إلى أخطاء، ويُصعّب فهم الكود البرمجي. // bad var a = 1; var b = 2; // good const a = 1; const b = 2; إن كنت مضطرًّا لإعادة تعيين المراجع، استخدم let بدلاً من var. لماذا؟ لأن مدى let محدود في الكتلة البرمجية (block-scoped) وليس محدودًا داخل الدالة (function-scoped) كما هو الحال مع var. // bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; } تذكر أن مدى كل من let و constمحدود في الكتلة (block-scoped). // const and let only exist in the blocks they are defined in. { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError الكائنات Objects استخدم الأسلوب الحرفي لإنشاء الكائن. // bad const item = new Object(); // good const item = {}; استخدم أسماءً محسوبةً للخصائص computed property names عند إنشاء كائنات ذات أسماء خصائص ديناميكية. لماذا؟ لأن هذا سيسمح لك بتعريف جميع خصائص الكائن في مكان واحد. function getKey(k) { return `a key named ${k}`; } // bad const obj = { id: 5, name: 'San Francisco', }; obj[getKey('enabled')] = true; // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true, }; استخدم أسلوب التعريف المختصر لوظائف الكائن. // bad const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { value: 1, addValue(value) { return atom.value + value; }, }; استخدم التعريف المختصر لقيمة الخاصية. لماذا؟ لأنه أقصر وأوضح. const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, }; اجمع الخصائص المُختصرة في بداية التصريح بالكائن object declaration. لماذا؟ لأنه من السهل هكذا معرفة أي الخصائص تستخدم الاختصار. const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { episodeOne: 1, twoJediWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker, }; // good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJediWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4, }; لا تضع بين علامات التنصيص إلّا الخصائص التي لها أسماء غير صالحة. لماذا؟ بشكل عام، لأنّه أسهل للقراءة ويُحسّن وضوح الكود، كما أنّه يسهُل استخدامه من قبل محركات الجافا سكريبت. // bad const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // good const good = { foo: 3, bar: 4, 'data-blah': 5, }; لا تستدعي وظائف Object.prototype مباشرة، مثل hasOwnProperty، propertyIsEnumerable، و isPrototypeOf. لماذا؟ تلك الوظائف قد تُظلّل shadowed من قبل خصائص الكائن المَعني – مثلًا {hasOwnProperty: false} –أو قد يكون الكائن معدومًا (Object.create(null)). // bad console.log(object.hasOwnProperty(key)); // good console.log(Object.prototype.hasOwnProperty.call(object, key)); // best const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. /* or */ import has from 'has'; // https://www.npmjs.com/package/has // ... console.log(has.call(object, key)); يفضل استخدام عامل التناقل (spread operator) على الكائن بدلًا من Object.assign إن كنت تريد القيام بالنسخ السطحي (shallow-copy) للكائنات. أو يمكنك استخدام العامل الساكن (rest operator) للحصول على كائن جديد مع حذف خصائص معينة. // very bad const original = { a: 1, b: 2 }; const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ delete copy.a; // so does this // bad const original = { a: 1, b: 2 }; const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 } // good const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 } الصفوف Arrays استخدم التعبير الحرفي لإنشاء الصف. // bad const items = new Array(); // good const items = []; استخدم Array#push بدلًا من الإحالة المباشرة لإضافة عناصر إلى الصف. const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra'); استخدم تناقل الصفوف (array spreads) لنسخ الصفوف. // bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]; لتحويل كائن شبيه بالصفوف إلى صف، استخدم التناقل بدلًا من Array.from. const foo = document.querySelectorAll('.foo'); // good const nodes = Array.from(foo); // best const nodes = [...foo]; استخدم Array.from بدلًا من التناقل لأجل تطبيق إجراء على المكرّرات mapping over iterables، وذلك لتجنب خلق صف مؤقت. // bad const baz = [...foo].map(bar); // good const baz = Array.from(foo, bar); استخدم العبارةreturn في استدعاءات وظائف الصف array method callbacks. لا ضير في حذفreturn إن كان متن الدالة function body يتكون من عبارة واحدة من دون آثار جانبية. // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); // good [1, 2, 3].map(x => x + 1); // bad - no returned value means `memo` becomes undefined after the first iteration [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memo[index] = flatten; }); // good [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); memo[index] = flatten; return flatten; }); // bad inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } else { return false; } }); // good inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee'; } return false; }); استخدم سطرًا جديدًا بعد معقوفة الفتح وقبل معقوفة إغلاق الصف إذا كان الصف متعدد الأسطر. // bad const arr = [ [0, 1], [2, 3], [4, 5], ]; const objectInArray = [{ id: 1, }, { id: 2, }]; const numberInArray = [ 1, 2, ]; // good const arr = [[0, 1], [2, 3], [4, 5]]; const objectInArray = [ { id: 1, }, { id: 2, }, ]; const numberInArray = [ 1, 2, ]; التفكيك Destructuring استخدم تفكيك الكائن عند التعامل مع عدة خصائص للكائن. لماذا؟ التفكيك يُعفيك من الحاجة إلى إنشاء مراجع مؤقتة لتلك الخصائص. // bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`; } // good function getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}`; } // best function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`; } استخدم تفكيك الصفوف. const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr; استخدم تفكيك الكائن لأجل إرجاع أكثر من قيمة واحدة multiple return values، ولا تستخدم تفكيك الصفوف. لماذا؟ يمكنك إضافة خاصيات جديدة مع الوقت وتغيير الترتيب دون مشاكل. // bad function processInput(input) { // then a miracle occurs return [left, right, top, bottom]; } // the caller needs to think about the order of return data const [left, __, top] = processInput(input); // good function processInput(input) { // then a miracle occurs return { left, right, top, bottom }; } // the caller selects only the data they need const { left, top } = processInput(input); النصوص Strings استخدم علامات التنصيص المفردة ’ لأجل النصوص. // bad const name = "Capt. Janeway"; // bad - template literals should contain interpolation or newlines const name = `Capt. Janeway`; // good const name = 'Capt. Janeway'; النصوص التي يتجاوز طولها 100 حرف لا ينبغي أن تُكتب على أسطر متعددة باستخدام ضمّ النصوص concatenation. لماذا؟ النصوص التي فيها أخطاء تكون مزعجةً وتجعل الكود أقل قابلية للبحث. // bad const errorMessage = 'This is a super long error that was thrown because \ of Batman. When you stop to think about how Batman had anything to do \ with this, you would get nowhere \ fast.'; // bad const errorMessage = 'This is a super long error that was thrown because ' + 'of Batman. When you stop to think about how Batman had anything to do ' + 'with this, you would get nowhere fast.'; // good const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; عند بناء النصوص برمجيا، حاول استخدام قوالب النصوص بدلًا من الضمّ concatenation. لماذا؟ قوالب النصوص تجعل الكود أكثر مقروئية وإيجازًا مع أسطر جديدة مناسبة وميزات حشو النصوصstring interpolation features. // bad function sayHi(name) { return 'How are you, ' + name + '?'; } // bad function sayHi(name) { return ['How are you, ', name, '?'].join(); } // bad function sayHi(name) { return `How are you, ${ name }?`; } // good function sayHi(name) { return `How are you, ${name}?`; } لا تستخدم أبدًا ()eval على النصوص، لأنها تفتح الكثير من نقاط الضعف. تجنب تحريف الأحرف (escape characters) في النصوص قدر الإمكان. لماذا؟ الشرطة المائلة Backslashes تضر بالمقروئية، وبالتالي يجب ألًا تُستخدم إلا عند الضرورة. // bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`; الدوال استخدم عبارات الدالة المُسماة named function expression بدلًا من تصريحات الدوال function declarations لماذا؟ تصريحات الدوال تُرفَّع hoisted إلى الأعلى، ما يعني أنه سيكون من السهل الإحالة إلى الدالة قبل أن يتم تعريفها في الملف. وهذا يضر المقروئية والصيانة. إذا وجدت أن تعريف دالة ما كبير أو معقد بشكل مربك، فربما حان الوقت لوضعه في وحدة خاصة به! لا تنسى أن تُسمي التعبير صراحة، بغض النظر عما إذا كان الاسم مستنتجًا من المتغير الحاوي inferred from the containing variable (كما هو الحال غالبًا في المتصفحات الحديثة أو عند استخدام متراجمات Babel). هذا سيلغي أي افتراضات حول مكدّس الخطأ Error’s call stack // bad function foo() { // ... } // bad const foo = function () { // ... }; // good // lexical name distinguished from the variable-referenced invocation(s) const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... }; ضع الدوال المستدعاة فورًا بين قوسين. لماذا؟ الدوال المستدعاة فورًا هي عبارةٌ واحدة – لذلك وضعها هي وعبارة استدعائها بين قوسين يجعل الكود واضحًا. وإن كان من المستبعد جدّا أن تحتاج الدوال المستدعاة فورًا IIFE)). // immediately-invoked function expression (IIFE) (function () { console.log('Welcome to the Internet. Please follow me.'); }()); لا تصرّح أبدًا بالدوال في كتلة غير خاصة بالدوال non-function block (مثل if, while …). قم بإحالة الدالة إلى متغير بدلًا من ذلك. المتصفحات ستسمح لك بفعل ذلك، ولكنّها ستفسرها بطرق مختلفة، وهو أمر لا يحبه المبرمجون. ملاحظة: ECMA-262 يٌعرّف الكتلة block كقائمة من التعليمات. وتعريف الدالة ليس تعليمةً. // bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; } لا تسمّي معاملًا بالاسم arguments. لأنه سيأخذ الأسبقية على الكائن arguments الذي يُحدد تلقائيّا في نطاق كل الدوال. // bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... } لا تستخدم أبدًا arguments، واستخدم الأسلوب الساكن rest syntax بدلًا من ذلك. لماذا؟ واضحة في تحديد المعاملات arguments التي تريد سحبها. بالإضافة إلى ذلك، المعاملات الساكنة هي في الأصل صف، وليست شبيهة بالصفوف مثل arguments. // bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); } استخدم أسلوب المعاملات الافتراضية بدلًا من المعاملات المتحوّلة mutating function arguments. // really bad function handleThings(opts) { // No! We shouldn’t mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... } تجنب الآثار الجانبية في المعاملات الافتراضية. لماذا؟ لأنّها مربكة. var b = 1; // bad function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3 ضع دائمًا المعاملات الافتراضية في الأخير. // bad function handleThings(opts = {}, name) { // ... } // good function handleThings(name, opts = {}) { // ... } لا تستخدم أبدًا منشئ الدوال Function لإنشاء دالة جديدة. لماذا؟ إنشاء دالة بهذه الطريقة يتضمّن تنفيذ نص كما تفعل ()eval، وهو ما يفتح نقاط ضعف. // bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b'); ضع مسافة في توقيع الدالة. لماذا؟ الاتساق أمر جيد، كما لن تكون مضطرًا لإضافة أو إزالة مسافة عند إضافة أو إزالة اسم. // bad const f = function(){}; const g = function (){}; const h = function() {}; // good const x = function () {}; const y = function a() {}; لا تحوًل mutate المعاملات أبدًا. لماذا؟ يمكن للتعامل مع الكائنات التي مُرُرت كمعاملات parameters أن يتسبب في آثار جانبية غير مرغوب فيها في المستدعي الأصلي. // bad function f1(obj) { obj.key = 1; } // good function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; } لا تُعد تعيين المعاملات. لماذا؟ إعادة تعيين المعاملات يمكن أن يؤدي إلى سلوك غير متوقع، خصوصًا عند التعامل مع الكائن arguments. كما يمكن أن يسبب مشاكل في الأداء، خصوصا في V8. // bad function f1(a) { a = 1; // ... } function f2(a) { if (!a) { a = 1; } // ... } // good function f3(a) { const b = a || 1; // ... } function f4(a = 1) { // ... } من الأفضل استخدام العملية التناقلية لاستدعاء الدوال المرنةvariadic functions . لماذا؟ لأنها أوضح، فلست مضطرًّا لتجهيز السياق، كما لا يمكنك أن تجمع بسهولة new مع apply. // bad const x = [1, 2, 3, 4, 5]; console.log.apply(console, x); // good const x = [1, 2, 3, 4, 5]; console.log(...x); // bad new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // good new Date(...[2016, 8, 5]); الدوال التي لها توقيعات أو استدعاءات متعددة الأسطر، ينبغي أن تكون مسافاتها البادئة تمامًا مثل كل القوائم متعددة الأسطر الأخرى في هذا الدليل: حيث كل عنصر في سطر، مع فاصلة زائدة بعد العنصر الأخير. // bad function foo(bar, baz, quux) { // ... } // good function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, ); الدوال السهميّة Arrow Functions عندما تكون مضطرًّا لاستخدام دالة مجهولة، استخدم أسلوب الدالة السهمية. لماذا؟ لأنها تخلق نسخة من الدالة والتي يمكن تنفيذها في سياق this، وهو عادةً ما ترغب فيه، كما أنها أكثر إيجازًا. متى لا تستخدمها؟ إذا كانت لديك دالة معقدة إلى حد ما، فقد ترغب في تعريف الدالة بشكل مستقل، ثم استخدامها في العبارة. // bad [1, 2, 3].map(function (x) { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); إن كان متن الدالة function body يتكون من تعليمة واحدة تُرجِع تعبيرًا دون آثار جانبية، فاحذف اللّامّتين واستخدم return الضمنية. خلافًا لذلك، أبق على الأقواس واستخدم العبارة return. لماذا؟ تٌسهل قراءة الكود عند استخدام دوال متعددة معًا. // bad [1, 2, 3].map(number => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map(number => `A string containing the ${number}.`); // good [1, 2, 3].map((number) => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map((number, index) => ({ [index]: number, })); // No implicit return with side effects function foo(callback) { const val = callback(); if (val === true) { // Do something if callback returns true } } let bool = false; // bad foo(() => bool = true); // good foo(() => { bool = true; }); في حالة امتد التعبير عبر عدة أسطر، ضعه بين قوسين. لماذا؟ لتوضيح أين تبدأ وأين تنتهي الدالة. // bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // good ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) )); إن كان للدالة معامل واحد فقط ولا تستخدم اللامّتين، فقم بحذف الأقواس. خلافًا ذلك، دائمًا ضع أقواسًا حول المعاملات من أجل مزيد من الوضوح والاتساق. ملاحظة: من المقبول أيضًا أن تستخدم الأقواس دائمًا، وفي هذه الحالة استخدم الخيارalways"". لماذا؟ لأجل وضوح أكبر. // bad [1, 2, 3].map((x) => x * x); // good [1, 2, 3].map(x => x * x); // good [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); // bad [1, 2, 3].map(x => { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; }); تجنب الخلط بين عبارات الدوال السهمية(=>) وبين عوامل المقارنة (<= , >=). // bad const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; // bad const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; // good const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); // good const itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height > 256 ? largeSize : smallSize; }; الأصناف والمُنشِئات Classes & Constructors استخدم دائمًا class. وتجنب التعامل مع prototype مباشرةً. لماذا؟ العبارة class أكثر إيجازا ووضوحًا. // bad function Queue(contents = []) { this.queue = [...contents]; } Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value; }; // good class Queue { constructor(contents = []) { this.queue = [...contents]; } pop() { const value = this.queue[0]; this.queue.splice(0, 1); return value; } } استخدم extends لأجل الوراثة. لماذا؟ لأنها وسيلة مدمجة لوراثة نموذج الوظائف extends دون التسبب بمشاكل مع instanceof. // bad const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); } inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; }; // good class PeekableQueue extends Queue { peek() { return this.queue[0]; } } يمكن للوظائف أن تُرجع this للمساعدة بخصوص تسلسل الوظائف method chaining. // bad Jedi.prototype.jump = function () { this.jumping = true; return true; }; Jedi.prototype.setHeight = function (height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // good class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump() .setHeight(20); لا بأس في كتابة وظائف ()toString مخصصة، فقط تأكد من أنها تعمل بسلاسة ولا تتسبب في أي آثار جانبية. class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } } الأصناف لديها منشئ افتراضي إذا لم يتم تحديد واحد سلفًا. لذلك فالمنشئات الفارغة أو التي تنوب عن الصنف الأب وحسب ليست ضرورية. // bad class Jedi { constructor() {} getName() { return this.name; } } // bad class Rey extends Jedi { constructor(...args) { super(...args); } } // good class Rey extends Jedi { constructor(...args) { super(...args); this.name = 'Rey'; } } تجنب تكرار عناصر الصنف. لماذا؟ في حال التصريح المكرر لعنصر من عناصر الصنف، فالقيمة الأخيرة فقط هي التي ستُعتبر – وجود التكرارات يعني بالضرورة أن هناك مشكلة في الكود. // bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } } الوحدات Modules دائمًا استخدم (import/export) لأجل الوحدات بدلًا من الطرق الأخرى غير القياسية. يمكنك الترجمة transpile دائمًا نحو نظام الوحدات المفضل لديك. لماذا؟ الوحدات هي المستقبل، دعونا نُدشن المستقبل منذ الآن. // bad const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // ok import AirbnbStyleGuide from './AirbnbStyleGuide'; export default AirbnbStyleGuide.es6; // best import { es6 } from './AirbnbStyleGuide'; export default es6; لا تستخدم الاستيراد الشامل wildcard imports. لماذا؟ لتتأكد من أنّ لديك تصديرًا افتراضيّا واحدًا. // bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide'; لا تُصدّر مباشرةً من الاستيراد. لماذا؟ على الرغم من أن الكتابة في سطر واحد تكون أوجز، وجود طريقة واضحة واحدة للاستيراد وأخرى للتصدير يجعل الأمور متسقةً. // bad // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6; استورد من المسار path في مكان واحد فقط. لماذا؟ وجود عدة أسطر تستورد من نفس المسار يمكن أن يجعل الكود أقل قابلية للصيانة. // bad import foo from 'foo'; // … some other imports … // import { named1, named2 } from 'foo'; // good import foo, { named1, named2 } from 'foo'; // good import foo, { named1, named2, } from 'foo'; لا تُصدر الارتباطات المتحوّلة mutable bindings. لماذا؟ العناصر المتحولة يُفضل تجنبها بشكل عام، ولكن على وجه الخصوص عند تصدير الارتباطات المتحولة. في حين أن هذا الأسلوب قد يكون ضروريّا في بعض الحالات الخاصة، فبشكل عام، يجب ألّا تُصدرَ إلا المراجع الثابتة. // bad let foo = 3; export { foo }; // good const foo = 3; export { foo }; في الوحدات التي فيها تصدير واحد فقط، يفضل استخدام التصدير الافتراضي بدلًا من التصدير المسمى named export. لماذا؟ لتشجيع استخدام الملفات التي لا تصدر إلًا شيءً واحدًا فقط، لأن ذلك أفضل وأسهل للقراءة والصيانة. // bad export function foo() {} // good export default function foo() {} ضع كل العبارات import فوق عبارات الاستيراد الأخرى. لماذا؟ لأن import يتم ترفيعهاhoisted ، فوضعها كلها في الجزء العلوي يمنع أي سلوك غير متوقع. // bad import foo from 'foo'; foo.init(); import bar from 'bar'; // good import foo from 'foo'; import bar from 'bar'; foo.init(); الاستيراد المتعدد يجب أن توضع قبله مسافة بادئة مثله مثل الصفوف متعددة الأسطر والتصريح الحرفي للكائنات object literals. لماذا؟ اللامّتان تتبعان نفس قواعد المسافة البادئة كما هو الحال في كل كتل اللامّات في هذا الدليل، وكذلك مثل الفواصل الزائدة trailing commas. // bad import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // good import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path'; تجنب تعابير مُحمّلات الحُزم Webpack loader في عبارات import. لماذا؟ بما أن مُحمّلات الحزم تستخدم معدات الوحدات module bundler. فمن الأفضل استخدام عبارة المُحمّل في webpack.config.js. // bad import fooSass from 'css!sass!foo.scss'; import barCss from 'style!css!bar.css'; // good import fooSass from 'foo.scss'; import barCss from 'bar.css'; المُكرّرات والمولّدات لا تستخدم المكررات. من الأفضل استخدام دوال الدرجات العليا higher-order functions لجافا سكريبت بدلًا من الحلقات مثلfor-in أو for-of. لماذا؟ هذا يتماشى مع قاعدة التحول immutable rule الخاصة بنا. التعامل مع الدوال التي تُرجع قيمًا أسهل للفهم مقارنة بالدوال ذات الآثار الجانبية. استخدم ()map / every() / filter() / find() / findIndex() / reduce() / some / للتمرير iterate على الصفوف، و ()Object.keys / Object.values() / Object.entries لإنتاج صف حتى تتمكن من التمرير على عناصره. const numbers = [1, 2, 3, 4, 5]; // bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // good let sum = 0; numbers.forEach((num) => { sum += num; }); sum === 15; // best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; // bad const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // good const increasedByOne = []; numbers.forEach((num) => { increasedByOne.push(num + 1); }); // best (keeping it functional) const increasedByOne = numbers.map(num => num + 1); لا تستخدم المولدات في الوقت الراهن. لماذا؟ لأنها لا تُترجم transpile جيدًا في ES5. إذا كان عليك استخدام المُولدات، أو إذا أردت تجاهل نصيحتنا، تأكد من أن تواقيع دوالها متباعدة بشكل صحيح. لماذا؟ function و * هما كلمتان مفتاحيتان من نفس المفهوم conceptual keyword – * ليست تعديلًا لـ function و function* مختلفة عن function. // bad function * foo() { // ... } // bad const bar = function * () { // ... }; // bad const baz = function *() { // ... }; // bad const quux = function*() { // ... }; // bad function*foo() { // ... } // bad function *foo() { // ... } // very bad function * foo() { // ... } // very bad const wat = function * () { // ... }; // good function* foo() { // ... } // good const foo = function* () { // ... }; الخصائص استخدم أسلوب النقطة dot notation عند الدخول إلى الخصائص. const luke = { jedi: true, age: 28, }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi; استخدام المعقوفتين [] عند الدخول إلى الخصائص بواسطة متغير. const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi'); استخدام العامل الأسي ** عند حساب الأسّ. // bad const binary = Math.pow(2, 10); // good const binary = 2 ** 10; المتغيرات استخدم دومًا const أو let لتعريف المتغيرات. عدم القيام بذلك سيؤدي إلى إنشاء متغيرات كلية global variables. ونحن نريد تجنب تلويث مجال الأسماء ((namespace العام. // bad superPower = new SuperPower(); // good const superPower = new SuperPower(); استخدم const أو let لكل متغير على حدة. لماذا؟ من الأسهل إضافة متغيرات جديدة بهذه الطريقة، ولن تكون مضطرا لتحريك ; أو,. وسيسهل أيضًا استخدام المنقح debugger على كل واحد منهم على حدة، بدلًا من القفز عليها كلها في وقت واحد. // bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z'; قم بتجميع كل التعابير const ثم بعدها جمّع التعابير let. لماذا؟ سيكون هذا مفيدًا لاحقًا عندما تحتاج إلى تعيين متغير اعتمادًا على متغير معيّن سابقًا. // bad let i, len, dragonball, items = getItems(), goSportsTeam = true; // bad let i; const items = getItems(); let dragonball; const goSportsTeam = true; let len; // good const goSportsTeam = true; const items = getItems(); let dragonball; let i; let length; عيّن المتغيرات حيث ما أردت، ولكن ضعها في مكان مناسب. لماذا؟ let و const كتلية المدى block scoped وليست دوالية المدى function scoped. // bad - unnecessary function call function checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name; } // good function checkName(hasName) { if (hasName === 'test') { return false; } const name = getName(); if (name === 'test') { this.setName(''); return false; } return name; } تجنب الإحالات المتسلسلة للمتغيرات chain variable assignments. لماذا؟ الإحالات المتسلسة للمتغيرات تُنشئ متغيرات كلية. // bad (function example() { // JavaScript interprets this as // let a = ( b = ( c = 1 ) ); // The let keyword only applies to variable a; variables b and c become // global variables. let a = b = c = 1; }()); console.log(a); // throws ReferenceError console.log(b); // 1 console.log(c); // 1 // good (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // throws ReferenceError console.log(b); // throws ReferenceError console.log(c); // throws ReferenceError // the same applies for `const` تجنب استخدام عمليات الزيادة والإنقاص الأحادية (++, --). لماذا؟ في وثائق eslint، عمليات الزيادة والإنقاص الأحادية تَخضع لقاعدة الإدراج التلقائي للفواصل المنقوطة، ويمكن أن تتسبب في حدوث أخطاء صامتة مع زيادة أو إنقاص قيمة ضمن التطبيق. كما أنه من الأفضل استخدام num += 1 بدلًا من num++ أو num--.تجنُّب استخدام عامل الزيادة والإنقاص الأحادي سيجنبك زيادة قيمة أو إنقاصها بشكل غير مقصود وهو ما يمكن أيضًا أن يسبب سلوكًا غير متوقع في برامجك. // bad const array = [1, 2, 3]; let num = 1; num++; --num; let sum = 0; let truthyCount = 0; for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } } // good const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; const sum = array.reduce((a, b) => a + b, 0); const truthyCount = array.filter(Boolean).length; التّرفيع Hoisting يتم ترفيع عبارات التصريح var إلى الأعلى داخل مدى الدالة المحتوية، ولكنّ إحالة قيم على تلك المتغيرات لا يُرفّع. const و let لديهما ميزة جديدة تُسمى Temporal Dead Zones (TDZ). لذلك من المهم أن تعرف لماذا استخدامtypeof لم يعد آمنًا. // we know this wouldn’t work (assuming there // is no notDefined global variable) function example() { console.log(notDefined); // => throws a ReferenceError } // creating a variable declaration after you // reference the variable will work due to // variable hoisting. Note: the assignment // value of `true` is not hoisted. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // the interpreter is hoisting the variable // declaration to the top of the scope, // which means our example could be rewritten as: function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; } // using const and let function example() { console.log(declaredButNotAssigned); // => throws a ReferenceError console.log(typeof declaredButNotAssigned); // => throws a ReferenceError const declaredButNotAssigned = true; } الدوال المجهولة يُرفّع اسم المتغير الخاص بها، بخلاف إحالة الدالة. function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { console.log('anonymous function expression'); }; } الدوال المُسماة يُرفّع اسم المتغير الخاص بها، بخلاف الدالة أو متنها. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } // the same is true when the function name // is the same as the variable name. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); }; } تقوم تصريحات الدوال بترفيع اسم ومتن الدالة. function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } } عمليات المقارنة والتساوي استخدم === و !== بدلًا من == و !=. التعليمات الشرطية مثل العبارة if تُقيّم التعبيرات عن طريق التحويلcoercion بواسطة الدالة المجرّدة ToBoolean، ودائمًا تتبع القواعد البسيطة التالية. Objects يتم إعطاؤها القيمة true Undefined يتم إعطاؤه القيمة false Null يتم إعطاؤه القيمة false Booleans يتم إعطاؤه قيمة العنصر Boolean Numbers يتم إعطاؤه القيمة false إن كان يساوي 0+ أو 0- أو NaN وإلا يُعطى القيمة true Strings يتم إعطاؤها القيمة false إن كانت تساوي "" وإلا تٌعطى القيمة true if ([0] && []) { // true // an array (even an empty one) is an object, objects will evaluate to true } استخدم الاختصارات من أجل القيم المنطقية، بالمقابل استخدم المقارنات الصريحة للنصوص والأرقام. // bad if (isValid === true) { // ... } // good if (isValid) { // ... } // bad if (name) { // ... } // good if (name !== '') { // ... } // bad if (collection.length) { // ... } // good if (collection.length > 0) { // ... } استخدم اللامّات {} لإنشاء الكتل في البنود case و default التي تحتوي عبارات التصريح declarations (على سبيل المثال let، const ، function ، وclass). لماذا؟ عبارات التصريح مرئية في كامل الكتلة switch ولكن لا يتم تهيئتها إلّا عندما يتم إحالة قيمة عليها، وهو ما لا يحدث إلا عند بلوغ case. هذا سيسبب مشاكل عندما تحاول أكثر من case واحدة تعريف الشيء نفسه. // bad switch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // ... } break; default: class C {} } // good switch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // ... } break; } case 4: bar(); break; default: { class C {} } } لا ينبغي أن تتداخل التعابير الثلاثية Ternaries وعادةً ينبغي أن تكون على سطر واحد // bad const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null; // split into 2 separated ternary expressions const maybeNull = value1 > value2 ? 'baz' : null; // better const foo = maybe1 > maybe2 ? 'bar' : maybeNull; // best const foo = maybe1 > maybe2 ? 'bar' : maybeNull; تجنب استخدام التعابير الثلاثية التي لا لزوم لها. // bad const foo = a ? a : b; const bar = c ? true : false; const baz = c ? false : true; // good const foo = a || b; const bar = !!c; const baz = !c; عند مزج العمليات، استخدم الأقواس. والاستثناء الوحيد هي العمليات الحسابية القياسية (+، -، *، /) بما أنّ الأسبقية فيها مفهومة بشكل جيد. لماذا؟ هذا يحسّن المقروئية. // bad const foo = a && b < 0 || c > 0 || d + 1 === 0; // bad const bar = a ** b - 5 % d; // bad // one may be confused into thinking (a || b) && c if (a || b && c) { return d; } // good const foo = (a && b < 0) || c > 0 || (d + 1 === 0); // good const bar = (a ** b) - (5 % d); // good if (a || (b && c)) { return d; } // good const bar = a + b / c * d; الكتل Blocks استخدم اللامّات في حال الكتل متعدد الأسطر. // bad if (test) return false; // good if (test) return false; // good if (test) { return false; } // bad function foo() { return false; } // good function bar() { return false; } إذا كنت تستخدم الكتل متعددة الأسطر مع if و else ، ضع else على نفس السطر الخاص باللّامة التي تغلق كتلة if. // bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); } إذا كانت كتلة if تُرجع دائمًا (أي تنتهي بـ return)، فكتلة else لن تكون ضرورية. عبارة return في كتلة else if موالية لكتلة if والتي تحتوي على return يمكن تقسيمها إلى عدة كتل if. // bad function foo() { if (x) { return x; } else { return y; } } // bad function cats() { if (x) { return x; } else if (y) { return y; } } // bad function dogs() { if (x) { return x; } else { if (y) { return y; } } } // good function foo() { if (x) { return x; } return y; } // good function cats() { if (x) { return x; } if (y) { return y; } } //good function dogs(x) { if (x) { if (z) { return y; } } else { return z; } } عبارات التحكم Control Statements في حال تجاوزت عبارات التحكم (if, while …) الحد الأقصى لطول السطر، فيمكن وضع كل شرط في سطر جديد. كما يجب أن يكون العامل المنطقي في بداية السطر. ● لماذا؟ وضع العوامل operators في بداية السطر يحافظ على محاذاة العوامل ويتبع نمطًا مماثلا لتسلسل الوظائف method chaining. كما يحسن القراءة من خلال توضيح العمليات المنطقية المعقدة. // bad if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { thing1(); } // bad if (foo === 123 && bar === 'abc') { thing1(); } // bad if (foo === 123 && bar === 'abc') { thing1(); } // bad if ( foo === 123 && bar === 'abc' ) { thing1(); } // good if ( foo === 123 && bar === 'abc' ) { thing1(); } // good if ( (foo === 123 || bar === "abc") && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening() ) { thing1(); } // good if (foo === 123 && bar === 'abc') { thing1(); } التعليقات استخدم /** ... */ لأجل التعاليق متعدد الأسطر. // bad // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) { // ... return element; } // good /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; } استخدم // لأجل التعاليق ذات السطر الواحد. ضع التعاليق ذات السطر الواحد على سطر جديد فوق التعليمة البرمجية موضوع التعليق. وضع سطرًا فارغًا قبل التعليق إلا إذا كان على السطر الأول من كتلة block. // bad const active = true; // is current tab // good // is current tab const active = true; // bad function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // good function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // also good function getType() { // set the default type to 'no type' const type = this.type || 'no type'; return type; } ابدأ كل التعليقات بمسافة لجعلها أسهل للقراءة. // bad //is current tab const active = true; // good // is current tab const active = true; // bad /** *make() returns a new element *based on the passed-in tag name */ function make(tag) { // ... return element; } // good /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; } ابدأ التعليقات التي تهدف إلى لفت انتباه المطورين الآخرين إلى أنّ هناك مشكلةً تحتاج إلى المراجعة بـ FIXME أو TODO، أو إذا كنت تقترح حلّا لتنفيذه. تختلف هذه عن التعليقات العادية لأنها قابلة للتنفيذ. الإجراءات قد تكون: FIXME: -- need to figure this out أو TODO: -- need to implement استخدم // FIXME: للإشارة إلى مشكلة. class Calculator extends Abacus { constructor() { super(); // FIXME: shouldn’t use a global here total = 0; } } استخدم // TODO: لاقتراح حل لمشكلة. class Calculator extends Abacus { constructor() { super(); // TODO: total should be configurable by an options param this.total = 0; } } المسافات Whitespace استخدم مسافتين فارغتين space character لتعيين المسافات. // bad function foo() { ∙∙∙∙let name; } // bad function bar() { ∙let name; } // good function baz() { ∙∙let name; } ضع مسافة قبل اللامّة الأولى. // bad function test(){ console.log('test'); } // good function test() { console.log('test'); } // bad dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog', }); // good dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog', }); ضع مسافةً قبل القوس الفاتح في عبارات التحكم (if, while …). لا تضع أي مسافات في لائحة المعاملاتarguments واسم الدالة عند استدعاء الدالة أو تعريفها. // bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ('Swooosh!'); } // good function fight() { console.log('Swooosh!'); } ضع مسافات بين العوامل operators. // bad const x=y+5; // good const x = y + 5; أنهِ الملفات بحرف الرجوع إلى السطر. // bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6; // bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ↵ // good import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ استخدم المسافة البادئة indentation عند استخدام سلسة طويلة من الوظائف (أكثر من اثنتين). استخدم نقطة في البداية، لكي تبين أنّ السطر هو استدعاء لوظيفة، وليس تعليمةً جديدةً. // bad $('#items').find('.selected').highlight().end().find('.open').updateCount(); // bad $('#items'). find('.selected'). highlight(). end(). find('.open'). updateCount(); // good $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount(); // bad const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // good const leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', `translate(${radius + margin},${radius + margin})`) .call(tron.led); // good const leds = stage.selectAll('.led').data(data); ضع سطرًا فارغًا بعد الكتل وقبل العبارة الموالية. // bad if (foo) { return bar; } return baz; // good if (foo) { return bar; } return baz; // bad const obj = { foo() { }, bar() { }, }; return obj; // good const obj = { foo() { }, bar() { }, }; return obj; // bad const arr = [ function foo() { }, function bar() { }, ]; return arr; // good const arr = [ function foo() { }, function bar() { }, ]; return arr; لا تبدأ الكتل بأسطر فارغة. // bad function bar() { console.log(foo); } // bad if (baz) { console.log(qux); } else { console.log(foo); } // bad class Foo { constructor(bar) { this.bar = bar; } } // good function bar() { console.log(foo); } // good if (baz) { console.log(qux); } else { console.log(foo); } لا تقم بإضافة مساحات داخل الأقواس. // bad function bar( foo ) { return foo; } // good function bar(foo) { return foo; } // bad if ( foo ) { console.log(foo); } // good if (foo) { console.log(foo); } لا تقم بإضافة مساحات داخل المعقوفات. // bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good const foo = [1, 2, 3]; console.log(foo[0]); قم بإضافة مساحات داخل اللامّات. // bad const foo = {clark: 'kent'}; // good const foo = { clark: 'kent' }; تجنب الأسطر التي يتجاوز طولها 100 حرف (بما في ذلك المسافات). النصوص الطويلة مستثناة من هذه القاعدة، وينبغي ألّا تُفكك. لماذا؟ هذا يضمن سهولة القراءة والصيانة. // bad const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // bad $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // good const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // good $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.')); الفواصل تجنب الفواصل في البداية. // bad const story = [ once , upon , aTime ]; // good const story = [ once, upon, aTime, ]; // bad const hero = { firstName: 'Ada' , lastName: 'Lovelace' , birthYear: 1815 , superPower: 'computers' }; // good const hero = { firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers', }; استخدم فاصلة زائدة إضافية. لماذا؟ هذا يؤدي إلى توضيح الاختلافات بين الأكواد فيgit . أيضًا، المترجمات من أمثال Babel ستزيل الفاصلة الزائدة الإضافية في الكود المترجم، ممّا يعني أنه لا داعي للقلق من مشكلة الفاصلة الزائدة في المتصفحات القديمة. // bad - git diff without trailing comma const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // good - git diff with trailing comma const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], }; // bad const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ]; // good const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // bad function createHero( firstName, lastName, inventorOf ) { // does nothing } // good function createHero( firstName, lastName, inventorOf, ) { // does nothing } // good (note that a comma must not appear after a "rest" element) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // bad createHero( firstName, lastName, inventorOf ); // good createHero( firstName, lastName, inventorOf, ); // good (note that a comma must not appear after a "rest" element) createHero( firstName, lastName, inventorOf, ...heroArgs ); الفاصلة المنقوطة Semicolons نعم لماذا؟ عندما يصادف جافا سكريبت فاصل أسطر line break دون فاصلة منقوطة، فإنه يستخدم مجموعة من القواعد تسمى الإدراج التلقائي للفاصلة المنقوطة (Automatic Semicolon Insertion) لتحديد ما إذا كان يجب اعتبار نهاية السطر كنهاية للتعليمة البرمجية، (كما يوحي الاسم) يقوم جافا سكريبت بوضع فاصلة منقوطة إلى الكود قبل نهاية السطر إذا اعتقد أن التعليمة قد انتهت. الإدراج التلقائي للفاصلة المنقوطة ترافقه بعض السلوكيات الشاذة، وقد يعطب الكود إذا أساء جافا سكريبت تأويل نهاية السطر. ستصبح هذه القواعد أكثر تعقيدًا عندما تضاف الميزات الجديدة إلى جافا سكريبت. قم بإنهاء التعليمات البرمجية بشكل واضح وقم بإعداد أداة linter لتحديد الفواصل المنقوطة المفقودة لتجنب هذه المشاكل. // bad - raises exception const luke = {} const leia = {} [luke, leia].forEach(jedi => jedi.father = 'vader') // bad - raises exception const reaction = "No! That's impossible!" (async function meanwhileOnTheFalcon(){ // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()) // bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI! function foo() { return 'search your feelings, you know it to be foo' } // good const luke = {}; const leia = {}; [luke, leia].forEach((jedi) => { jedi.father = 'vader'; }); // good const reaction = "No! That's impossible!"; (async function meanwhileOnTheFalcon(){ // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()); // good function foo() { return 'search your feelings, you know it to be foo'; } تحويل الأنواع Type Casting & Coercion قم بتحويل الأنواع في بداية التعليمة. النصوص // => this.reviewScore = 9; // bad const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string" // bad const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() // bad const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string // good const totalScore = String(this.reviewScore); بالنسبة للأعداد: استخدم Number لأجل التحويل type casting، أما لتحليل النصوص فاستخدم parseInt دائمًا مع أساس radix . const inputValue = '4'; // bad const val = new Number(inputValue); // bad const val = +inputValue; // bad const val = inputValue >> 0; // bad const val = parseInt(inputValue); // good const val = Number(inputValue); // good const val = parseInt(inputValue, 10); إذا كنت مضطرّا لاستعمال parseInt واحتجت إلى استخدام Bitshiftلأسباب تتعلق بالأداء، فضع تعليقًا يشرح ما الذي تفعله ولماذا. // good /** * parseInt was the reason my code was slow. * Bitshifting the String to coerce it to a * Number made it a lot faster. */ const val = inputValue >> 0; كن حذرًا عند استخدام عمليات الإزاحة bitshift ، فالأعداد مُمثلة ب 64 بتّة. ولكنّ عمليات الإزاحة دائما تُرجع أعدادًا ممثلة ب 32 بتة. الإزاحة يمكن أن تؤدي إلى نتائج غير متوقعة عند استخدام قيم عددية أكبر من 32 بتة. أكبر قيمة للأعداد ذات الإشارة signed numbers الممثلة على 32 بتة هو 2,147,483,647: 2147483647 >> 0; // => 2147483647 2147483648 >> 0; // => -2147483648 2147483649 >> 0; // => -2147483647 القيم المنطقية Booleans: const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // best const hasAge = !!age; اصطلاحات التسمية تجنب الأسماء المكونة من حرف واحد. استخدم أسماءً معبّرة. // bad function q() { // ... } // good function query() { // ... } استخدم أسلوب التسمية camelCase عند تسمية الأشياء، الدوال، والعينات instances. // bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {} لا تستخدم أسلوب التسمية PascalCase إلا عند تسمية المُنشئات أو الأصناف. // bad function user(options) { this.name = options.name; } const bad = new user({ name: 'nope', }); // good class User { constructor(options) { this.name = options.name; } } const good = new User({ name: 'yup', }); لا تستخدم العارضة السفلية في البداية أو النهاية. لماذا؟ لا يوجد في جافا سكريبت مفهوم الخصوصية بالنسبة للخصائص أو الوظائف. على الرغم من أن وضع العارضة السفلية في البداية يعتبره الكثيرون اصطلاحًا لـ “خاص”، إلّا أن هذه الخصائص عامة كلها، وعلى هذا النحو، فهي جزء من الواجهة البرمجية API العامة. هذا الاصطلاح قد يؤدي بالمطورين للاعتقاد خطأً بأن التغيير لن يُعتبر عطبًا في الكود البرمجي، أو أنه ليست هناك حاجة للاختبار. إن بدى لك هذا طويلًا، فتذكر هذه الجملة: إذا كنت تريد لشيء أن يكون “خاصّا”، فيجب ألا تضعه في مكان ملحوظ. // bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // good this.firstName = 'Panda'; لا تحفظ مرجعًا لـ this. واستخدم الدوال السهمية أو Function#bind . // bad function foo() { const self = this; return function () { console.log(self); }; } // bad function foo() { const that = this; return function () { console.log(that); }; } // good function foo() { return () => { console.log(this); }; } اسم الملف الأساسي base filename يجب أن يتطابق تمامًا مع اسم التصدير الافتراضي. // file 1 contents class CheckBox { // ... } export default CheckBox; // file 2 contents export default function fortyTwo() { return 42; } // file 3 contents export default function insideDirectory() {} // in some other file // bad import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export // bad import CheckBox from './check_box'; // PascalCase import/export, snake_case filename import forty_two from './forty_two'; // snake_case import/filename, camelCase export import inside_directory from './inside_directory'; // snake_case import, camelCase export import index from './inside_directory/index'; // requiring the index file explicitly import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly // good import CheckBox from './CheckBox'; // PascalCase export/import/filename import fortyTwo from './fortyTwo'; // camelCase export/import/filename import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" // ^ supports both insideDirectory.js and insideDirectory/index.js استخدم أسلوب التسمية camelCase عندما تقوم بالتصدير الافتراضي لدالة. يجب أن يكون اسم الملف الخاص بك مطابقًا لاسم دالتك. function makeStyleGuide() { // ... } export default makeStyleGuide; استخدم أسلوب التسمية PascalCase عندما تصدّر منشئ / صنف / فردsingleton / مكتبة دوال / أو كائن عارٍ bare object. const AirbnbStyleGuide = { es6: { }, }; export default AirbnbStyleGuide; المختصرات والكلمات المنحوتة initialisms ينبغي أن تُكتب كلها بأحرف كبيرة أو صغيرة. لماذا؟ الأسماء ينبغي أن تكون واضحة للإنسان، وليس لاسترضاء خوارزميات الكمبيوتر. // bad import SmsContainer from './containers/SmsContainer'; // bad const HttpRequests = [ // ... ]; // good import SMSContainer from './containers/SMSContainer'; // good const HTTPRequests = [ // ... ]; // also good const httpRequests = [ // ... ]; // best import TextMessageContainer from './containers/TextMessageContainer'; // best const requests = [ // ... ]; دالّات الدخول Accessors دالّات الدخول ليست ضرورية للخصائص. لا تستخدم الدوال getters أو setters لأنّ لها آثارًا جانبيةً غير متوقعة، ويصعب اختبارها وصيانتها والتعامل معها. بدلًا من ذلك، إذا كنت تريد استخدام دوال الدخول، فاستخدم ()getVal و(’setVal(‘hello . // bad class Dragon { get age() { // ... } set age(value) { // ... } } // good class Dragon { getAge() { // ... } setAge(value) { // ... } } إذا كانت قيمة الخاصية / الوظيفة boolean، استخدم()isVal أو ()hasVal. // bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; } لا ضير في إنشاء دوال ()get و ()set، ولكن يجب أن تكون متسقة. class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } } الأحداث عند ربط حمولات البيانات بالأحداث (مثل أحداث DOM أو غيرها، مثل أحداث Backbone)، قم بتمرير كائن حرفي object literal (معروف أيضًا باسم “hash”) بدلًا من قيمة خام raw value. هذا سيسمح لاحقًا بإضافة المزيد من البيانات إلى حَمولة الحدث دون الحاجة إلى إيجاد وتحديث كل معالجات الحدث. على سبيل المثال، بدلًا من: // bad $(this).trigger('listingUpdated', listing.id); // ... $(this).on('listingUpdated', (e, listingID) => { // do something with listingID }); من الأفضل استخدام: // good $(this).trigger('listingUpdated', { listingID: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { // do something with data.listingID }); jQuery ضع السابقة $ قبل متغيراتjQuery . // bad const sidebar = $('.sidebar'); // good const $sidebar = $('.sidebar'); // good const $sidebarBtn = $('.sidebar-btn'); خزّن عمليات البحث المؤقت لـ jQuery. // bad function setSidebar() { $('.sidebar').hide(); // ... $('.sidebar').css({ 'background-color': 'pink', }); } // good function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ... $sidebar.css({ 'background-color': 'pink', }); } بالنسبة لاستعلامات DOM استخدم $('.sidebar ul') أو parent > child $('.sidebar > ul'). استخدم find مع استعلامات jQuery المحدّدة scoped jQuery object queries)). // bad $('ul', '.sidebar').hide(); // bad $('.sidebar').find('ul').hide(); // good $('.sidebar ul').hide(); // good $('.sidebar > ul').hide(); // good $sidebar.find('ul').hide(); المكتبة القياسية المكتبة القياسية تحتوي بعض الأدوات المساعدة التي لم تعد تُستخدم ولكن يتم الإبقاء عليها لأجل المتصفحات القديمة. استخدم Number.isNaN بدلًا من isNaN. لماذا؟ isNaN تحوّل القيم غير العددية إلى أعداد، وتُرجع القيمة true لأي شيء يتحوّل إلى NaN. إذا كان هذا ما تريد، فاجعله صريحًا إذًا. // bad isNaN('1.2'); // false isNaN('1.2.3'); // true // good Number.isNaN('1.2.3'); // false Number.isNaN(Number('1.2.3')); // true استخدم Number.isFinite بدلًا من isFinite. لماذا؟ isFinite تحوّل القيم غير العددية إلى أعداد، وتُرجع القيمة true لأي شيء يتحوّل إلى عدد منتهٍ. إذا كان هذا ما تريد، فاجعله صريحًا إذًا. // bad isFinite('2e3'); // true // good Number.isFinite('2e3'); // false Number.isFinite(parseInt('2e3', 10)); // true الاختبار نعم function foo() { return true; } مهما كان إطار الاختبار الذي تستخدمه، يجب أن تكتب الاختبارات بنفسك! احرص على كتابة العديد من الدوال البسيطة والصغيرة، وقلّل من استخدام البيانات المتحوّلةmutations . كن حذرًا عند استخدام أصناف stubs و mocks – لأنها يمكن أن تجعل اختباراتك أكثر هشاشة. محاولة اختبار 100٪ من الكود هو هدف جيد، حتى لو لم يكن دائمًا عمليًّا. كلما أصلحت خللًا، قم بكتابة اختبار تقييم regression test. فبدونه من المؤكد أنّ الثغرات ستعود مجدّدًا. ترجمة -وبتصرّف- للمقال Airbnb JavaScript Style Guide