full_stack_101 أساسيات جافاسكربت اللازمة للعمل مع React


ابراهيم الخضور

نهدف خلال تقدمنا في المنهاج، إلى تعلم ما يكفي عن JavaScript، فهذا أمر بالغ الأهمية، إضافة إلى تطوير الويب. لقد تطورت JavaScript بسرعة خلال السنوات القليلة الماضية، لذلك سنعتمد هنا الميزات التي توفرها النسخ الجديدة. يطلق على معيار JavaScript رسميًا اسم ECMAScript. وتعتبر النسخة ECMAScript® 2020 هي الأحدث بعد إصدارها في يونيو (حزيران) لعام 2020، كما تُعرف بالرمز ES11. لا تدعم المتصفحات الميزات الأحدث للغة JavaScript حتى الآن. لذلك نقلت الشيفرة (transpiled) التي تنفذها المتصفحات من النسخ الأحدث إلى النسخ الأقدم لتكون أكثر توافقًا. يعتبر استخدام Babel حاليًا أكثر الطرق شعبية لإجراء عملية النقل. وتُهيّأ عملية النقل آليًا في تطبيقات React التي تبنى باستخدام create-react-app. سنلقي نظرة أقرب إلى تهيئة عمليات النقل في القسم 7 لاحقًا.

تُعرَّف Node.js بأنها بيئة لتنفيذ شيفرة JavaScript مبنية على محرك JavaScript الذي طورته Google والمعروف باسم chrome V8، وتعمل هذه البيئة في أي مكان من الخوادم إلى الهواتف النقّالة. سنتدرب على كتابة شيفرة JavaScript باستخدام Node، لذلك ينبغي أن تكون نسخة Node.js المثبتة على جهازك 10.18.0 على الأقل. وطالما أنّ أحدث نسخ Node ستتوافق تلقائيًا مع أحدث نسخ JavaScript، لذلك لا حاجة لنقل الشيفرة.

تُكتب الشيفرة في ملفات تحمل اللاحقة js. وتنفذ من خلال الأمر node name_of_file.js، كما يمكن أن نكتبها ضمن طرفية Node.js التي نفتحها بكتابة الأمر node في سطر الأوامر، وأيضًا بكتابة الأمر نفسه في طرفية التطوير الخاصة بالمتصفح. تتماشى التنقيحات الأخيرة للمتصفح Chrome جيدًا مع ميزات JavaScript الأحدث دون الحاجة إلى نقل الشيفرة. كما يمكن استخدام أدوات بديلة مثل JS Bin.

تُذكِّرنا JavaScript من حيث الاسم والتعابير بلغة Java. لكن من ناحية آلية العمل فهي أبعد ماتكون عنها. فإذا نظرت إليها من منظور مبرمجي Java، سيبدو سلوكها غريبًا قليلًا وخاصة إن لم تكلّف نفسك عناء البحث في ميزاتها. لقد شاع فيما مضى محاكاة ميزات Java في تصميم نماذج JavaScript، لكننا لا نحبّذ هذا الأمر لأن اللغتين والبيئتين اللتين تعملان ضمنهما تختلفان كليًا.

المتغيرات Variables

تُعرَّف المتغيرات في JavaScript بطرقٍ عدة:

const x = 1
let y = 5

console.log(x, y)   // 1, 5
y += 10
console.log(x, y)   // 1, 15
y = 'sometext'
console.log(x, y)   // 1, sometext
x = 4               // خطا

لا تُعرّف الكلمة const متغيرًا، بل تدل على قيمة ثابتة (constant) لايمكن بعد ذلك تغييرها. بالمقابل تُعرّف الكلمة let متغيّرًا عاديًا. يمكن أن نرى بوضوح من خلال المثال السابق أن المتغيّرات قد تأخذ بيانات من أنواع مختلفة أثناء التنفيذ، ففي البداية يخزن المتغير y قيمة صحيحة، ثم سلسة نصية في النهاية. كما تُستخدم الكلمة var في تعريف المتغيّرات، فهذه الكلمة ولفترة طويلة كانت الطريقة الوحيدة لتعريف المتغيّر، ثم أضيفت const وlet مؤخرًا في النسخة ES6. وقد تعمل الكلمة var بطريقة مختلفة لتعريف المتغيرات مقارنة بالطريقة التي تُعرّف بها في معظم اللغات. سنلتزم في منهاجنا باستخدام const وlet عند تعريف المتغيرات. يمكنك الاطلاع على المزيد حول هذا الموضوع عبر YouTube كالفيديو التالي: var, let and const - ES6 JavaScript Features، أومن خلال المقاليين التاليين في أكاديمية حسوب:

المصفوفات Arrays

تُعرِّف الشيفرة التالية مصفوفة وتقدم مثالين عن كيفية استخدامها:

const t = [1, -1, 3]

t.push(5)

console.log(t.length) // 4
console.log(t[1])     // -1

t.forEach(value => {
  console.log(value)  // 1, -1, 3, 5
})                    

الأمر الملفت في هذه الشيفرة، هو إمكانية تعديل محتوى المصفوفة بالرغم من كونها ثابتة (const). ذلك أن المصفوفة عبارة عن كائن والمتغيّر سيشير إلى نفس الكائن دومًا حتى لو تغيّر محتواه بإضافة العناصر الجديدة. تستخدم تعليمة forEach كطريقة لتعداد عناصر المصفوفة في الشيفرة السابقة. حيث تمتلك forEach مُعاملًا على شكل دالة سهمية لها الصيغة التالية:

value => {
  console.log(value)
}

تستدعي forEach الدالة من أجل كل عنصر من عناصر المصفوفة، وتمرر كل عنصر كمُعامل. يمكن للدالة التي تمثل مُعاملًا لتعليمة forEach أن تمتلك مُعاملات أخرى خاصة بها. كما استخدم التابع push لإضافة عنصر جديد إلى المصفوفة في المثال السابق. تستخدم عادة البرمجة بأسلوب الدوال (functional programming) مع React. ولهذا الأسلوب ميزة استخدام بنى المعطيات الصامدة (Immutable). ويفضل عند كتابة شيفرة React استخدام التابع concat، فلا يضيف هذا التابع عنصرًا جديدًا إلى المصفوفة، بل ينشئ مصفوفة جديدة تضم محتويات القديمة بالإضافة إلى العنصر الجديد.

const t = [1, -1, 3]

const t2 = t.concat(5)

console.log(t)  // [1, -1, 3]
console.log(t2) // [1, -1, 3, 5]

لن يضيف الأمر (t.concat(5 عنصرًا جديدًا إلى المصفوفة القديمة، بل سيعيد مصفوفة جديدة تضم العنصر الجديد (5) بالإضافة إلى عناصر المصفوفة القديمة. لقد عُرّفت الكثير من التوابع المفيدة للتعامل مع المصفوفات، فلنلقي نظرة على استخدام التابع map على سبيل المثال:

const t = [1, 2, 3]

const m1 = t.map(value => value * 2)
console.log(m1)   // [2, 4, 6]

يُنشئ التابع map مصفوفةً جديدةً مبنيةً على أساس القديمة، وتستخدم الدالة التي مُررت كوسيط لإنشاء عناصر المصفوفة الجديدة. لاحظ في مثالنا السابق كيف نتجت العناصر الجديدة عن القديمة بضربها بالعدد 2. ويمكن للتابع map تحويل المصفوفة إلى شيءٍ مختلفٍ تمامًا:

const m2 = t.map(value => '<li>' + value + '</li>')
console.log(m2)  
// [ '<li>1</li>', '<li>2</li>', '<li>3</li>' ]

لقد تحولّت المصفوفة المكوَّنة من أعداد صحيحة إلى مصفوفة تحوي عبارات HTML باستخدام map. سنستخدم هذا التابع بكثرة في React كما سنرى في القسم 2 لاحقًا. يمكن إسناد قيمة كل عنصر من المصفوفة إلى المتغيّرات بطريقة الإسناد بالتفكيك (destructuring assignment).

const t = [1, 2, 3, 4, 5]

const [first, second, ...rest] = t

console.log(first, second)  // 1, 2
console.log(rest)          // [3, 4 ,5]

"فُكّكت" عناصر المصفوفة t باستخدام الإسناد بالتفكيك، وأسند أول عنصرين منها إلى المتغيّرين first وsecond، ثم جُمّعت بقية عناصر المصفوفة في مصفوفة جديدة أسندت بدورها إلى المتغيّر rest.

الكائنات Objects

توجد طرق عدة لتعريف الكائنات في JavaScript، وأكثر هذه الطرق شيوعًا هي استخدام الشكل المختصر لتعريف الكائنات (object literals) والتي تُنشأ بوضع خصائصها على شكل قائمة عناصر تفصل بينها الفواصل بين قوسين معقوصين.

const object1 = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
}

const object2 = {
  name: 'Full Stack web application development',
  level: 'intermediate studies',
  size: 5,
}

const object3 = {
  name: {
    first: 'Dan',
    last: 'Abramov',
  },
  grades: [2, 3, 5, 3],
  department: 'Stanford University',
}

تأخذ الخصائص قيمًا من أي نوع مثل الصحيحة والنصية والمصفوفات والكائنات، وتعرّف خصائص كائن ما باستخدام النقطة "." أو باستخدام الأقواس المعقوفة:

console.log(object1.name)         // Arto Hellas
const fieldName = 'age' 
console.log(object1[fieldName])    // 35

كما تُضاف الخصائص إلى الكائن مباشرة باستخدام النقطة "." أو الأقواس المعقوفة:

object1.address = 'Helsinki'
object1['secret number'] = 12341

لقد توجب علينا استخدام الأقواس المعقوفة عند إضافة الخاصية الأخيرة، ذلك أن الاسم (secret number) لا يصلح اسمًا لخاصية نظرًا لوجود فراغ بين الكلمتين. تمتلك الكائنات في JavaScript توابعًا أيضًا، لكننا لن نستخدم في المنهاج كائنات لها توابعها الخاصة، وسنكتفي بمناقشتها بإيجاز خلال تقدمنا. تُعرّف الكائنات أيضًا باستخدام الدوال البانية (constructor functions) التي تشابه في آلية عملها مثيلاتها في لغات برمجة أخرى، كدوال البناء في صفوف Java. مع ذلك، لا تمتلك JavaScript صفوفًا بالمفهوم الذي نراه في البرمجة كائنية التوجه (OOP). أضيفت الصيغة class للغة JavaScript ابتداء من النسخة ES6، والتي تساعد في بعض الحالات على بناء صفوف كائنية التوجه.

الدوال Functions

تعرّفنا سابقًا على استخدام التوابع السهمية والتي تُكتب بصيغتها الكاملة على النحو:

const sum = (p1, p2) => {
  console.log(p1)
  console.log(p2)
  return p1 + p2
}

وتُستدعى كما هو متوقع على النحو:

const result = sum(1, 5)
console.log(result)

يمكن تجاهل كتابة الأقواس المحيطة بالمُعاملات إن كان للدالة مُعامل واحد:

const square = p => {
  console.log(p)
  return p * p
}

وإن احتوت الدالة على عبارة برمجية واحدة، لا حاجة عندها لاستخدام الأقواس المعقوصة، وستعيد الدالة القيمة الناتجة عن تنفيذ هذه العبارة. لاحظ كيف سيُختصر تعريف الدالة لو أزلنا العبارة التي تطبع النتيجة على الطرفية:

const square = p => p * p

ولهذا الشكل أهميته التطبيقية عندما نتعامل مع المصفوفات وخاصة باستعمال التابع map:

const t = [1, 2, 3]
const tSquared = t.map(p => p * p)
// tSquared is now [1, 4, 9]

أضيفت الدوال السهمية إلى JavaScript مع النسخة ES6 منذ سنتين تقريبًا. وقد عُرّفت الدوال قبل ذلك بالكلمة function فقط. وعمومًا هناك طريقتان لتعريف الدوال، الأولى تسمية الدالة بالتصريح عنها (function declaration):

function product(a, b) {
  return a * b
}

const result = product(2, 6)
// result is now 12

أما الطريقة الثانية فهي استخدام تعبير تعريف دالة (function expression)، وفي هذه الحالة لا داع لإعطاء الدالة اسمًا:

const average = function(a, b) {
  return (a + b) / 2
}

const result = average(2, 5)
// result is now 3.5

سنستخدم في هذا المنهاج الدوال السهمية دائمًا.

التمارين 1.3- 1.5

سنكمل بناء التطبيق الذي بدأناه سابقًا، لهذا يمكنك متابعة كتابة الشيفرة في نفس المشروع طالما أن المهم هو الحالة النهائية للتطبيق الذي ستسلّمه.

نصيحة للاحتراف: قد تواجهك عقبات تتعلق ببنية الخصائص التي تمتلكها المكوِّنات. اجعل الأمر أكثر وضوحًا بطباعة الخصائص على الطرفية كالتالي:

const Header = (props) => {
  console.log(props)  return <h1>{props.course}</h1>
}

1.3 معلومات عن المنهاج: الخطوة 3

سنمضي قدمًا نحو الأمام مستخدمين الكائنات في تطبيقنا. عدّل تعريف المتغيّر في المكوّن App بالشكل التالي وأعد صياغة التطبيق حتى يبقى قابلًا للتنفيذ:

const App = () => {
  const course = 'Half Stack application development'
  const part1 = {
    name: 'Fundamentals of React',
    exercises: 10
  }
  const part2 = {
    name: 'Using props to pass data',
    exercises: 7
  }
  const part3 = {
    name: 'State of a component',
    exercises: 14
  }

  return (
    <div>
      ...
    </div>
  )
}

1.4 معلومات عن المنهاج: الخطوة 4

ضع بعد ذلك الكائنات في مصفوفة. عدّل تعريف المتغيّر في المكوِّن App إلى الشكل التالي ولا تنس تعديل بقية الأجزاء في المكوِّن بما يتوافق معه:

const App = () => {
  const course = 'Half Stack application development'
  const parts = [
    {
      name: 'Fundamentals of React',
      exercises: 10
    },
    {
      name: 'Using props to pass data',
      exercises: 7
    },
    {
      name: 'State of a component',
      exercises: 14
    }
  ]

  return (
    <div>
      ...
    </div>
  )
}

ملاحظة: اعتبر حتى هذه اللحظة أن هناك ثلاثة عناصر فقط ضمن المصفوفة لذلك لا ضرورة لاستخدام الحلقات عند التنقل بين العناصر. سنعود لاحقًا بتفصيل أعمق إلى موضوع تصيير الكائنات المبنية من عناصر مصفوفة في القسم التالي. لاتُمرِّر كائنات مختلفة على شكل خصائص من المكوِّن App إلى المكوِّنين Content و Total، بل مررها مباشرة كمصفوفة:

const App = () => {
  // const definitions

  return (
    <div>
      <Header course={course} />
      <Content parts={parts} />
      <Total parts={parts} />
    </div>
  )
}

1.5 معلومات عن المنهاج: الخطوة 5

لنتقدم خطوة أخرى إلى الأمام. حوّل المنهاج وأقسامه إلى كائن JavaScript واحد، وأصلح كل ما يتضرر:

const App = () => {
  const course = {
    name: 'Half Stack application development',
    parts: [
      {
        name: 'Fundamentals of React',
        exercises: 10
      },
      {
        name: 'Using props to pass data',
        exercises: 7
      },
      {
        name: 'State of a component',
        exercises: 14
      }
    ]
  }

  return (
    <div>
      ...
    </div>
  )
}

توابع الكائنات والمؤشر "this"

لن نستخدم كما ذكرنا سابقًا كائنات لها توابعها الخاصة، ذلك أننا سنعتمد على نسخة من React تدعم الخطافات. لن تجد فيما سيأتي لاحقًا في هذا الفصل ما يتعلق بالمنهاج، لكن من الجيد الاطلاع على المعلومات الواردة وفهمها وخاصة عندما تستخدم نسخًا أقدم من React.

يختلف سلوك الدوال السهمية عن تلك المعرّفة باستخدام التصريح function فيما يتعلق باستخدام الكلمة this التي تشير إلى الكائن نفسه. من الممكن إسناد التوابع إلى كائن بتعريف بعض خصائصه على شكل دوال:

const arto = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
  greet: function() {    console.log('hello, my name is ' + this.name)  },}

arto.greet()  // "hello, my name is Arto Hellas"

وقد تسند التوابع إلى الكائنات حتى بعد إنشاء هذه الكائنات:

const arto = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
}

arto.growOlder = function() {  this.age += 1}
console.log(arto.age)   // 35 is printed
arto.growOlder()
console.log(arto.age)   // 36 is printed

لنعدّل الكائن كما يلي:

const arto = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
  doAddition: function(a, b) {    console.log(a + b)  },}

arto.doAddition(1, 4)        // 5 is printed

const referenceToAddition = arto.doAddition
referenceToAddition(10, 15)   // 25 is printed

يمتلك الكائن الآن التابع doAddition الذي يجمع الأعداد التي تمرر إليه كوسطاء. يستدعى هذا التابع بالطريقة التقليدية (arto.doAddition(1, 4، أو بإسناد مرجعٍ للتابع إلى متغّير ومن ثم استدعاءه من خلال المتغيّر كما هو الحال في السطرين الأخيرين من الشيفرة السابقة. لكن لو حاولنا استخدام الطريقة ذاتها مع التابع greet سنواجه مشكلة:

arto.greet()       // "hello, my name is Arto Hellas"

const referenceToGreet = arto.greet
referenceToGreet() // "hello, my name is undefined"

فعندما نستدعي التابع باستخدام مرجع لكائن، سيفقد التابع القدرة على تحديد الكائن الذي تشير إليه الكلمة this. وعلى نقيض بقية اللغات، تُحدّد قيمة this في JavaScript وفقًا للطريقة التي يُستدعى بها التابع. فعندما تُستدعى من خلال مرجع تتحول this إلى كائن عام (global object) ولن تكون النتيجة ما يريده المطوّر في أغلب الأوقات. تقودنا الحالة السابقة لمواجهة العديد من المشاكل وخاصة عندما تحاول React أو Node استدعاء توابع عرفها المطوّر للكائن. لذلك سنتغلب على هذه العقبة بكتابة شيفرة JavaScript لا تستعمل this. لكن هناك حالة خاصة تظهر عندما نحاول ضبط قيمة انتهاء الوقت (timeout) للدالة greet التي تمثل جزءًا من اللكائن arto باستخدام setTimeout.

const arto = {
  name: 'Arto Hellas',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
}

setTimeout(arto.greet, 1000)

تُحدّد قيمة this في JavaScript وفقًا للطريقة التي يُستدعى بها التابع كما أشرنا سابقًا. فعندما نستخدم الدالة setTimeout لاستدعاء تابع، فإن محرك JavaScript هو من يستدعى التابع حقيقةً، وستشير this عندها إلى كائن عام. هناك آليات عديدة للاحتفاظ بمرجع this الأصلي، منها استدعاء الدالة bind كالتالي:

setTimeout(arto.greet.bind(arto), 1000)

سينشئ الاستدعاء (arto.greet.bind(arto دالة جديدة تشير فيها this إلى Arto بصرف النظر عن الطريقة التي تم بها استدعاء التابع. تُستخدم الدوال السهمية في بعض الأحيان لحل بعض مشاكل this. لكن لا ينبغي استخدام هذه الدوال كتوابع لكائنات، لأن this لن تعمل عندها أبدًا. سنعرّج لاحقًا على توضيح سلوك this مع الدوال السهمية. تمتلئ صفحات الانترنت بمعلومات عن استخدام this في JavaScript، إن أردت فهم ذلك أكثر، نوصيك بشدة متابعة سلسة Understand JavaScript's this Keyword in Depth التي أعدها egghead.io على سبيل المثال.

الأصناف Classes

لا تمتلك JavaScript كما أشرنا سابقًا صفوفًا تشابه تلك التي تقدمها اللغات كائنية التوجه. لكنها تمتلك ميزات تمكّننا من محاكاة تلك الأصناف. لنلق نظرة سريعة على الصيغة class التي قدمتها النسخة ES6 والتي تبسّط تعريف الأصناف (أو الأغراض الشبيه بالأصناف). سنعرّف في المثال التالي صفًا يسمى Person وكائنين Person:

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  greet() {
    console.log('hello, my name is ' + this.name)
  }
}

const adam = new Person('Adam Ondra', 35)
adam.greet()

const janja = new Person('Janja Garnbret', 22)
janja.greet()

تتشابه الأصناف والكائنات الناتجة من الناحية التعبيرية (Syntax) مع تلك التي تقدمها Java، كما تتشابه من ناحية السلوك أيضًا. لكنها تبقى في الصميم كائنات ناتجة عن وراثة نماذج أولية (prototypal inheritance) تقدمها JavaScript. فنمط كلٍّ من الكائنين عمليًا هو Object، لأن JavaScript تعرّف افتراضيًا أنواع البيانات الأساسية التالية: Boolean و Null و Undefined و Number و String و Symbol و Object فقط. على أية حال كان تقديم الصيغة class محط خلاف، يمكنك القراءة أكثر عن ذلك في المقالة Not Awesome: ES6 Classes أو في المقالة ?Is “Class” In ES6 The New “Bad” Part. لقد استخدمت هذه الصيغة مرارًا في النسخ القديمة من React وكذلك في Node.js، لكننا لن نستخدمها في المنهاج كبنية أساسية، طالما أننا سنستخدم ميزة الخطافات الجديدة في React.

مواد إضافية لتعلم JavaScript

تقدم أكاديمية حسوب توثيقًا عربيًا شاملًا مترجمًا عن الموقع javascript.info وننصح بشدة الاطلاع على كل المقالات الموجودة. بالإضافة إلى ذلك ستجد على شبكة الانترنت الكثير من المواد المتعلقة باللغة منها الجيد ومنها السيء، فيمكنك الاطلاع على المراجع التي تقدمها شركة Mozilla والمتعلقة بميزات JavaScript من خلال الدليل Mozilla's Javascript Guide. كما نوصيك بشدة أن تتطلع مباشرة على الدورة التعليمية A re-introduction to JavaScript JS tutorial على موقع الويب لشركة Mozilla. إن أردت التعمق بهذه اللغة نوصيك بسلسلة كتب مجانية ممتازة على الإنترنت تدعى You-Dont-Know-JS. يعتبر الموقع egghead.io مرجعًا جيدًا حيث يضم شروحات قيّمة عن JavaScript وعن React وغيرها من المواضيع الهامة، لكن ليست جميع المواد الموجودة مجانية.

ترجمة -وبتصرف- للفصل JavaScript من سلسلة Deep Dive Into Modern Web Development





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن