ما لا تعرفه عن الأغراض في جافاسكريبت

إذا كنت ممن يتعاملون مع JavaScript بكثرة، فلا بد أنك وجدت الكثير من النقاط المربكة خلال مسيرتك، خصوصاُ قبل تعرفك على مكتبات مثل jQuery و YUI، وفي حال كنت ممن تعرفواً على اللغة من خلال إحدى مكتباتها فقد لا تعرف هذا النوع من المشاكل (أو ليس بعد على الأقل).

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

سنذكر هنا بعض تلك النواحي التي تخص الأغراض تحديداً، ونحاول ذكر المزيد في مقالات قادمة بإذن الله.

ما لا تعرفه عن الأغراض في جافاسكريبت

كل شيئ غرض

في جافاسكريبت كل شيئ يتصرف كغرض (Object) باستثناء undefined و null.


false.toString() // 'false'
[1, 2, 3].toString(); // '1,2,3'

function Foo(){}
Foo.bar = 1;
Foo.bar; // 1

هناك اعتقاد شائع بأن الأرقام لا تتصرف كأغراض في جافاسكريبت، لكن ذلك ليس صحيحاً. المشكلة هنا أن مفسر اللغة يحاول معالجة النقطة كجزء من الرقم (فاصلة عائمة).


2.toString(); // raises SyntaxError

تحل هذه المشكلة ببساطة بإحدى الطرق التالية:


2..toString(); // the second point is correctly recognized
2 .toString(); // note the space left to the dot
(2).toString(); // 2 is evaluated first

الوصول لخصائص غرض

يمكن ذلك بإحدى طريقتين: الوصول المباشر باستخدام النقطة (.) أو باستخدام الأقواس المربعة [].


var foo = {name: 'Kitten'}
foo.name; // kitten
foo['name']; // kitten

var get = 'name';
foo[get]; // kitten

foo.1234; // SyntaxError
foo['1234']; // works

الطريقتان متطابقتان من حيث التنفيذ والنتيجة باستثناء أن استخدام الأقواس المربعة يسمح بتحديد أسماء الخصائص بطريقة ديناميكية كما يسمح باستخدام أسماء قد تسبب أخطاء في الطريقة الأخرى (كبداية الاسم برقم).

حذف خصائص غرض

الطريقة الوحيدة الفعلية لتحقيق ذلك هو استخدام معامل delete. في حين أن إسناد undefined أو null إلى الخاصية يحذف قيمتها فقط، دون أن يحذفها من تعريف الغرض.


var obj = {
    bar: 1,
    foo: 2,
    baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;

for(var i in obj) {
    if (obj.hasOwnProperty(i)) {
        console.log(i, '' + obj[i]);
    }
}

الوراثة في جافاسكريبت

إن نموذج الوراثة في جافاسكريبت ليس كلاسيكياً، حيث تعتمد على ما يدعى بسلسلة التعريف (Prototype Chain). وقد اعتبر هذا النموذج ضعيفاً مقارنة بالكلاسيكي لعدم اعتياد جمهور المطورين عليه، إلا أن العكس هو الصحيح، حيث يمكنك أن تستخدمه لبناء نموذج مشابه للكلاسيكي وليس العكس.

لنتعرف على نموذج الوراثة في جافاسكريبت من خلال مثال وتحليل البنية الناتجة عنه:


function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// Set Bar's prototype to a new instance of Foo
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// Make sure to list Bar as the actual constructor
Bar.prototype.constructor = Bar;

var test = new Bar() // create a new bar instance

// The resulting prototype chain
test [instance of Bar]
    Bar.prototype [instance of Foo]
        { foo: 'Hello World' }
        Foo.prototype
            { method: ... }
            Object.prototype
                { toString: ... /* etc. */ }

في المثال السابق يرث test من Bar.prototype ومن Foo.prototype وبالتالي يمكنه الوصول للتابع method والخاصية value المعرفين ضمن Foo. من الضروري ملاحظة أن ()new Bar لا ينشئ غرضاً جديداً من نوع Foo وإنما يعيد استخدام الغرض الذي تم تعريفه أول مرة وبالتالي تؤثر جميع نسخ Bar على نفس خاصية value.

عند محاولة الوصول إلى أي خاصية ضمن غرض، يحاول مفسر اللغة العثور عليها ضمن سلسلة التعريف بالصعود مستواً تلو آخر إلى أن يصل إلى القمة (Object.prototype) ويعيد undefined عند عدم إيجاد الخاصية. تعتبر عملية البحث عن الخاصية مكلفة بالنسبة للأداء وزمن التنفيذ ويجب أخذ ذلك بعين الاعتبار.

يمكن إسناد أي غرض لخاصية prototype، إلا أن الأغراض البدائية (كالأرقام مثلاً) يتم تجاهلها.


function Foo() {}
Foo.prototype = 1; // no effect

hasOwnProperty

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


// Poisoning Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};

foo.bar; // 1
'bar' in foo; // true

foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true

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


var foo = {
    hasOwnProperty: function() {
        return false;
    },
bar: 'Here be dragons'
};

foo.hasOwnProperty('bar'); // always returns false

// Use another Object's hasOwnProperty and call it with 'this' set to foo
({}).hasOwnProperty.call(foo, 'bar'); // true

// It's also possible use the hasOwnProperty property from the Object property for this purpuse
Object.prototype.hasOwnProperty.call(obj, 'bar'); // true

تعلم المزيد من JavaScript Garden

معظم ما سبق هو ترجمة وأمثلة من القسم الأول لدليل JavaScript Garden، الذي يضم مجموعة تقنيات وحيل وتصحيح معتقدات في جافاسكريبت، يقوم عليها Ivo Wetzel في الكتابة، و Zhang Yi Jiang في التصميم.

حاول زيارة هذا الدليل بشكل دوري للاطلاع على التحديثات وشاركه مع زملائك.

محمود الدعاس

من مواليد دمشق. ابتدأ بعد التخرج كمبرمج ويب عام 2005 مع شركة إيميا، انتقل بعدها إلى شركة تكنوليد للأتمتة المتكاملة ومنها إلى شقيقتها أتمتة للحلول المتقدمة كمصمم غرافيك ومصمم ومطور ويب. أسس مع مجموعة من زملاء الدراسة موقع HOMELESS-PRO.com لنشر قصص مصورة باللغة العربية للشباب، و Guestra كوكالة تصميم وتطوير.

مواضيع الكاتبموقع الكاتب

لمتابعة الكاتب:
TwitterFacebookPinterest

2 thoughts on “ما لا تعرفه عن الأغراض في جافاسكريبت

أضف تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *