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



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

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

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

نوع المُحتوى


التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

أسئلة وأجوبة

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

التصنيفات

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

تمّ العثور على 4 نتائج

  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
  2. كوتلن (Kotlin) هي لغة برمجة مخصّصة لمنصّة جافا الافتراضية (Java Virtual Machine أو اختصارًا JVM)، الأندرويد، والمتّصفح. تنتمي للغات statically typed (التي تفحص الأنواع وقت الترجمة). وهي متوافقة مع جافا 100%. // (//)التعليقات على سطر واحد تبدأ بـ/* التعليقات المؤلفة من عدة أسطر تبدو كهذه */ تعمل الكلمة المفتاحية package بنفس طريقة جافا package com.learnxinyminutes.kotlin نقطة الإدخال لبرامج Kotlin هي دالة(تابع) تسمى main ،يمرر التّابع مصفوفة تحتوي على وسطاء arguments لسطر الأوامر. fun main(args: Array<String>) { التّصريح عن القيم يتم باستخدام إما var أو val ،تصريح val لا يمكن إعادة تعيينه، في حين يمكن ذلك في var val fooVal =10 لا يمكننا لاحقًا إعادة تعيين قيمة fooVal لقيمة أخرى. var fooVar =10 fooVar =20 من الممكن إعادة تعيين قيمة fooVar. في أغلب الحالات، يمكن لكوتلن أن تحدّد ما هو نوع المتغير، لذلك لا داعي لتحديده صراحة في كل مرّة. يمكننا أن نصرّح بنوع المتغير بوضوح كالتالي: val foo:Int=7 يمكن تمثيل السلاسل بطريقة مماثلة في java. يتم الهروب باستخدام backslash. val fooString ="My String Is Here!" val barString ="Printing on a new line?\nNo Problem!" val bazString ="Do you want to add a tab?\tNo Problem!" println(fooString) println(barString) println(bazString) تُحدد السلسلة الخام string raw باستخدام triple quote ("""). ويمكن أن تحتوي أسطر جديدة و أية محارف أخرى. val fooRawString =""" fun helloWorld(val name :String){ println("Hello, world!")}""" println(fooRawString) السلاسل ممكن أن تحتوي تعابير القالب template expressions. تبدأ تعابير القالب بالرمز $. val fooTemplateString ="$fooString has ${fooString.length} characters" println(fooTemplateString)// => My String Is Here! has 18 characters من أجل المتغيرات التي تحوي قيمة فارغة null يجب تحديد ذلك صراحة nullable. يمكن تحديد المتغير قابلاً للقيمة null بإلحاق? بنوعه. ويمكننا الوصول إلى المتحولات القابلة لـ null باستخدام مُعامل التشغيل? ،ويمكننا استخدام عامل التشغيل :? لتحديد قيمة بديلة للاستخدام إذا كان المتغير فارغ null. var fooNullable:String?="abc" println(fooNullable?.length)// => 3 println(fooNullable?.length ?:-1)// => 3 fooNullable = null println(fooNullable?.length)// => null println(fooNullable?.length ?:-1)// => -1 يمكن التصريح عن الدوال باستخدام الكلمة المفتاحية fun. وتحدد وسطاء الدالة بين قوسين بعد اسم الدالة. ويمكن لوسطاء الدالة اختياريًا الاحتواء على قيمة افتراضية. ويحدّد نوع إرجاع الدالة، إذا لزم الامر بعد المعطيات. fun hello(name:String="world"):String{return"Hello, $name!"} println(hello("foo"))// => Hello, foo! println(hello(name ="bar"))// => Hello, bar! println(hello())// => Hello, world! يمكن لبارامتر الدالة أن يوسم بالكلمة المفتاحية vararg للسماح بتمرير عدد متغير من المعطيات إلى الدالة. fun varargExample(vararg names:Int){ println("Argument has ${names.size} elements")} varargExample()// => لا يحوي الوسيط أية عناصر varargExample(1)// => يحوي الوسيط عنصر واحد varargExample(1,2,3)// => يحوي الوسيط 3 عناصر عندما تتكوّن الدّالة من تعبير واحد فقط، يمكن حذف الأقواس المنحنية { } ويتم تحديد الجسم(العملية) بعد رمز = fun odd(x:Int):Boolean= x %2==1 println(odd(6))// => false println(odd(7))// => true إذا كان نوع الاسترجاع يمكن استنتاجه فلسنا بحاجة لتحديده. fun even(x:Int)= x %2==0 println(even(6))// => true println(even(7))// => false الدوال يمكن أن تأخذ دوال كوسطاء وترجع دالة. fun not(f:(Int)->Boolean):(Int)->Boolean{return{n ->!f.invoke(n)}} يمكن للدوال المسمّاة أن تحدّد كوسائط باستخدام معامل التشغيل val notOdd = not(::odd) val notEven = not(::even) يمكن تحديد تعابير Lambda كوسائط val notZero = not {n -> n ==0} إذا احتوت lambda على بارامتر واحد فقط يمكن حذف التصريح عنه (جنبًا إلى جنب مع<- )وسيكون اسم البارامتر الوحيد it. val notPositive = not {it >0}for(i in 0..4){ println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")} تستخدم الكلمة المفتاحية class للتّصريح عن الأصناف. classExampleClass(val x:Int){ fun memberFunction(y:Int):Int{return x + y } infix fun infixMemberFunction(y:Int):Int{return x * y }} لإنشاء حالة instance جديدة نستدعي الباني. ولاحظ أن Kotlin لا تحوي الكلمة المفتاحية new. val fooExampleClass =ExampleClass(7) يمكن استدعاء دوال المستخدم باستخدام التنويت النقطي . println(fooExampleClass.memberFunction(4))// => 11 إذا وُسمت الدالة بالكلمة المفتاحية infix يمكن عندها أن يستدعى باستخدام التنويت الداخلي println(fooExampleClass infixMemberFunction 4)// => 28 أصناف البيانات Data classes هي طريقة مختصرة لإنشاء الأصناف التي تحتوي بيانات فقط وتنشئ دوال hashCode،equals و toString تلقائيًا. data classDataClassExample(val x:Int, val y:Int, val z:Int) val fooData =DataClassExample(1,2,4) println(fooData)// => DataClassExample(x=1, y=2, z=4) أصناف البيانات لديها دالة copy val fooCopy = fooData.copy(y =100) println(fooCopy)// => DataClassExample(x=1, y=100, z=4) يمكن أن تفكَك الكائنات Objects في متغيرات متعددة val (a, b, c)= fooCopy println("$a $b $c")// => 1 100 4 التفكيك باستخدام حلقة for for((a, b, c) in listOf(fooData)){ println("$a $b $c")// => 1 100 4} val mapData = mapOf("a" to 1,"b" to 2) Map.Entry قابل للتفكيك كذلك for((key, value) in mapData){ println("$key -> $value")} الدالة with مشابهة لعبارة with في جافا data classMutableDataClassExample(var x:Int, var y:Int, var z:Int) val fooMutableData =MutableDataClassExample(7,4,9) with (fooMutableData){ x -=2 y +=2 z--} println(fooMutableData)// => MutableDataClassExample(x=5, y=6, z=8) يمكننا إنشاء قائمة (لائحة) باستخدام الدالة listOf. ستكون القائمة غير قابلة للتغيير ولا يمكن إضافة عناصر أو إزالتها. val fooList = listOf("a","b","c") println(fooList.size)// => 3 println(fooList.first())// => a println(fooList.last())// => c// index يمكن الوصول إلى عناصر القائمة من خلال فهرسها println(fooList[1])// => b يمكن إنشاء قائمة قابلة للتعديل باستخدام الدالة mutableListOf val fooMutableList = mutableListOf("a","b","c") fooMutableList.add("d") println(fooMutableList.last())// => d println(fooMutableList.size)// => 4 يمكن إنشاء تعيين set باستخدام الدّالة setOf val fooSet = setOf("a","b","c") println(fooSet.contains("a"))// => true println(fooSet.contains("z"))// => false يمكننا إنشاء خريطة map باستخدام الدّالة mapOf val fooMap = mapOf("a" to 8,"b" to 7,"c" to 9) يمكن الوصول لقيم الـ Map من خلال مفاتيحها println(fooMap["a"])// => 8 تُمثل المتتالية Sequences مجموعات تقييمها مؤجل إلى حين الحاجة lazily-evaluated collections. ويمكننا إنشاء متتالية باستخدام الدّالة generateSequence. val fooSequence = generateSequence(1,{ it +1}) val x = fooSequence.take(10).toList() println(x)// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] مثال لاستخدام متتالية لتوليد أرقام فيبانوتشي fibonacci fun fibonacciSequence():Sequence<Long>{ var a =0L var b =1L fun next():Long{ val result = a + b a = b b = result return a }return generateSequence(::next)} val y = fibonacciSequence().take(10).toList() println(y)// => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] تزوّد Kotlin دوال عالية الرتبة للعمل مع المجموعات collections val z =(1..9).map{it *3}.filter {it <20}.groupBy {it %2==0}.mapKeys {if(it.key)"even"else"odd"} println(z)// => {odd=[3, 9, 15], even=[6, 12, 18]} يمكن استخدام حلقة for مع أي شيء يؤمن التكرار. for(c in "hello"){ println(c)} تعمل حلقات while بشكل مشابه لعملها في اللغات الأخرى var ctr =0while(ctr <5){ println(ctr) ctr++}do{ println(ctr) ctr++}while(ctr <10) يمكن استخدام if كتعبير يرجع القيم. لهذا السبب ليس هناك حاجة لمعامل التشغيل :? في Kotlin. val num =5 val message =if(num %2==0)"even"else"odd" println("$num is $message")// => 5 is odd يمكن استخدام when كبديل لسلاسل if-else if val i =10 when { i <7-> println("first block") fooString.startsWith("hello")-> println("second block")else-> println("else block")} يمكن استخدام when مع المعطيات. when (i){0,21-> println("0 or 21") in 1..20-> println("in the range 1 to 20")else-> println("none of the above")} يمكن استخدام when كدالة ترجع القيم. var result = when (i){0,21->"0 or 21" in 1..20->"in the range 1 to 20"else->"none of the above"} println(result) يمكننا التحقق فيما إذا كان الكائن object من نوع محدّد باستخدام معامل التشغيل is. إذا مرر الكائن فحص النوع type check عندها يمكن استخدام هذا النوع دون الموائمة بشكل صريح. fun smartCastExample(x:Any):Boolean{if(x is Boolean){ X توائم أوتوماتيكيًا إلى Boolean return x }elseif(x is Int){ X توائم أوتوماتيكيًا إلى Int return x >0}elseif(x is String){ X توائم أوتوماتيكيًا إلى String return x.isNotEmpty()}else{returnfalse}} println(smartCastExample("Hello, world!"))// => true println(smartCastExample(""))// => false println(smartCastExample(5))// => true println(smartCastExample(0))// => false println(smartCastExample(true))// => true تعمل الموائمة الذكيّة smartcast أيضًا مع كتلة when fun smartCastWhenExample(x:Any)= when (x){ is Boolean-> x is Int-> x >0 is String-> x.isNotEmpty()else->false} الملحقات Extensions هي طريقة لإضافة وظائف جديدة للصنف class. وهي مشابهة لدوال ملحقات #C fun String.remove(c:Char):String{returnthis.filter {it != c}} println("Hello, world!".remove('l'))// => Hello, word! println(EnumExample.A)// => A println(ObjectExample.hello())// => hello أصناف Enum مشابهة لأنواع enum في جافا. enumclassEnumExample{ A, B, C } يمكن استخدام الكلمة المفتاحية object لإنشاء كائنات وحيدة. لا يمكننا تمثيلها ولكن يمكن أن نشير لحالتها الفريدة من خلال اسمها. وهذا مشابه لكائنات Scala singleton object ObjectExample{ fun hello():String{return"hello"}} fun useObject(){ObjectExample.hello() val someRef:Any=ObjectExample//كماهي تمامًا objects نستخدم أسماء الكائنات } ترجمة -وبتصرّف- للمقال Learn kotlin in Y Minutes
  3. يشرح هذا المقال +PHP 5. يجب أن تكون شفرة PHP مغلقة بوسوم php?> ومن الأفضل عمليًا تجاهل وسم الإغلاق إذا كان ملف php يحتوي على شفرة php فقط، لتلافي نتيجة خرج غير متوقعة. // استخدم علامة القوسين المائلين // لبدأ تعليق بسطر واحد // يمكن استخدام العلامة # لكن علامة // أكثر شيوعيا. /* إحاطة نص بالقوس المائل متبوعا بالنجمة بداية وبالنجمة متبوعة بالقوس المائل نهاية يجعل من هذا النص تعليقا متعدد الأسطر */ استخدم "echo" أو "print" لطباعة الخرج. print('Hello '); // بدون سطر فاصل "Hello " طباعة استخدام القوسين ( ) اختياري مع تعليمتي echo و print echo "World\n"; // مع سطر فاصل "World" طباعة كل العبارات يجب أنت تنتهي بالفاصلة المنقوطة ";" وأي شيء مكتوب خارج وسوم php?> ستتم طباعته على الشاشة بشكل تلقائي. الأنماط Types والمتغيرات Variables تبدأ المتغيرات بالرمز $ أسماء المتغيرات المقبولة تبدأ بحرف أو تسطيرة سفلية متبوعة بأي عدد من الأحرف، الأرقام أو التسطيرة السفلية. والمتغيرات المنطقية حساسة لحالة الأحرف. $boolean = true; // TRUE أو True $boolean = false; // FALSE أو False الأعداد الصحيحة $int1 = 12; // => 12 $int2 = -12; // => -12 $int3 = 012; // => 10 (البادئة 0 تدل على عدد ثماني) $int4 = 0x0F; // => 15 (0x تدل على رقم ستة عشري) المحارف الصحيحة الثنائية متاحة اعتبارًا من PHP 5.4.0 $int5 = 0b11111111; // 255 (0b يدل على عدد ثنائي) الأرقام العائمة Floats أو (المضاعفة doubles) $float = 1.234; $float = 1.2e3; $float = 7E-10; حذف متغير unset($int1); رياضيات $sum = 1 + 1; // 2 $difference = 2 - 1; // 1 $product = 2 * 2; // 4 $quotient = 2 / 1; // 2 رياضيات مختزلة $number = 0; $number += 1; // بمقدار 1 $number زيادة echo $number++; // (طباعة 1 (الزيادة بعد التقييم echo ++$number; // (طباعة 3 (الزيادة قبل التقييم $number /= $float; // $number قسمة واسناد ناتج القسمة إلى النصوص يجب أن تكون محدودة ضمن إشارة تنصيص واحدة(’) من كل جانب $sgl_quotes = '$String'; // => '$String' تجنب استخدام علامات التنصيص المزدوجة إلا في حالة تضمين متغيرات أخرى $dbl_quotes = "This is a $sgl_quotes."; // => 'This is a $String.' المحارف الخاصة تحدد حصريًا باقتباسات مزدوجة $escaped = "This contains a \t tab character."; $unescaped = 'This just contains a slash and a t: \t'; قم بإحاطة المتغيرات بالقوسين المائلين عند الحاجة $apples = "I have {$number} apples to eat."; $oranges = "I have ${number} oranges to eat."; $money = "I have $${number} in the bank."; منذ الإصدار الخامس يمكن استخدام nowdocs في حالة عدة أسطر غير مجمعة $nowdoc = <<<'END' Multi line string END; ستقوم heredocs بتجميع النصوص $heredoc = <<<END Multi line $sgl_quotes END; وصل النصوص يتم باستخدام النقطة "." echo 'This string ' . 'is concatenated'; يمكن تمرير النصوص كبارامتر لـ echo echo 'Multiple', 'Parameters', 'Valid';// 'MultipleParametersValid' يعيد الثوابت يعرّف الثابت باستخدام ()define ولا يمكن تغيير قيمته خلال فترة التشغيل. ويجب أن تبدأ أسماء الثوابت الصحيحة بحرف أو تسطيرة سفلية, متبوعة بأية حروف أو أرقام أو تسطيرات سفلية. define("FOO", "something"); الوصول إلى المتغير الثابت متاح دون الحاجة الى استخدام الرمز $ echo FOO; // 'something' يعيد echo 'This outputs ' . FOO; // 'This outputs something' يعيد المصفوفات Arrays كل المصفوفات في PHP هي مصفوفات مترابطة (hashmaps في بعض اللغات البرمجية) وتعمل في جميع اصدارات php $associative = array('One' => 1, 'Two' => 2, 'Three' => 3); تم إدخال تركيب قواعدي syntax جديد في PHP 5.4 $associative = ['One' => 1, 'Two' => 2, 'Three' => 3]; echo $associative['One']; // 1 تطبع إضافة عنصر للمصفوفة المترابطة $associative['Four'] = 4; القوائم المحرفية يسند لها مفاتيح صحيحة حكمًا $array = ['One', 'Two', 'Three']; echo $array[0]; // => "One" إضافة عنصر إلى نهاية المصفوفة $array[] = 'Four'; أو array_push($array, 'Five'); حذف عنصر من مصفوفة unset($array[3]); الخرج Output echo('Hello World!'); طباعة !Hello World على شاشة الاظهار القياسية stdout، وstdout هي صفحة الوب إذا كنت تستخدم متصفحًا print('Hello World!'); // echo مشابه لـ // هي أيضًا من تركيبات اللغة لذلك يمكنك إلغاء الأقواس echo و print echo 'Hello World!'; print 'Hello World!'; $paragraph = 'paragraph'; echo 100; // اطبع المتغيرات العددية مباشرة echo $paragraph; // أو المتغيرات إذا تم إعداد الوسوم القصيرة المفتوحة أو كانت نسخة PHP الخاصة بك 5.4.0 أو أحدث يمكنك استخدام قواعد بناء الجمل القصيرة لـ echo <p><?= $paragraph ?></p> $x = 1; $y = 2; $x = $y; المتغير x$ يحتوي نفس قيمة المتغير y$ $z = &$y; المتغير z$ يحتوي إسناد إلى قيمة y$، وأي تغيير في قيمة z$سيغير قيمة y$ وبالعكس المتغير x$ تبقى قيمته بدون تغيير كقيمة y$ الأصلية echo $x; // => 2 echo $z; // => 2 $y = 0; echo $x; // => 2 echo $z; // => 0 التفريغات Dumps تخرج نوع وقيمة المتحول إلى شاشة العرض القياسية stdout var_dump($z); // int(0) يطبع Print تطبع المتغيرات إلى شاشة الإظهار القياسية stdout بصيغة مقروءة للإنسان print_r($array); // Array ( [0] => One [1] => Two [2] => Three ) يطبع العمليات المنطقية $a = 0; $b = '0'; $c = '1'; $d = '1'; يقوم assert بإصدار تنبيه إذا كانت معطياته غير صحيحة وهذه المقارنة ستكون دومًا صحيحة حتى إذا كانت الأنواع مختلفة assert($a == $b); // equality المساواة assert($c != $a); // inequality عدم المساواة assert($c <> $a); // alternative inequality عدم المساواة بشكل بديل assert($a < $c); assert($c > $b); assert($a <= $b); assert($c >= $d); التالي سيكون صحيحًا فقط في حال كانت القيم متساوية ومن نفس النوع assert($c === $d); assert($a !== $d); assert(1 === '1'); assert(1 !== '1'); المشغل `Spaceship` <=> (منذ PHP 7) يعيد 0 إذا كانت القيم على طرفيه متساوية، ويعيد 1 إذا كانت القيمة على اليسار أكبر، ويعيد -1 إذا كانت القيمة على اليمين أكبر $a = 100; $b = 1000; echo $a <=> $a; // النتيجة 0 كونهما متساويين echo $a <=> $b; // $a < $b النتيجة -1 كون echo $b <=> $a; // $b > $aالنتيجة 1 كون يمكن تحويل المتغيرات إلى أنواع مختلفة بحسب استخدامها $integer = 1; echo $integer + $integer; // => 2 $string = '1'; echo $string + $string; // => 2 (تم تحويل النصوص الى أعداد صحيحة) $string = 'one'; echo $string + $string; // => 0 النتيجة 0 لأن المشغل + لا يستطيع موائمة النص `one` الى رقم. موائمة الأنواع Type casting يمكن استخدامها للتعامل مع متغير من نوع معين كنوع آخر. $boolean = (boolean) 1; // => true $zero = 0; $boolean = (boolean) $zero; // => false يوجد أيضًا توابع مخصصة لموائمة أغلب الأنواع $integer = 5; $string = strval($integer); null هي قيمة خالية $var = null; Control Structures بنى التحكم if (true) { print 'I get printed'; } if (false) { print 'I don\'t'; } else { print 'I get printed'; } if (false) { print 'Does not get printed'; } elseif (true) { print 'Does'; } المشغل الثلاثي ternary print (false ? 'Does not get printed' : 'Does'); المشغل الثلاثي المختصر بدأ من PHP 5.3 وهو مكافئ لـ "$x ? $x : 'Does'"" $x = false; print($x ?: 'Does'); مشغّل دمج القيم الفارغة null coalesce operator بدأ من php 7 $a = null; $b = 'Does print'; echo $a ?? 'a is not set'; // 'a is not set' تطبع echo $b ?? 'b is not set'; // 'Does print' تطبع $x = 0; if ($x === '0') { print 'Does not print'; } elseif ($x == '1') { print 'Does not print'; } else { print 'Does print'; } القواعد البديلة لبناء الجمل مفيدة لبناء النماذج <?php if ($x): ?> This is displayed if the test is truthy. <?php else: ?> This is displayed otherwise. <?php endif; ?> استخدم switch لتوفير قسط من العمليات المنطقية switch ($x) { case '0': print 'Switch does type coercion'; break; // 'two' و'three' وإلا سينتقل الى الحالة break يجب تضمين case 'two': case 'three': // 'two' أو 'three' تساوي $variable قم بعمل شيء ما اذا كانت قيمة break; default: // قم بعمل الاجراء الافتراضي } الحلقات while، do…while، for هي حلقات مألوفة غالبًا $i = 0; while ($i < 5) { echo $i++; } // "01234" اطبع $i = 0; do { echo $i++; } while ($i < 5); // "01234" اطبع for ($x = 0; $x < 10; $x++) { echo $x; } // "0123456789" اطبع $wheels = ['bicycle' => 2, 'car' => 4]; حلقات foreach يمكن أن تعمل على المصفوفة foreach ($wheels as $wheel_count) { echo $wheel_count; } // "24"اطبع يمكنك التكرار فوق المفاتيح أو القيم foreach ($wheels as $vehicle => $wheel_count) { echo "A $vehicle has $wheel_count wheels"; } $i = 0; while ($i < 5) { if ($i === 3) { break; // while اخرج من حلقة } echo $i++; } // "012" اطبع for ($i = 0; $i < 5; $i++) { if ($i === 3) { continue; // تجاوز هذا التكرار من الحلقة } echo $i; } // "0124" اطبع الدوال Functions عرّف الدالة باستخدام function function my_function () { return 'Hello'; } echo my_function(); // => "Hello" الاسم المقبول للدالة يبدأ بحرف أو تسطيرة سفلية متبوعة بأي عدد من الأحرف ،الأرقام، أو التسطيرات السفلية function add ($x, $y = 1) { // اختيارية وقيمتها الإفتراضية تساوي 1 $y $result = $x + $y; return $result; } echo add(4); // => 5 echo add(4, 2); // => 6 المتغير result$ لا يمكن الوصول إليه من خارج الدالة واستخدام الأمر ;print $result سيعطي خطأ منذ النسخة PHP 5.3 يمكنك تعريف دوال مجهولة anonymous functions $inc = function ($x) { return $x + 1; }; echo $inc(2); // => 3 function foo ($x, $y, $z) { echo "$x - $y - $z"; } يمكن للدوال أن تعيد دوالًا أخرى function bar ($x, $y) { // استخدم use لإحضار المتغيرات الخارجية return function ($z) use ($x, $y) { foo($x, $y, $z); }; } $bar = bar('A', 'B'); $bar('C'); // "A - B - C" يطبع يمكنك استدعاء الدوال المسماة named functions باستخدام النصوص $function_name = 'add'; echo $function_name(1, 2); // => 3 وهو هام من أجل تحديد أي دالة يتوجب تشغيلها برمجيًا، أو استخدم الدالة التالية call_user_func (callable $callback [, $parameter [, … ]]); يمكنك الحصول على كل البارمترات الممررّة للدالة function parameters() { $numargs = func_num_args(); if ($numargs > 0) { echo func_get_arg(0) . ' | '; } $args_array = func_get_args(); foreach ($args_array as $key => $arg) { echo $key . ' - ' . $arg . ' | '; } } parameters('Hello', 'World'); // Hello | 0 - Hello | 1 - World | يمكنك الحصول على عدد متغير من المعطيات ابتداء من PHP 5.6 function variable($word, ...$list) { echo $word . " || "; foreach ($list as $item) { echo $item . ' | '; } } variable("Separate", "Hello", "World"); // Separate || Hello | World | التضمين includes يجب أيضًا وضع وسم البداية لـ PHP في الملفات المتضمنة include 'my-file.php'; وبهذا تكون الشفرة ضمن الملف my-file.php متاحة ضمن مجال الرؤية، إذا لم يكن بالإمكان تضمين الملف (وكمثال الملف غير موجود )، يظهر تنبيه. include_once 'my-file.php'; إذا كانت شفرة الملف my-file.php متضمنة في مكان أخر ، لن يتم تضمينها ثانية. وهذا يمنع الاخطاء التي تحدث بسبب تكرار التصريح عن الصفوف require 'my-file.php'; require_once 'my-file.php'; الدالة ()require تقوم بنفس عمل ()include إلا أنه سيتسبب بخطأ فادح إذا لم يتمكن من تضمين الملف. محتويات ملف my-include.php <?php return 'Anything you like.'; الدوال Includes، requires يمكن أيضًا أن تعيد قيمة $value = include 'my-include.php'; يتم تضمين الملفات بالاعتماد على المسار المعطى، وإذا لم يحدد المسار يتم اعتماد مسار توجيه الاعداد include_path فإذا لم يتم إيجاد الملف يتم البحث في مسار استدعاء السكريبت ومسار العمل الحالي قبل الاعلان عن الاخفاق Classes الأصناف يتم تعريف الأصناف باستخدام العبارة class class MyClass { const MY_CONST = 'value'; // ثابت static $staticVar = 'static'; المتغيرات الثابتة Static ومجال رؤيتها public static $publicStaticVar = 'publicStatic'; يمكن الوصول إليها خلال الصنف فقط private static $privateStaticVar = 'privateStatic'; يمكن الوصول اليها من خلال الصنف والأصناف المشتقة protected static $protectedStaticVar = 'protectedStatic'; يجب تحديد مجال الرؤية للخصائص Properties public $property = 'public'; public $instanceProp; protected $prot = 'protected'; يمكن الوصول إليها من الصنف والأصناف الفرعية private $priv = 'private'; // يمكن الوصول إليها من الصنف فقط قم بإنشاء الباني باستخدام __construct public function __construct($instanceProp) { // $this قم بالوصول إلى متغيرات الحالة باستخدام $this->instanceProp = $instanceProp; } الطرق Methods تعرًف كدوال داخل الصنف public function myMethod() { print 'MyClass'; } الكلمة المفتاحية final تجعل الدالة غير قابلة للتحميل الزائد unoverridable final function youCannotOverrideMe() { } طرق سحرية Magic Methods ماذا تفعل إذا تمت معاملة العنصر كنص (سلسلة) public function __toString() { return $property; } ()destruct__ هو عكس الباني ()construct__ ويستدعى عندما لا تعود هنالك حاجة لأن يكون العنصر مشارًا إليه كمرجع public function __destruct() { print "Destroying"; } تعريف خصائص الصنف والطرق كثابت يجعلها قابله للوصول بدون الحاجة إلى تجسيد الصنف. الخاصية المعرّفة بأنها ثابتة لا يمكن الوصول إليها من العنصر المشتق من الصنف (لكن يمكن ذلك في الطرق الثابتة) public static function myStaticMethod() { print 'I am static'; } ثوابت الصنف Class constants يمكن الوصول إليها دائمًا بشكل ثابت echo MyClass::MY_CONST; // 'value' الخرج; echo MyClass::$staticVar; // 'static' الخرج; MyClass::myStaticMethod(); // 'I am static' الخرج; اشتقاق حالات الأصناف instance باستخدام new $my_class = new MyClass('An instance property'); استخدام القوسين اختياري إذا كنت لا تريد تمرير أيّة معطيات يمكننك الوصول إلى أعضاء الصنف class members باستخدام <- echo $my_class->property; // => "public" echo $my_class->instanceProp; // => "An instance property" $my_class->myMethod(); // => "MyClass" قم بتوسيع الصنف باستخدام extends class MyOtherClass extends MyClass { function printProtectedProperty() { echo $this->prot; } // التحميل الزائد للطرق Override function myMethod() { parent::myMethod(); print ' > MyOtherClass'; } } $my_other_class = new MyOtherClass('Instance prop'); $my_other_class->printProtectedProperty(); // => "protected" يطبع $my_other_class->myMethod(); // "MyClass > MyOtherClass" يطبع final class YouCannotExtendMe { } يمكنك استخدام "magic methods" لإنشاء دوال الارجاع getters والاسناد setters class MyMapClass { private $property; public function __get($key) { return $this->$key; } public function __set($key, $value) { $this->$key = $value; } } $x = new MyMapClass(); echo $x->property; // __get() سوف تستخدم طريقة $x->property = 'Something'; // __set() سوف تستخدم طريقة يمكن أن تكون الأصناف تجريدية (باستخدام الكلمة المفتاحية abstract) أو واجهات تنفيذ (باستخدام الكلمة المفتاحية implements). يصرح عن الواجهة بالكلمة المفتاحية interface. interface InterfaceOne { public function doSomething(); } interface InterfaceTwo { public function doSomethingElse(); } يمكن أن توسّع الواجهات interface InterfaceThree extends InterfaceTwo { public function doAnotherContract(); } abstract class MyAbstractClass implements InterfaceOne { public $x = 'doSomething'; } class MyConcreteClass extends MyAbstractClass implements InterfaceTwo { public function doSomething() { echo $x; } public function doSomethingElse() { echo 'doSomethingElse'; } } يمكن أن يكون للأصناف أكثر من واجهة واحدة class SomeOtherClass implements InterfaceOne, InterfaceTwo { public function doSomething() { echo 'doSomething'; } public function doSomethingElse() { echo 'doSomethingElse'; } } Traits السمات trait متوفرة بدءًا من النسخة PHP 5.4.0 ويصرّح عنها باستخدام trait trait MyTrait { public function myTraitMethod() { print 'I have MyTrait'; } } class MyTraitfulClass { use MyTrait; } $cls = new MyTraitfulClass(); $cls->myTraitMethod(); // Prints "I have MyTrait" Namespaces هذا مقطع منفصل, لأن التصريح عن namespace يجب أن يكون أول عبارة في الملف. دعونا نتظاهر بأن هذا ليس هو الحال <?php افتراضيًا, توجد الأصناف في namespace عام, ويمكن أن تستدعى صراحة بـ backslash $cls = new \MyClass(); ضبط namespace لملف namespace My\Namespace; class MyClass { } من ملف آخر $cls = new My\Namespace\MyClass; أو من داخل namespace آخر namespace My\Other\Namespace; use My\Namespace\MyClass; $cls = new MyClass(); أو يمكنك استبدال namespace باسم مستعار namespace My\Other\Namespace; use My\Namespace as SomeOtherNamespace; $cls = new SomeOtherNamespace\MyClass(); Late Static Binding الربط الساكن المتأخر class ParentClass { public static function who() { echo "I'm a " . __CLASS__ . "\n"; } public static function test() { // على الصف الذي عُرّف التابع ضمنه self تؤشر self::who(); // على الصف الذي استدعي التابع منه static تؤشر static::who(); } } ParentClass::test(); /* ParentClass أنا ParentClass أنا */ class ChildClass extends ParentClass { public static function who() { echo "But I'm " . __CLASS__ . "\n"; } } ChildClass::test(); /* ParentClass أنا ChildClass ولكن أنا */ Magic constants الثوابت السحرية الحصول على اسم الصنف الحالي. يجب أن تستخدم داخل تصريح الصنف. echo "Current class name is " . __CLASS__; الحصول على مسار الدليل الكامل لملف echo "Current directory is " . __DIR__; الاستخدام النموذجي require __DIR__ . '/vendor/autoload.php'; الحصول على المسار الكامل للملف echo "Current file path is " . __FILE__; الحصول على اسم الدالة الحالية echo "Current function name is " . __FUNCTION__; الحصول على رقم السطر الحالي echo "Current line number is " . __LINE__; الحصول على اسم الطريقة الحالية. يرجع قيمة فقط عندما يستخدم داخل تصريح سمة أو كائن echo "Current method is " . __METHOD__; الحصول على اسم namespace الحالي echo "Current namespace is " . __NAMESPACE__; الحصول على اسم السمة الحالية. ترجع قيمة فقط عندما تستخدم داخل التصريح عن السمة أو الكائن. echo "Current trait is " . __TRAIT__; Error Handling معالجة الأخطاء يمكن أن تتم معالجة أخطاء بسيطة باستخدام كتلة try catch try { // افعل شيء ما } catch (Exception $e) { // معالجة استثناء } عند استخدام كتلة try catch في بيئة namespace استخدم التالي try { // افعل شيء ما } catch (\Exception $e) { // معالجة استثناء } استثناءات مخصّصة class MyException extends Exception {} try { $condition = true; if ($condition) { throw new MyException('Something just happened'); } } catch (MyException $e) { // معالجة استثنائية } ترجمة -وبتصرّف- للمقال Learn PHP in Y Minutes
  4. جافا هي لغة برمجة حاسوبية للأغراض العامة، المتزامنة، المعتمدة على الصفوف، وغرضية التوجه // يبدأ التعليق المكتوب على خط واحد ب .// /* يبدو التعليق المكتوب على عدة سطور بهذا الشكل. */ /** *تبدو التعليقات في ملفات لغة البرمجة جافا بهذا الشكل *و تستخدم لوصف الصف أو الصفات المختلفة لكائن معين. *:الصفات الرئيسية * *@author- اسم مؤلف الكود. *ويحتوي على معلومات الاتصال كالبريد الالكتروني لمؤلف الكود أو للمؤلفين. *@version- النسخة الحالية من البرنامج. *@since -الوقت الذي تم فيه إضافة هذا الجزء من البرنامج. *@param -من أجل وصف البارامترات المختلفة للمنهج (method). *@return -لوصف القيمة التي يرجعها المنهج. *@deprecated -لإظهار انتهاء صلاحية الكود أو عدم وجوب استخدامه. *@see - روابط إلى جزء آخر من المستندات */ استورد الصف ArrayList بدلاً من استيراد الرزمة java.util كلّها import java.util.ArrayList; استورد جميع الصفوف الموجود داخل الرزمة import java.security.*; يحوي أي ملف جافا على صف عام، على المستوى الخارجي، له نفس اسم الملف public class LearnJava { ليعمل برنامج جافا يجب أن يحوي على تابع رئيسي بمثابة نقطة البدء public static void main(String[] args) { الدخل/الخرج الخرج استخدم ()System.out.println لطباعة السطور النصيّة System.out.println("Hello World!"); System.out.println( "Integer: " + 10 + " Double: " + 3.14 + " Boolean: " + true); استخدم ()System.out.print للطباعة بدون سطر جديد System.out.print("Hello "); System.out.print("World"); استخدم ()System.out.printf لتنسيق الطباعة بسهولة System.out.printf("pi = %.5f", Math.PI); // => pi = 3.14159 الدخل استخدم scanner لقراءة الدخل، يجب استيراد الصف ;java.util.Scanner Scanner scanner = new Scanner(System.in); لقراءة السلاسل المحرفية المُدخلة String name = scanner.next(); لقراءة البايتات المُدخلة byte numByte = scanner.nextByte(); لقراءة العدد الصحيح المُدخل int numInt = scanner.nextInt(); لقراءة العدد الحقيقي المٌدخل float numFloat = scanner.nextFloat(); لقراءة العدد الحقيقي مضاعف الدقة المُدخل double numDouble = scanner.nextDouble(); لقراءة القيمة المنطقية المُدخلة boolean bool = scanner.nextBoolean(); المتغيرات التصريح عن المتغيرات يتم التصريح عن متغير باستخدام // <type> <name> int fooInt; يتم التصريح عن مجموعة من المتغيرات من نفس النوع // <type> <name1>, <name2>, <name3> int fooInt1, fooInt2, fooInt3; تهيئة المتغير يتم تهيئة متغير باستخدام // <type> <name> = <val> int barInt = 1; يتم تهيئة مجموعة من المتغيرات من نفس النوع بنفس القيمة باستخدام // <type> <name1>, <name2>, <name3> // <name1> = <name2> = <name3> = <val> int barInt1, barInt2, barInt3; barInt1 = barInt2 = barInt3 = 1; أنواع المتغير البايت (Byte) : وهو 8bit ويستخدم لترميز الأعداد الصحيحة بين -128 و 127 byte fooByte = 100; إذ كنت ترغب بتفسير البايت كعدد صحيح موجب (بدون إشارة). فإن هذه العملية البسيطة من الممكن أن تساعد int unsignedIntLessThan256 = 0xff & fooByte; هذا يناقض عمل cast الذي من الممكن أن يعطي عدد سالب int signedInt = (int) fooByte; القصير (Short): وهو 16bit ويستخدم لترميز الأعداد الصحيحة بين -32,768 و 32,767 short fooShort = 10000; الصحيح (Integer): وهو 32bit ويستخدم لترميز الأعداد الصحيحة بين -2,147,483,648 و 2,147,483,647 int bazInt = 1; الطويل (Long): وهو 64bit ويستخدم لترميز الأعداد الصحيحة بين -9,223,372,036,854,775,808 و 9,223,372,036,854,775,807 long fooLong = 100000L; يستخدم المحرف L للدلالة على أن قيمة المتحول هي من النوع Long. و أي قيمة مُسندة للمتحول بدون استخدام L هي عبارة عن عدد صحيح int بشكل افتراضي. ملاحظة: إن الأنواع byte، short، int، long، هي أنواع ذات إشارة signed. أي من الممكن أن تحوي على قيم موجبة أو قيم سالبة. لا يوجد متغيرات بقيمة موجبة فحسب. ولكن المحارف تعتبر من نوعية unsigned ذات القيمة الموجبة فقط ذات 16bit. العائم (Float): وهو ذو دقة أحادية 32 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 149-^2 و 127^2 - (23-^2-2) float fooFloat = 234.5f; يستخدم المحرف f أو F للدلالة على أن قيمة المتحول هي من النوع Float. و إلا سيعتبر المتغير من النوع الحقيقي المضاعف. المضاعف (Double): وهو ذو دقة مضاعفة 64 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 1074-^2 و 1023^2 - (52-^2-2) double fooDouble = 123.4; القيم المنطقية (Boolean) ذات القيمة: true و false boolean fooBoolean = true; boolean barBoolean = false; Char هو محرف يونيكود 16بت أحادي char fooChar = 'A'; لا يمكن أعادة تهيئة المعطيات من النوع final final int HOURS_I_WORK_PER_WEEK = 9001; ولكن من الممكن تهيئتها بعد عملية التصريح عنها final double E; E = 2.71828; BigInteger -عبارة عن نوع الأعداد الصحيحة الثابتة بمستويات دقّة مختلفة. يستخدم نوع المعطيات BigInteger للسماح للمبرمج بالتعامل مع الأعداد الصحيحة التي هي أكبر من 64-بت. حيث تُخزّن الأعداد الصحيحة في مصفوفة من البايتات، التي يمكن التلاعب بها باستخدام التوابع المبنيّة في الصف BigInteger . يمكن تهيئة المتغير من النوع BigInteger بمصفوفة من البايتات أو بمصفوفة من السلاسل المحرفية. BigInteger fooBigInteger = new BigInteger(fooByteArray); BigDecimal عبارة عن نوع الأعداد الحقيقية الثابتة بمستويات دقة مختلفة. ويأخذ المتغير من النوع BigDecimal معاملين: عدد صحيح ذو حجم غير مقّيّد، يمثل مستوى الدقّة، وعدد صحيح أخر بحجم 32 بت. يسمح النوع BigDecimal بالتحكم بتقريب الأعداد الحقيقية. عندما يكون مطلوب مستوى دقة محدد للعدد الحقيقي يُنصح باستخدام BigDecimal . من الممكن تهيئة المتغير من النوع BigDecimal بأحد الأنواع التالية: int ،long ،double ،String ،BigInteger BigDecimal fooBigDecimal = new BigDecimal(fooBigInteger, fooInt); كُن حذرًا عند استخدامك للأنواع float ,double لأن عدم الدقّة في تحديد النوع سوف تُنسخ في الـ BigDecimal ويُفضل استخدام سلسلة محرفية ثابتة عندما تحتاج إلى قيمة دقيقة. BigDecimal tenCents = new BigDecimal("0.1"); String -السلاسل المحرفية String fooString = "My String Is Here!"; الحرف الخاص n\ يُعرف بحرف السطر الجديد الذي يحرك مؤشر الكتابة إلى بداية السطر التالي. String barString = "Printing on a new line?\nNo Problem!"; الحرف الخاص t\ يُعرف بالمسافة الأفقية، يقوم بتحريك مؤشر الكتابة مسافة معينة إلى النقطة التالية في السطر. String bazString = "Do you want to add a tab?\tNo Problem!"; System.out.println(fooString); System.out.println(barString); System.out.println(bazString); بناء السلاسل المحرفية: باستخدام عامل الجمع (+) تُعتبر الطريقة الأساسية (الأمثل) للقيام ببناء السلاسل المحرفية. String plusConcatenated = "Strings can " + "be concatenated " + “via + operator.”; System.out.println(plusConcatenated); // Output: Strings can be concatenated via + operator. باستخدام الصف StringBuilder لا تشكل هذه الطريقة أية سلاسل محرفية وسيطة، فقط تقوم بتخزين قطع السلاسل المحرفية و ربطها مع بعضها عندما يتم استدعاء التابع ()toString. تلميح: لا يعتبر الصف StringBuilder إجرائية آمنة. يوجد صف بديل آمن StringBuffer (مع تأثير بسيط على الأداء). StringBuilder builderConcatenated = new StringBuilder(); builderConcatenated.append("You "); builderConcatenated.append("can use "); builderConcatenated.append("the StringBuilder class."); System.out.println(builderConcatenated.toString()); // فقط الآن تمّ بناء السلسة المحرفية // Output: You can use the StringBuilder class. تكون StringBuilder فعاله عندما السلسلة المحرفية الكاملة المبنيّة مطلوبة في نهاية عملية ما. StringBuilder stringBuilder = new StringBuilder(); String inefficientString = ""; for (int i = 0 ; i < 10; i++) { stringBuilder.append(i).append(" "); inefficientString += i + " "; } System.out.println(inefficientString); System.out.println(stringBuilder.toString()); تتطلب inefficientString عمل أكثر، حيث أنها تنتج سلسلة محرفية عند كل دورة للحلقة. يتم ترجمة تجميع السلاسل المحرفية البسيطة بالعامل (+) إلى: toString() و StringBuilder تجنب استخدام تجميع السلاسل المحرفية داخل الحلقات. لاستخدام منسق السلاسل المحرفية طريقة أخرى بديلة لتوليد السلاسل المحرفية سريعة و قابلة للقراءة. String.format("%s may prefer %s.", “Or you”, “String.format()”); // Output: Or you may prefer String.format(). المصفوفات يجب تحديد حجم المصفوفة بشكل فوري بمجرد التصريح عنها. تستخدم الصيغة التالية للتصريح عن مصفوفة. // <datatype>[] <var name> = new <datatype>[<array size>]; // <datatype> <var name>[] = new <datatype>[<array size>]; int[] intArray = new int[10]; String[] stringArray = new String[1]; boolean boolArray[] = new boolean[100]; طريقة أخرى للتصريح عن مصفوفة و تهيئتها int[] y = {9000, 1000, 1337}; String names[] = {"Bob", "John", "Fred", "Juan Pedro"}; boolean bools[] = {true, false, false}; فهرسة المصفوفة - الوصول إلى عنصر فيها System.out.println("intArray @ 0: " + intArray[0]); تبدأ المصفوفات بالفهرس (0)، و هي قابله للتغيير . intArray[1] = 1; System.out.println("intArray @ 1: " + intArray[1]) ArrayLists; // => 1 مجموعة من أنواع المعطيات الأخرى، التي تستحق التدقيق فيها: ArrayLists : مشابهة للمصفوفات، إلا أنها تحوي على وظائف إضافية، وحجمها قابل للتعديل. LinkedLists : عبارة عن تنفيذ لقائمة مترابطة بشكل مضاعف، التي تنفذ جميع العمليات التي من المتوقع لقائمة مترابطة بشكل مضاعف أن تنفذها. Maps : عبارة عن وصل كائنات المفتاح إلى كائنات القيمة. وهي عبارة عن واجهة، و بالتالي لا يمكن أن يتم تشكيل كائنات منها. يجب أن يتم تحديد نوع المفاتيح و القيم بناءً على الكائن المُشكل من الصف. من الممكن ان يُربط كل مفتاح إلى قيمة واحدة فقط، ومن الممكن أن يظهر كل مفتاح مرة واحدة فقط. (لا يوجد نسخ) لتنفيذ الخريطة/الواجهة Map hashtable : يُستخدم هذا الصف الجدول ، HashMaps وينفذ الخريطة Map ، هذا يسمح بتثبيت زمن العمليات الأساسية، مثل الحصول على عنصر أو إدخال عنصر، حتى في المجموعات الكبيرة. TreeMap: عبارة عن Map ، مصنفة حسب مفاتيحها. يُحافظ كل تعديل على ترتيبه، إما باستخدام المقارن المزود عند عملية توليد الكائن، أو باستخدام المقارنات لكل كائن، اذا كان ينفذ واجهة قابلة للمقارنة. سوف يؤدي الفشل المدمج في تنفيذ واجهة قابلة للمقارنة، مع الفشل في تزويد مقارن إلى رمي ClassCastExceptions تأخذ عمليات إدخال و حذف عناصر زمن من الدرجة ((O(log(n ، لذلك تجنب استخدام بنى المعطيات هذه، إلا أذا كنت ترغب من الاستفادة من ميزة الترتيب. العمليات System.out.println("\n->Operators"); int i1 = 1, i2 = 2; // الطريقة المختصرة للتصريح عن عدة متغيرات في نفس الوقت العمليات الحسابية بسيطة System.out.println("1+2 = " + (i1 + i2)); // => 3 System.out.println("2-1 = " + (i2 - i1)); // => 1 System.out.println("2*1 = " + (i2 * i1)); // => 2 System.out.println("1/2 = " + (i1 / i2)); // => 0 (int يعيد int/int) System.out.println("1/2.0 = " + (i1 / (double)i2)); // => 0.5 باقي القسمة System.out.println("11%3 = "+(11 % 3)); // => 2 عمليات المقارنة System.out.println("3 == 2? " + (3 == 2)); // => false System.out.println("3 != 2? " + (3 != 2)); // => true System.out.println("3 > 2? " + (3 > 2)); // => true System.out.println("3 < 2? " + (3 < 2)); // => false System.out.println("2 <= 2? " + (2 <= 2)); // => true System.out.println("2 >= 2? " + (2 >= 2)); // => true العمليات المنطقية System.out.println("3 > 2 && 2 > 3? " + ((3 > 2) && (2 > 3))); // => false System.out.println("3 > 2 || 2 > 3? " + ((3 > 2) || (2 > 3))); // => true System.out.println("!(3 == 2)? " + (!(3 == 2))); // => true العمليات على مستوى البت ~ عامل إيجاد المتمم على مستولى البتات. << عامل الإزاحة الحسابية نحو اليسار (مع الأخذ بعين الاعتبار بت الإشارة). >> عامل الإزاحة الحسابية نحو اليمين (مع الأخذ بعين الاعتبار بت الإشارة). >>> عامل الإزاحة المنطقية نحو اليمين (بدون الأخذ بعين الاعتبار بت الإشارة). & على مستوى البتّات and العملية المنطقية ^ على مستوى البتّات xor العملية المنطقية | على مستوى البتّات or العملية المنطقية عمليات الزيادة int i = 0; System.out.println("\n->Inc/Dec-rementation"); العامل ++ يقوم بالزيادة بمقدار واحد. العامل – يقوم بالإنقاص بمقدار واحد. إذا تم وضع هذين العاملين قبل المتغير، فإنهن يقومان بالزيادة أو الإنقاص، ومن ثم يعيدان قيمة المتحول الجديدة. أما إذا تم وضع هذين العاملين بعد المتغير، فإنهما يقومان بإعادة قيمة المتحول، ومن ثم يقومان بالزيادة أو الإنقاص. System.out.println(i++); // i = 1, يطبع 0 (زياده لاحقة). System.out.println(++i); // i = 2, يطبع 2 (زيادة سابقة). System.out.println(i--); // i = 1, يطبع 2 (إنقاص لاحق) System.out.println(--i); // i = 0, يطبع 0 (إنقاص سابق) بنى التحكم System.out.println("\n->Control Structures"); عبارة if مشابهة لتلك الموجودة في لغة البرمجة C int j = 10; if (j == 10) { System.out.println("I get printed"); } else if (j > 10) { System.out.println("I don't"); } else { System.out.println("I also don't"); } حلقة While int fooWhile = 0; while(fooWhile < 100) { System.out.println(fooWhile); //زيادة العدّاد //(في المجال (0،1،..99 foowhile مكررة 100 مرة ، حيث أن قيم المتغير fooWhile++; } System.out.println("fooWhile Value: " + fooWhile); حلقة Do While int fooDoWhile = 0; do { System.out.println(fooDoWhile); //زيادة العدّاد //في المجال 0-99 foowhile مكررة 99 مرة ، حيث أن قيم المتغير fooDoWhile++; } while(fooDoWhile < 100); System.out.println("fooDoWhile Value: " + fooDoWhile); حلقة For : هيكلية الحلقة على الشكل التالي: for(<start_statement>; <conditional>; <step>) مثال for (int fooFor = 0; fooFor < 10; fooFor++) { System.out.println(fooFor); // fooFor 0->9 مكررة 10 مرات، حيث } System.out.println("fooFor Value: " + fooFor); //outerيتم الخروج من الحلقة الداخلية باستخدام جملة الهروب المعنونّة بـ outer: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (i == 5 && j ==5) { break outer; //يخرج من الحلقة الخارجية أيضا، بدلاً من الخروج فقط من الحلقة الداخلية } } } حلقة For Each : قادرة على العمل مع المصفوفات و الكائنات for إن حلقة التي تنفذ الواجهات القابلة للتكرار. int[] fooList = {1, 2, 3, 4, 5, 6, 7, 8, 9}; هيكلية الحلقة على الشكل التالي: for (<object> : <iterable>) تقرأ هكذا: لكل عنصر في ال iterable ملاحظة: يجب أن يتطابق نوع الكائن مع نوع عناصر ال iterable for (int bar : fooList) { System.out.println(bar); //سوف تتكرر 9 مرات و يطبع 1-9 على سطور جديدة. } Switch تعمل Switch مع أنواع المعطيات: int، char، short ،byte وأيضًا من الممكن أن تعمل مع النوع Enum و String و مجموعة من الصفوف الخاصة التي تجمع الأنواع البدائية: Character, Byte, Short, Integer. بدءًا من جافا 7، من الممكن استخدام النوع String. ملاحظة: تذكر عدم استخدام التعليمة "break" في نهاية حالة جزئية إذا كان هناك يوجد حالة أخرى تستوفي الشرط أيضًا. int month = 3; String monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; default: monthString = "Some other month"; break; } System.out.println("Switch Case Result: " + monthString); عبارة (Try-with-resources) في Java الإصدار +7 تُستخدم في جافا عبارات Try-catch-finally، و لكن في جافا +7 أيضا ً مُتاح استخدام try-with-resources إن عبارات try-with-resources تُبسط عمل عبارات try-catch-finally عن طريق إغلاق المصادر بشكل أتوماتيكي. لاستخدام try-with-resources ضمّن كائن من صف في تعليمة try. try (BufferedReader br = new BufferedReader(new FileReader("foo.txt"))) { // يمكنك أن تجرّب شيئًا يحتاج إلى استثناء System.out.println(br.readLine()); // في جافا 7 المصادر مغلقة دومًا حتى لو كان هناك استثناء } catch (Exception ex) { //catch سوف يتم إغلاق المصدر قبل أن تنفذ عبارة System.out.println("readLine() failed."); } في هذه الحالة، لا يوجد حاجة إلى عبارة finally حيث أن BufferedReader تم إغلاقه للتو. يتم استخدام هذا لتجنب الحالات الحرجة، عندما عبارة finally لا يمكن استدعاءها. الاختزال الشرطي يمكنك استخدام العامل "?" لسرعة الأسناد . تُقرأ كما يلي: "إذا كانت العبارة صحيحة ، استخدم <أول قيمة> و إلا استخدم <القيمة الثانية> int foo = 5; String bar = (foo < 10) ? "A" : "B"; System.out.println("bar : " + bar); // "bar : A"تطبع // صحيحة (foo < 10) لأن العبارة //أو بشكل مبسط: System.out.println("bar : " + (foo < 10 ? "A" : "B")); التحويل بين أنواع المعطيات تحويل المعطيات تحويل السلاسل المحرفية إلى عدد صحيح Integer.parseInt("123");//يعيد نسخة العدد الصحيح من السلسة المحرفية "123" تحويل العدد الصحيح إلى سلسلة محرفية Integer.toString(123);//يعيد النسخة المحرفية من العدد الصحيح 123 راجع تحويل المعطيات للأنواع التالية من الصفوف: Double Long String الصفوف و التوابع للتصريح عن كائن من الصف Bicycle استخدم الكلمة المفتاحية new Bicycle trek = new Bicycle(); استدعاء توابع الكائن trek.speedUp(3); // setter , getter يجب عليك استخدام توابع الواضع و الآخذ trek.setCadence(100); يُعيد التابع ()toString التمثيل النصي للكائن. System.out.println("trek info: " + trek.toString()); التهيئة مزدوجة الأقواس لا تملك جافا تراكيب لتشكيل مجموعات ساكنة من المعطيات بطريقة سهلة عادةً يتم استخدام الطريقة التالية: private static final Set<String> COUNTRIES = new HashSet<String>(); static { COUNTRIES.add("DENMARK"); COUNTRIES.add("SWEDEN"); COUNTRIES.add("FINLAND"); } ولكن يوجد طريقة أنيقة لإنجاز الشيء نفسه بشكل أسهل، تُدعى هذه الطريقة مزدوجة الأقواس private static final Set<String> COUNTRIES = new HashSet<String>() {{ add("DENMARK"); add("SWEDEN"); add("FINLAND"); }} يولد القوس الاول { } صف داخلي بدون اسم AnonymousInnerClass ويعرّف القوس الثاني{ } كتلة مهيئ الحالة instance. يتم استدعاء هذه الكتلة، عندما يتم توليد الصف الداخلي لا يعمل هذا فقط للمجموعات، إنما يعمل أيضًا لكل الصفوف غير النهائي non-final يمكنك تضمين صفوف أخرى خارجية غير عامة في ملف جافا ، ولكنها ليست بممارسة جيدة. بدلاً من ذلك يفضّل فصل الصفوف في ملفات جافا منفصلة صيغة التصريح عن صف: // <public/private/protected> class <class name> { // } حقول البيانات, أنماط الباني, الوظائف كلها في الداخل تستدعى الوظائف كالدوال في جافا class Bicycle { حقول و متغيرات الصف Bicycle Public: يمكن الوصول إليه من أي مكان public int cadence; Private: يمكن الوصول إليه فقط من داخل الصف private int speed; Protected: يمكن الوصول إليه فقط من داخل الصف و الصفوف الفرعية protected int gear; default: يمكن الوصول إليه فقط من داخل الرزمة. String name; Static متغير صف ساكن static String className; البلوك الساكن Static لا تملك جافا تنفيذ للبواني الساكنة، ولكن جافا تملك كتل برمجية لتعريف متغيرات الصف (المتغيرات لساكنة) سيتم استدعاء هذا البلوك عند تحميل الصف. static { className = "Bicycle"; } الباني هي الطريقة التي يتم فيها توليد الصفوف. هذا هو الباني public Bicycle() { //يمكنك أيضا استدعاء باني آخر: // this(1, 50, 5, "Bontrager"); gear = 1; cadence = 50; speed = 5; name = "Bontrager"; } هذا عبارة عن باني ذو معاملات public Bicycle(int startCadence, int startSpeed, int startGear, String name) { this.gear = startGear; this.cadence = startCadence; this.speed = startSpeed; this.name = name; } صيغة الدالة // <public/private/protected> <return type> <function name>(<args>) أصناف الجافا غالبًا ما تنفّذ getters وsetters في حقولها صيغة التصريح عن الدالة // <access modifier> <return type> <method name>(<args>) public int getCadence() { return cadence; } دوال void لا تحتاج تصريح عودة public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void speedUp(int increment) { speed += increment; } public void slowDown(int decrement) { speed -= decrement; } public void setName(String newName) { name = newName; } public String getName() { return name; } دالة لتوليد قيم الصفات لهذا الكائن @Override // موروثة من الصف لهذا الكائن. public String toString() { return "gear: " + gear + " cadence: " + cadence + " speed: " + speed + " name: " + name; } الصف PennyFarthing عبارة عن صنف فرعي من Bicycle class PennyFarthing extends Bicycle { -Penny Farthings //هي عباره عن نوع من الدراجات بعجلات أمامية كبيرة. public PennyFarthing(int startCadence, int startSpeed) { // super استدعي الباني الأب باستخدام الكلمة المفتاحية super(startCadence, startSpeed, 0, "PennyFarthing"); } يجب عليك تعليم المنهج الذي تُعيد كتابته بـ annotation@. @Override public void setGear(int gear) { this.gear = 0; } Object casting بما أن الصف PennyFarthing يرث الصف Bicycle : فمن الممكن القول أن: Bicycle هو PennyFarthing، و يمكن أن نكتب: // Bicycle bicycle = new PennyFarthing(); هذا يُدعى"object casting" حيث يتم تشكيل كائن من كائن آخر. الواجهات صيغة التصريح عن واجهة // <access-level> interface <interface-name> extends <super-interfaces> { // // Constants // // Method declarations // } مثال - الطعام: public interface Edible { public void eat(); //يجب على أي صف ينفذ هذه الواجهة // ان ينفذ هذه الدالة } public interface Digestible { public void digest(); // منذ جافا 8، من الممكن أن تملك الواجهات دالة افتراضية. public default void defaultMethod() { System.out.println("Hi from default method ..."); } } الآن يمكنك تشكيل صف يُنفذ كلا من هاتين الواجهتين. public class Fruit implements Edible, Digestible { @Override public void eat() { // ... } @Override public void digest() { // ... } } في جافا، يمكنك أن تمدد(ترث) صف واحد فقط، ولكن يمكنك أن تنفذ عدّة واجهات . على سبيل المثال: public class ExampleClass extends ExampleClassParent implements InterfaceOne, InterfaceTwo { @Override public void InterfaceOneMethod() { } @Override public void InterfaceTwoMethod() { } } الصفوف التجريدية صيغة التصريح عن صف تجريدي: // <access-level> abstract class <abstract-class-name> extends // <super-abstract-classes> { // // Constants and variables // // Method declarations // } لا يمكن تشكيل كائنات من الصفوف المجردة (تمثيل الصفوف المجردة) يمكن للصفوف المجردة أن تعرف عن دوال مجرّدة. المناهج المجردة لا تحوي على جسم ويجب تعليمها بالكلمة المفتاحية abstract . يجب على الصفوف الأبناء غير المجردة أن تعيد كتابة Override@ جميع الدوال المجردة في صفوفها العليا. من الممكن أن تكون الصفوف المجرّدة مفيدة عند الجمع بين منطق التكرار و السلوك المخصص، ولكن بما أن الصف المجرد يتطلب الوراثة، فإن الصفوف المجردة تنتهك منطق "التركيب عبر الوراثة". لذلك خُذ بعين الاعتبار الطرق الأخرى التي تستخدم هذا التركيب public abstract class Animal { private int age; public abstract void makeSound(); من الممكن أن يحوي المنهج جسم public void eat() { System.out.println("I am an animal and I am Eating."); //(private)ملاحظة: من الممكن هنا الوصول للمتغيرات الخاصة age = 30; } public void printAge() { System.out.println(age); } يمكن أن تحوي الصفوف المجردة على دالة رئيسية public static void main(String[] args) { System.out.println("I am abstract"); } } class Dog extends Animal { لاحظ بأنه لا يزال هناك حاجة لإعادة كتابة الدوال التجريدية في الصف التجريدي // @Override public void makeSound() { System.out.println("Bark"); // age = 30; ==> ERROR! // Animal خاص بالنسبة للصف age خطأ- و ذلك لأن المتغير } ملاحظة: من الممكن أن تحصل على خطأ إذا قمت هنا باستخدام Override@ لأن جافا لا تسمح لك بإعادة كتابة الدوال الساكنة. ما يحدث هنا يُدعى إخفاء الدالة METHOD HIDING public static void main(String[] args) { Dog pluto = new Dog(); pluto.makeSound(); pluto.eat(); pluto.printAge(); } الصفوف النهائية final class صيغة التصريح عن الصفوف النهائية // <access-level> final <final-class-name> { // // Constants and variables // // Method declarations // } الصفوف النهائية هي عبارة عن الصفوف التي لا يمكن وراثتها، (أي هي الولد النهائي). بطريقة ما، تُعد الصفوف النهائية معاكس الصفوف المجردة. لأنه يجب توسيع (وراثة) الصفوف المجردة، أما الصفوف النهائية لا يمكن توسيعها. public final class SaberToothedCat extends Animal { لاحظ بأنه ما يزال يوجد حاجة لإعادة كتابة الدوال المجردة في الصف المجرد.// @Override public void makeSound() { System.out.println("Roar"); } } الدوال النهائية public abstract class Mammal() { // صيغة الدالة النهائية: // <access modifier> final <return type> <function name>(<args>) // لا يمكن إعادة كتابة الدوال النهائية بواسطة صف ابن، // وبالتالي الدالة النهائية هو أخر تنفيذ للدالة . public final boolean isWarmBlooded() { return true; } } النوع Enum еnum هي طريقة تنظيمية للثوابت (Constants) في الكود بحيث تجمع الثوابت التي لها علاقة ببعضها تحت فئة واحدة بطريقة تنظم الوصول إليها .بما أن هذه القيم هي عبارة عن ثوابت، لذلك فإن أسماء الحقول من النوع enum يجب أن تكتب بحروف كبيرة. في لغة جافا يتم تعريف هذا النوع باستخدام الكلمة المفتاحية enum. لتعريف متحول عن يوم من أيام الأسبوع، باستخدام النوع enum : public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } من الممكن استخدام متغيرنا Day من النوع enum كما يلي: public class EnumTest { //Enumالمتغير من النوع Day day; public EnumTest(Day day) { this.day = day; } public void tellItLikeItIs() { switch (day) { case MONDAY: System.out.println("Mondays are bad."); break; case FRIDAY: System.out.println("Fridays are better."); break; case SATURDAY: case SUNDAY: System.out.println("Weekends are best."); break; default: System.out.println("Midweek days are so-so."); break; } } public static void main(String[] args) { EnumTest firstDay = new EnumTest(Day.MONDAY); firstDay.tellItLikeItIs(); // => Mondays are bad. EnumTest thirdDay = new EnumTest(Day.WEDNESDAY); thirdDay.tellItLikeItIs(); // => Midweek days are so-so. } } الأنواع enum هي فعالة بشكل أكبر مما تم توضيحه فوق. من الممكن أن يحتوي جسم المتغير enum على دوال و حقول أخرى. ترجمة -وبتصرّف- للمقال Learn Java in Y Minutes