اذهب إلى المحتوى

أساسيّات جافاسكريبت والأخطاء الشّائعة الّتي يرتكبها المبتدئون


محمد فوّاز عرابي

بُنيت jQuery على لغة جافاسكريبت، وهي لغةٌ غنيّة وقويّة في حدّ ذاتها. يُغطّي هذا الدّرس أساسيّات لغة جافا سكريبت، وبعض الأخطاء الشّائعة الّتي يرتكبها المبتدئون بها. يُفيد هذا الدّرس القادمين الجدد إلى عالم البرمجة، ويفيد أيضًا المبرمجين بلغات أخرى الّذين لم يسبق لهم الاطّلاع على الجوانب المميّزة لـJavaScript.

فيما يلي برنامج JavaScript بسيط يُضيف رسالةً إلى صفحة ويب:

// أنشئ دالّة لإلقاء التّحية على شخص
// وأسندها إلى المُتغيّر `greet`
var greet = function( person, message ) {
  var greeting = 'Hello, ' + person + '!';
  log( greeting + ' ' + message );
};

// استخدم الدالّة لتحيّة Jory، بإمرار اسمها ورسالة التّحيّة
greet( 'Jory', 'Welcome to JavaScript' );

// استخدم الدالّة لتحيّة Rebecca، بإمرار اسمها ورسالة مختلفة
greet( 'Rebecca', 'Thanks for joining us' );

مُلاحظة: في المثال السابق، استخدمنا الدّالّة log. وهي دالّة مُساعِدة متوفّرة في الأمثلة في هذه السّلسلة فقط، وليست متوفّرة تلقائيًّا في JavaScript، يمكن استخدام log في محرّر النّصوص البرمجيّة في هذه السّلسلة، ولكن ستحتاج إلى استخدام console.log محلّها في النّصوص البرمجيّة خارج السّلسلة، وعندها ستُطبع نتائج النّصّ إلى طرفيّة المتصفّح الّذي تستعمله.

// create a function that will greet a person,
// and assign the function to the `greet` variable
var greet = function( person, message ) {
  var greeting = 'Hello, ' + person + '!';
  log( greeting + ' ' + message );
};

// use the function to greet Jory, passing in her
// name and the message we want to use
greet( 'Jory', 'Welcome to JavaScript' );

// use the function to greet Rebecca, passing in her
// name and a different message
greet( 'Rebecca', 'Thanks for joining us' );

النّتيجة

المثال السابق يعرض نتيجة تنفيذ النّص البرمجيّ ضمن قسم Console، يمكنك إعادة ضبط المثال باستخدام الزر Clear ثمّ تعديل النصّ إن رغبت بالتجربة وإعادة تنفيذه بنقر Run. لا بأس إن بدت بعض الأشياء غير مفهومة الآن؛ ففي الفقرات التّالية سنطّلع على مفاهيم المتغيّرات والدوالّ والمكوّنات الأساسيّة الأخرى لـJavaScript.

ملاحظة عن التعليقات

تفيد التّعليقات في شرح ما يفعله كل جزء من النصّ البرمجيّ، ولا تؤثّر التعليقات في عمل البرنامج على الإطلاق. بعض المُبرمجين يحبّون الإكثار من التعليقات في برامجهم، بينما يفضّل آخرون كتابتها فقط عندما تكون بعض الأجزاء من النّصّ غامضة ولا تتضّح بسهولة.

تسمح JavaScript بنوعين من التعليقات: مفردة السّطر، ومتعدّدة السّطور، ويبدأ الشّكل الأوّل بالحرفين //؛ بينما يبدأ الثّاني بالحرفين ‎/*‎ وينتهي بـ‎*/‎.

// هذا تعليق مُفرد السّطر
var foo;

/*
  هذا تعليقٌ متعدّد السّطور
  يمكن أن يمتدّ على أكثر من سطر، كما ترى.
 */

يمكن أيضًا استخدام التّعليقات متعدّدة السّطور مُضمّنة ضمن سطرٍ من النّصّ البرمجيّ:

function cornify( unicorns /* عدد صحيح */, rainbows /* عدد صحيح */ ) {

}

المكوّنات الأساسيّة لـJavaScript

المُتغيِّرات (Variables)

المُتغيّرات هي الطّريقة الّتي نخزّن بها القيم الّتي نريد استخدامها في وقت لاحقٍ. يمكن أن تكون المتغيّرات على عدّة أنواع من نصوصٍ أو أعداد أو بياناتٍ أخرى كالمصفوفات والكائنات، أو حتى نصوصًا برمجيّة (بصورة دالّة مُخزّنة في متغيّر). يُصرّح عن المتغيّر بجملة var:

var myName = 'Rebecca';

يمكن اختيار أي اسم للمتغيّر بشرط ألّا يبدأ الاسم برقمٍ وألّا يحوي إشارة النّاقص "-".

بإمكانك التّصريح عن متغيّر واحدٍ في كلّ جملة:

var a = 1;
var b = 'two';

أو التّصريح عن عدّة متغيّرات في جملة واحدة بفصلها بفواصل لاتينيّة:

var a = 1,
    b = 'two';

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

log( myName ); // يطبع 'Rebecaa'

المتغيّرات مكوّن ضروريّ للالتزام بمبدأ "لا تكرّر نفسك" (Don't Repeat Yourself)، فإذا أردت استخدام قيمة واحدة أكثر من مرّة، فالغالب أنّك ترغب في تخزينها في مُتغيّر.

سنتحدّث عن المتغيّرات بتفصيل أكبر في الفقرة القادمة.

الدّوالّ (Functions)

الدّوال مكوّن أساسيّ في برامج JavaScript لأنّها توفّر وسيلة لتهيئة وظائف صغيرة في البرنامج بصورة جزء مستقلّ ومنظّم. افترض مثلًا دالّة تجمع عددين:

function(a, b) {
    return a + b;
}

تأخذ هذه الدّالة مُعاملين اثنين هما a وb، تجمعهما ثمّ تعيد المجموع كنتيجة الدّالّة.

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

var addTwoNumbers = function(a, b) {
  return a + b;
}

ما فعلناها في المثال الأخير هو أنّنا أخذنا تعبيرًا عن دالّة (function expression) وأسندناه إلى مُتغيّر. يمكننا الآن استدعاء هذه الدّالة باستخدام اسم المتغيّر:

log( addTwoNumbers(1, 1) ); // تطبع 2

يمكننا بدلاً من ذلك استخدام تصريح عن دالّة (function declaration) لإعطاء دالّتنا اسمًا:

function addTwoNumbers(a, b) {
  return a + b;
}

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

الخلاصة: تسمية الدّوالّ بالتّصريح عن الدّالّة قد تؤدّي إلى نتائج غير مُتوقّعة إن لم تفهم بدقّة إحدى ميّزات JavaScript المعروفة باسم "الرّفع إلى أعلى النّطاق" (hoisting). تفصيل هذه الميزة موضوعٌ خارج عن نطاق هذه السّلسلة، ولكنّنا سنعتمد إسناد الدّوال إلى المُتغيّرات في الوقت الحاليّ.

الدّوال ونطاق المُتغيّرات (Variable scope)

تكون المُتغيّرات الّتي تُعرَّف ضمن دالّة بجملة var مُتاحة فقط من داخل هذه الدّالّة، وهذا أمر مرغوبٌ عادةً، ولذا يجب التّصريح عن كلّ المُتغيّرات في البرنامج باستخدام جمل var ما لم يُقصد جعلها عامّة (global)، أي متاحةً عبر كامل البرنامج وفي أيّ موضع منه. ليس هذا ما نُريده عادةً إلّا إذا أردنا تغيير قيمة المُتغيّر من نصّ برمجيّ آخر.

ولكن ما معنى أن تكون المُتغيّرات مُتاحةً فقط داخل الدّالّة؟ جرّب هذا البرنامج:

تحذير: نصّ برمجيّ خاطئ

var myFunction = function() {
  var foo = 'bar';
};

myFunction();
log( typeof foo ); // تطبع undefined!

النّتيجة

 

لو أنّنا حاولنا في المثال السّابق استخدام قيمة foo خارج الدّالة (بدل الاكتفاء بفحص نوعها)، لأبلغنا المُتصفّح بوجود خطأ، ولن يتابع البرنامج العمل فيما يلي الموضع الّذي استخدمنا foo عنده.

المثال التّالي يبيّن كيف يمكن لمُتغيّرين مختلفين أن يوجدا باسمٍ واحد طالما أنّ كلًّا منهما موجود في نطاق منفصل عن الآخر. في هذا المثال نُصرّح عن المُتغيّر foo ونسند إليه القيمة 'qux'، ثم نُصرّح عن مُتغيّر آخر داخل دالّة، ونُسمّيه foo أيضًا، ونُسند إليه القيمة 'bar'. لاحظ أنّ المُتغيّر foo خارج الدّالة لا يتغيّر حتّى بعد أن نُنشئ المُتغيّر foo داخل الدّالة.

var foo = 'qux';
var myFunction = function() {
  var foo = 'bar';
};

log( foo ); // تطبع 'qux'
myFunction();
log( foo ); // تطبع 'qux' أيضًا

على الرّغم من تشارك المُتغيّرين اسمًا واحدًا، إلّا أنّ JavaScript تعتبرهما شيئين مختلفين بالكامل، وهذا واحد من عدّة أسباب تدفعك لإعطاء مُتغيّراتك أسماء مُعبّرة.

نطاق المُتغيّرات مفهوم جوهريّ في JavaScript، ولعلّه يسبّب بعض الارتباك للمبتدئين. تذكّر:

  • في الأغلبية السّاحقة من الحالات، صرّح عن المُتغيّرات بجملة var
  • لا يمكن الوصول إلى المُتغيّرات المفروضة داخل دالّة بجملة var من خارج هذه الدّالّة
  • المُتغيّرات الّتي تُفرض بلا جملة var تكون عامّةً دائمًا

انتبه إلى أنّ المُتغيّرات الّتي يُصرَّح عنها بلا الكلمة var تكون ضمنيًّا عامّةً. في المثال التّالي لدينا المُتغيّر a المتاح خارج الدّالة لأنّ عُرِّف بدون الكلمة var (وهذا أمرٌ غير مرغوب في معظم الحالات).

تحذير: نصّ برمجيّ غير آمن

function test() {
  a = 1;
}

test();

log( a ); // تطبع 1

النّتيجة

الكائنات (Objects)

الحقيقة أنّ معظم ما نتعامل معه في JavaScript ليس سوى كائنات، وليس في اللّغة إلّا 5 أنواع من القيم الّتي لا تُعتبر كائنات:

  • السلاسل النّصيّة (strings)
  • القيم المنطقية (true/false)
  • الأعداد
  • undefined (غير مُعرّف)
  • null (معدوم)

تُسمّى القيم السّابقة بالأنواع الأوّليّة (primitives)، ويمكن حتّى لبعض هذه القيم أن تُعامل كما لو أنّها كائنات (سنشرح هذا بعد قليل). ولكن ما الكائن؟ لنُلقِ نظرةً على كائنٍ بسيط:

var person = {
  firstName: 'Boaz',
  lastName: 'Sender'
};

للكائن الّذي أنشأناه أعلاه خاصّتان اثنتان (properties):‏ firstName وlastName. أنشأنا الكائن بصياغة "الكائن الحرفيّ (object literal)" أي بإحاطة مجموعة من أزواج مفتاح/قيمة بقوسين معكوفين {}. لاحظ أنّ نقطتين (:) تفصلان بين المفتاح والقيمة وأنّ فاصلة لاتينيّة (,) تفصل بين كلّ زوجين. لاحظ أنّه ما من فاصلة بعد الزّوج الأخير، وإن أضفتها سهوًا فستقع أخطاء عند تنفيذ البرنامج في المتصفّحات القديمة.

الوصول إلى الخصائص

حفظنا الكائن في مُتغيّر أسميناه person، ممّا يُسهّل الوصول إلى خصائصه، إمّا باستخدام أسلوب النّقاط (dot notation) أو أسلوب الأقواس المُربّعة (bracket notation).

var person = {
  firstName : 'Boaz',
  lastName : 'Sender'
};

log( 'First name: ' + person.firstName );     // dot notation
log( 'Last name: ' + person[ 'lastName' ] );  // bracket notation

النّتيجة

ستلاحظ أنّنا استخدمنا سلسلة نصّيّة (string) للوصول إلى اسم الخاصّة الّتي نريدها عند اعتماد أسلوب الأقواس المُربّعة. أمّا عند اعتماد الأسلوب الآخر، أسلوب النّقاط، فقد استخدمنا اسم الخاصّة نفسه دون علامتي الاقتباس. يفيد أسلوب الأقواس المُربّعة عندما نريد الوصول إلى خاصّة يُحفَظ اسمها ضمن مُتغيّر:

var person = {
  firstName : 'Boaz',
  lastName : 'Sender'
};

var prop = 'lastName';

log( 'Last name: ' + person[ prop ] );

النّتيجة

بعد أن أنشأنا الكائن، يمكننا تعديل خصائصه:

var person = {
  firstName : 'Boaz',
  lastName : 'Sender'
};

person.firstName = 'Ben';
person.lastName = 'Alman';

log( 'First name: ' + person.firstName );
log( 'Last name: ' + person.lastName );

النّتيجة

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

وظائف الكائنات (Object methods)

الوظائف (methods) هي خصائص في الكائن قيمها دوالّ (functions)، لنُضِف وظيفة ‎.greet()‎ إلى كائن person:

var person = {
  firstName : 'Boaz',
  lastName : 'Sender',
  greet : function(name) {
    log( 'Hi, ' + name );
  }
};

person.greet( person.firstName );

النّتيجة

استقبلت الوظيفة ‎.greet()‎ في المثال السّابق النّصّ name كُمعامِل لها. عند استدعاء هذه الدّالة، أرسلنا قيمة الخاصّة firstName لكائن person ببساطة. لو أردنا وظيفة فائقة المُرونة تُلقي التّحيّة على أيّ شخص، فلرّبما يكون ما كتبناه صحيحًا. ولكن الغالب أنّنا نريد لوظيفتنا أن تُلقي التّحيّة على الشّخص المُحدّد في الكائن person فقط.

معنى this

داخل كلّ وظيفة (وداخل كلّ دالّة) تتوفّر كلّمة مفتاحيّة خاصّة: this، وهي تُشير إلى الكائن الّذي يُمثِّل السّياق الّذي اُستدعيت فيه الدّالة.

عندما نستدعي person.greet()‎ يكون السّياق هو الكائن person ذاته. معنى هذا أنّ بإمكاننا استخدام this للوصول إلى إحدى خصائص الكائن person مباشرة من داخل الوظيفة ‎.greet()‎.

ملاحظة: معنى this قد يُسبّب ارتباكًا شديدًا لمطوّري JavaScript المُبتدئين، يمكنك الاطمئنان إن لم تفهم بدقّة معناها، لأنّ jQuery تُحسن استغلالها بحيث لا تضطّر للدّخول في تفاصيلها؛ ومع ذلك فلا يكتمل الحديث عن الكائنات ووظائفها دون الإشارة إلى this وإن قليلًا. سأترك لك حرّيّة الانتقال إلى فقرة "الكائنات في jQuery" من هذه المقالة إن شئت، ثمّ عُد إلى هنا عندما تشعر أنّك جاهز لفهمها تمامًا.

لنحاول فهم كيف يمكن استخدام this في وظيفتنا:

var person = {
  firstName : 'Boaz',
  lastName : 'Sender',
  greet : function() {
    log( 'Hi, ' + this.firstName );
  }
};

person.greet();

النّتيجة

ليست مُعقّدةً كثيرًا حتى الآن... صحيح؟ منشأ التّعقيد هو أنّ معنى this يمكن أن يتغيّر، فالأمر كما قلنا يعتمد على السّياق الّذي استدعيت فيه الدّالة! لاحظ النّصّ التّالي:

تحذير: نصّ برمجيّ خاطئ

var person = {
  firstName : 'Boaz',
  lastName : 'Sender',
  greet : function() {
    log( 'Hi, ' + this.firstName );
  }
};

var sayIt = person.greet; // store the method in a variable

sayIt(); // logs 'Hi, undefined' -- uh-oh

النّتيجة

عندما نحفظ الوظيفة ‎.greet()‎ في مُتغيّر sayIt ثمّ نستدعي sayIt()‎، فإنّ السّياق يتغيّر ليُصبح الكائن العامّ window، لا الكائن person. وبما أنّ ليس للكائن window خاصّة اسمها firstName، فإنّ النتيجة تكون undefined عندما نحاول الوصول إليها.

ما الذي على المُبرمج فعله إذًا؟ أوّلًا: عليه أن يحذر من النّتائج غير المُتوقّعة لمعنى this عند تخزين وظائف الكائنات في متغيّرات. وثانيًا: عليه أن يعلم أنّ بإمكانه إجبار this أن تُشير إلى ما يشاء من الكائنات وذلك باستخدام إحدى الوظيفتين ‎.call()‎ و‎.apply()‎ مع الدّالّة ذاتها.

var person = {
  firstName : 'Boaz',
  lastName : 'Sender',
  greet : function() {
    log( 'Hi, ' + this.firstName );
  }
};

var sayIt = person.greet;

sayIt.call( person );

النّتيجة

كلا الوظيفتين المُتشابهتين ‎.call()‎ و‎.apply()‎ تُتيحان إمرار المُعاملات إلى الدّالّة الّتي تُستدعى بهما. تخيّل أنّ دالة التّحيّة تقبل بعض المعاملات، يمكن حينئذٍ إمرار هذه المُعاملات باستخدام ‎.call()‎ كما يلي:

var person = {
  firstName : 'Boaz',
  lastName : 'Sender',
  greet : function(greeting, punctuation) {
    log( greeting + ', ' + this.firstName + punctuation );
  }
};

var sayIt = person.greet;

sayIt.call( person, 'Hello', '!!1!!1' );

النّتيجة

يمكن تنفيذ الأمر ذاته باستخدام ‎.apply()‎، ولكن سنحتاج معها إلى إمرار المُعاملات ضمن مصفوفة واحدة بدلًا من إمرارها مُنفصلة:

var person = {
  firstName : 'Boaz',
  lastName : 'Sender',
  greet : function(greeting, punctuation) {
    log( greeting + ', ' + this.firstName + punctuation );
  }
};

var sayIt = person.greet;

sayIt.apply( person, [ 'Hello', '!!1!!1' ] );

النّتيجة

مُلاحظة: لتفاصيل أكثر عن ‎.call()‎ و‎.apply()‎، راجع وثيقتي شبكة مُطوِّري موزيلّا .call() و.apply()،

الكائنات في jQuery

لم نتطرّق بعدُ إلى تفاصيل الكائنات في JavaScript لكنّك الآن تعرف الأساسيّات الّتي تُتيح لك التّعامل مع الكائنات خلال تعلّمك jQuery، ففيها ستستعمل الكائنات لضبط الخيارات، كتغيير عدّة خصائص CSS لعنصر في الصّفحة مرّة واحدة:

$('#main').css({
  color: 'red',
  border: '1px solid blue'
});

بخصوص this، فإنّ jQuery تُسيطر على معناها، وفي حالة مُتولّيات الأحداث (event handlers)، تُشير this إلى العنصر في الصّفحة الّذي ربطتّه بالمُتولّي، وفي حالة سرد مجموعة من الكائنات في تحديدٍ، تُشير this إلى العنصر الحاليّ في السّرد. ليس عليك الحرص على فهم this بدقّة في المراحل الأولى لتعلّمك، بل اكتفِ بإبقاء فكرتها في ذهنك أثناء تعلّمك.

تفاصيل أكثر

يتوسّع دليل JavaScript في شبكة مُطوِّري موزيلّا في شرح مواضيع مثل النّماذج البدئيّة للكائنات (object prototypes) والدّوال المُشيِّدة (constructor functions) وحذف خصائص الكائنات.

المصفوفات (Arrays)

المصفوفات نوعٌ من الكائنات تُستخدم لحفظ قائمة من القيم، وهي وسيلةٌ سهلة لتخزين مجموعة من العناصر المترابطة من نفس النّوع (كالسّلاسل النّصيّة)، مع أنّ الواقع أنّه لا شيء يمنع مصفوفةً من أن تحوي عدّة أنواع من العناصر، بما في ذلك مصفوفاتٍ أخرى.

الطّريقة المُفضّلة لإنشاء مصفوفة استخدامُ أسلوب المصفوفات الحرفيّة:

var myArray = [ 'a', 'b', 'c' ];

ملاحظة: ستقرأ من وقتٍ لآخر برامج تُنشئ المصفوفات بالأسلوب التالي: new Array('a', 'b', 'c') وهو أسلوب غير مقبولٍ وسط مُطوّري JavaScript لأنّه لا يختلف عن الأسلوب الأوّل سوى في أنّ له عيوبًا ليست في الأوّل! فمثلاً: كتابة new Array(3) تعني إنشاء مصفوفة فيها ثلاث عناصر كلّها غير مُعرّفة، بدلاً من إنشاء مصفوفة فيها العدد 3 (أي بدلًا من [ 3 ]).

يمكن الوصول إلى خصائص المصفوفات (الّتي تُسمّى أحيانًا بعناصر المصفوفة (elements)) باستخدام صياغة الأقواس المربّعة ذاتها المستخدمة مع الكائنات، فكلّ عنصر يُعطى اسما تلقائيًّا بحسب موقعه في المصفوفة. ولكن عليك الحذر: فالأعداد تبدأ من الصّفر. لنُلق نظرة على مصفوفة فيها 3 عناصر:

تحذير: أسلوب غير مُفضّل

var myArray = [ 'a', 'b', 'c' ];
var firstItem = myArray[ "0" ]; // الوصول إلى العنصر الأوّل

عند الوصول إلى عناصر المصفوفة، فإنّه من الأسهل استخدام الأعداد لتحديد موضع العنصر المطلوب في المصفوفة، والّذي يُسمّى الدّليل (index):

var myArray = [ 'a', 'b', 'c' ];
var firstItem = myArray[ 0 ];

var secondItem = myArray[ 1 ]; // access the item at index 1
log( secondItem ); // logs 'b'

النّتيجة

يمكننا معرفة عدد العناصر في مجموعة بالوصول إلى الخاصّة length في المصفوفة:

var myArray = [ 'a', 'b', 'c' ];
var len = myArray.length;
log( len ); // logs 3

النّتيجة

حلقات for: سرد المصفوفات

بما أنّنا نعلم كيف نحدّد طول المصفوفة (عدد العناصر فيها)، وأنّ أوّل عناصرها يقع عند الدّليل 0، يمكننا إذن سرد عناصر المصفوفة باستخدام حلقة for:

النّتيجة

يمكننا فعل الكثير من الأشياء بالمصفوفات، راجع وثيقة المصفوفات في شبكة مُطوِّري موزيّلا للاطّلاع على الدّليل الكامل.

المنطق والتّحقّق من الصّحِّة

تُوفِّر JavaScript الكلمتين if وelse، وكذلك المُعامل الثّلاثي، للسّماح بتنفيذ بعض الأوامر فقط عند تحقّق شروط معيّنة. تُحدّد JavaScript توفّر شرطٍ ما بتحرِّي "صحِّة" قيمة أو عبارة. ولأنّ JavaScript لُغةٌ متغيّرة الأنواع (dynamically typed)، فإنّ بإمكاننا استخدام أيّة قيمة أو أي تركيبٍ من القيّم، إلّا أنّ قواعد التّحقّق من كون قيمةٍ مُعيّنة أو عبارةٍ معيّنة صحيحةً (true) أو خاطئة (false) قد تُسبّب بعض الارتباك.

فيما يلي مثال عن جملة شرطيّة (if) بسيطة في JavaScript. تتحرّى الجملة من صحِّة العدد 1؛ ولأنّ 1 قيمة "صائبة" (truthy)، فسيُنفَّذ النّص البرمجيّ الواقع بين القوسين المعكوفين {} بعد جملة if.

if ( 1 ) {
  // سيُنفَّذ هذا الأمر
  log( '1 is truthy' );
}

النّتيجة

الحقيقة أنّ معظم القيم في JavaScript "صائبة"، بل إنّ خمس قيمٍ فقط في اللّغة تُعتبر "خاطئة" (falsy):

  • undefined (القيمة المبدئيّة لكّل مُتغيّر يُفرض دون إسناد قيمة إليه)
  • null
  • NaN (ليس عددًا "not a number")
  • 0 (العدد صفر)
  • '' (سلسلة نصّيّة فارغة)

عندما نريد تحرّي "خطأ" قيمة ما، نستخدم المُعامل !:

var a = '';

if ( !a ) {
  // لن يُنفَّذ هذا الأمر إن كانت a صائبة
  log( 'a was falsy' );
}

النّتيجة

أمّا القيمة NaN فلها وضعٌ خاصّ، فعند تحرّيها في جملة شرطيّة بسيطة فإنّها خاطئة:

var notANumber = 'four' - 'five';

if ( !notANumber ) {
  // سيُفَّذ هذا الأمر
  log( '!notANumber was truthy' );
}

النّتيجة

أمّا عندما نقارن القيمة NaN مع false، فإنّنا نحصل على قيمة "خاطئة":

var notANumber = 'four' - 'five';

if ( notANumber == false ) {
  // لن يُنفَّذ هذا الأمر
  log( 'notANumber was falsy' );
} else {
  // سُينفَّذ هذا الأمر
  log( 'notANumber was truthy' );
}

النّتيجة

من المهمّ أن نتذكّر أنّ كلّ القيم الأخرى ما عدا الخمس السّابقة تُعتبر صائبة، بما في ذلك المصفوفات الفارغة والكائنات الفارغة وكل السّلاسل النّصيّة غير الفارغة (متضمّنة السلسلة النّصيّة '0') وكلّ الأعداد ما عدا 0.

ملاحظة: من الممكن كتابة جملة شرطيّة فيها if وelse في سطر واحدٍ دون استخدام الأقواس المعكوفة، ولكنّ هذه الممارسة غير مفضّلة لأنّها تجعل قراءة البرنامج وصيانته أصعب؛ وما ذكرناها هنا إلّا لأنّها قد تصادفك عند قراءة برامج مطوّرين آخرين.

المُعامِلات المنطقيّة (Logical Operators)

تسمح المُعاملات المنطقيّة بتقييم طرفيّ العمليّة المنطقيّة بتنفيذ عمليتيّ AND (&&) وOR (||).

var foo = 1;
var bar = 0;
var baz = 2;

foo || bar;     // تُعيد 1 وهي قيمة صائبة
bar || foo;     // تُعيد 1 وهي قيمة صائبة

foo && bar;     // تُعيد 0 وهي قيمة خاطئة
foo && baz;     // تُعيد 2 وهي قيمة صائبة

في حالة المُعامل ||، تكون القيمة المُعادة هي أوّل قيمة تُثبت صحّة الجُملة، أو آخر قيمة. أمّا في حالة المُعامل && فإنّ القيمة المُعادة تكون أوّل قيمة تُثبت خطأ الجملة، أو آخر قيمة.

قد ترى المُعاملات المنطقيّة تُستخدم بأسلوب ذكيّ للتّحكّم بسير البرنامج:

foo && bar();   // استدعِ الدالّة bar فقط عندما تكون foo صائبة

var bar = baz || createBar();  // استخدم baz كقيمة لـbar إلّا إن كانت
                               // خاطئة، وعندها أنشئ bar

هذا الأسلوب جميل ومُختصر، ولكنّه قد يزيد قراءة البرنامج صعوبةً، خصوصًا للمبتدئين. عليك أن تتعلّمه لتعرفه في برامج الآخرين، ولكننّني أنصحك بالنأي عن استخدامه في البداية، واستخدام if وelse التّقليديّتين لأنّهما أوضح.

المُعامِل الثّلاثي (Ternary Operator)

عادةً ما ترغب بتعيين قيمة مُتغيّر بحسب صحّة شرط معيّن أو خطئِه، بإمكانك بالطّبع استخدام if وelse:

تحذير: أسلوب غير مُفضّل

var propertyName;

if (dim === 'width') {
  propertyName = 'clientWidth';
} else {
  propertyName = 'clientHeight';
}

إلّا أنّ المُعامل الثّلاثيّ يسمح بتحقيق الغاية ذاتها بطريقة بأسلوب أقصر وأوضح:

var propertyName = ( dim === 'width' ) ? 'clientWidth' : 'clientHeight';

تُتحرَّى الجملة قبل ? لمعرفة صوابها، فإن كانت صائبة أُسندت القيمة الأولى (clientWidth) إلى المُتغيّر propertyName، وإلّا أُسنِدت القيمة الثّانية (clientWidth) إليه.

عثرات JavaScript

بالإضافة إلى نطاقات المُتغيّرات والتّحرّي من الصّحة، هنالك العديد من العثرات الأخرى في JavaScript، وسنلقي نظرة على بعضها.

تسمية الأشياء

تبدأ الأسماء السّليمة في JavaScript بحرف أو برمز مُعيّن، متبوعٍ أو غير متبوع بحروف أو أرقام أو علامات _ أو رموز أخرى. لا يمكن أن تبدأ الأسماء بأرقام أو أن تتضمّن إشارة النّاقص (-). سمِّ المتغيرات بالأسماء الّتي تحلو لك طالما التزمت بهذه القواعد، وهذه الأسماء كلّها سليمة:

  • a
  • a1
  • foo_bar
  • fooBarBaz
  • $fooBar
  • _foo
  • __foo__

وضع مجتمع JavaScript بعض الأعراف الشّائعة لتسمية المُتغيّرات والخصائص في JavaScript، لكنّها مُجرَّد أعراف، فهي ليست إلزاميّة، ولا تؤثّر في عمل البرنامج:

  • الأسماء الّتي تبدأ بـ_ تُشير عادةً إلى قيم سرِّيَّة (private) (سنوضِّح ذلك لاحقًا).
  • الأسماء الّتي تبدأ بحرف كبير هي عادةً دوال مُشيّدة (constructors) تُستخدم لإنشاء نُسخ جديدة من الكائنات (سنوضّح ذلك لاحقًا أيضًا).
  • في البرامج الّتي تستخدم jQuery، الأسماء الّتي تبدأ بالرّمز $ تُشير عادّة إلى كائنات jQuery.

الكلمات المحجوزة (Reserved words)

تحتفظ JavaScript لنفسها ببعض الكلمات الّتي يفضّل اجتنابها كأسماء لمكوّنات برامجك:

abstract boolean break byte case catch char class const continue debugger
default delete do double else enum export extends false final finally float
for function goto if implements import in instanceof int interface long
native new null package private protected public return short static super
switch synchronized this throw throws transient true try typeof var
volatile void while with

فإنّ كان لا بدّ من استخدامها كاسم لخاصّة كائن، فعليك إحاطتها بعلامتي اقتباس:

var myObject = {
  'class': 'tasty'
};

العمليّات على الأعداد والسّلاسل النّصيّة

التّعامل مع الأرقام في JavaScript قد يُعطي نتائج غير متوقّعة أحيانًا، بسبب طريقة تمثيل الأرقام داخليًّا في JavaScript، فمثلاً: عند جمع عددين عشريّين، تكون النّتيجة عدًدا قريبًا جدًّا من القيمة المتوقّعة، لكنّه غير مساوٍ تمامًا:

log( 0.0001 + 0.0002 ); // 0.00030000000000000003

النّتيجة

لن تصادف هذا كثيرًا عند العمل مع jQuery، ولكنّها قضيّة مهمّة يجب الانتباه لها خصوصًا عندما تتعامل مع أرقامٍ عشريّة كالعملات. عندما تُجري عمليّات حسابيّة تحتاج دقّة تامّة، فعليك تحويل القيم إلى أعداد صحيحة قبل العمل بها، ثمّ تحويلها ثانيةً إلى أعداد عشريّة بعد الانتهاء.

تتسامح JavaScript مع اختلاف الأنواع، فعند إجراء عمليات حسابية باستخدام قيم غير عدديّة، فلن تُبلغك JavaScript بوجود خطأ، بل ستُتابع العمليّة مُعطيةً نتائج قد لا تتوقّعها:

log( 'a' + 2 );           // 'a2'
log( '4' + 3 );           // '43'
log( 'five' - '4' );      // NaN (not a number)
log( - '1' );             // -1
log( 1 + true );          // 2
log( 1 == true );         // true
log( 1 === true );        // false

النّتيجة

مطالعة

لم نخض في أعماق لغة JavaScript بعدُ. شبكة مُطوّري موزيلّا (MDN) مصدر ممتاز (بالإنكليزيّة) لتعلّم JavaScript بتفاصيلها، وخصوصًا دليل JavaScript على الشّبكة. أكثر المواضيع أهمّيّة لك الآن:

مصادر إضافية

ترجمة (بشيء من التصرف) للجزء الأول من سلسلة  jQuery Fundamentals لمؤلّفتها Rebecca Murphey.


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

أفضل التعليقات

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



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...