نگاه عمقی به متغیرها در Actionscript

شروع موضوع توسط yaa110 ‏25 اکتبر 2013 در انجمن برنامه فلش Flash

کلمات کلیدی:
  1. yaa110

    yaa110 کاربر فعال

    ارسال‌ها:
    206
    تشکر شده:
    507
    امتیاز دستاورد:
    16
    زمانی که بعنوان یک تازه کار قصد یادگرفتن یک زبان برنامه نویسی را داریم، برای تعریف "متغیر" یا "variable" یک عبارت آشنا وجود دارد: "متغیر محلی در حافظه است که می تواند مقادیر مختلفی را داشته باشد". همین تعریف ساده کافی است تا یک برنامه نویس تازه کار بتواند برنامه نویسی را شروع کند اما آیا می توان همیشه با درنظر گرفتن همین تعریف ساده برنامه نویسی را ادامه داد و به یک برنامه نویس حرفه ای تبدیل شد؟ جواب این سوال کاملا مشخص است، تا وقتی که ندانیم چه می نویسیم، نمی توانیم به سمت حرفه ای شدن پیش برویم حتی اگر انجام پروژه های بزرگ را در رزومه خود داشته باشیم. برای روشن شدن مطلب یک مثال ساده می زنم: "دو دوچرخه سوار را در نظر بگیرید که دیگران این دو را به نام حرفه ای می شناسند. یکی از این دو با ساختار دوچرخه خود آشناست و می داند که چطور کار می کند، اما نفر دوم فقط دوچرخه اش را سوار می شود و از ساز و کار آن بی اطلاع است. فرض کنید در میان راه دوچرخه های این دو نفر از کار بیفتد و نیاز به تعمیر داشته باشد، در این زمان است که فرق دوچرخه سوار واقعا حرفه ای و به اصطلاح حرفه ای متمایز می شود. دوچرخه سواری که با ساز و کار دوچرخه اش آشناست به راحتی آن را تعمیر می کند و به راه خود ادامه می دهد، اما نفر دوم در زمان تعمیر دوچرخه خود ممکن است حتی آسیب وارده را شدیدتر کرده و کاملا از ادامه راه باز بماند". بنابراین، این مطلب با این هدف نگاشته می شود تا تعاملی باشد برای حرفه ای شدن و حرفه ای نوشتن. امید است که با نظرات و نکات خوانندگان این مطلب، اطلاعات بیشتری برای نویسنده و سایر خوانندگان فراهم شود.

    در زبان برنامه نویسی Actionscript 3.0 تمامی "متغیرها" (البته بهتر است بگویم "مقادیر") object هستند و آن ها را می توان به دو دسته Primitive و Complex تقسیم کرد. تمامی مقادیر int، String، Number، Boolean، uint از نوع Primitive هستند درحالی که سایر مقادیر مثل Array، Date، Function، Error، RegExp، XML و ... از نوع Complex به شمار می روند. تفاوت اصلی این دو نوع مقدار در سرعت محاسبه آن ها و نحوه اشغال فضا در حافظه (Memory) می باشد به طوری که کارکردن با مقادیر Primitive بسیار سریعتر از کارکردن با مقادیر Complex است. علت اصلی این امر این است که Actionscript مقادیر داخلی را به صورت immutable ذخیره می کند. با این تعریف متغیرها به دو دسته immutable (تغییرناپذیر) و mutable (تغییر پذیر) تقسیم می شوند. حال این سوال ممکن است مطرح شود که اگر متغیرهای immutable غیر قابل تغییر هستند پس چرا به آن ها متغیر می گویند؟ برای پاسخ با این تناقض ظاهری نیاز است که مشخصه های هر متغیر را بشناسیم.

    • مشخصه های هر متغیر

    می توانیم متغیرها را متشکل از شش مشخصه در نظر بگیریم: 1- نام متغیر، 2- مقدار متغیر، 3- نوع متغیر، 4- موقعیت متغیر در حافظه (آدرس متغیر)، 5- حوزه تعریف (Scope) متغیر، 6- طول عمر متغیر که به صورت خودکار توسط Actionscript تعیین می شود. به مثال زیر توجه کنید:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]var global_var:String = "Global";[/FONT][/COLOR]

    function func() {
        var local_var:String = "Local";
        trace(local_var); // Output: Local
    }


    trace(global_var); // Output: Global
    [COLOR=#222222][FONT=Tahoma]trace(local_var); // Error: Access of undefined property local_var[/FONT][/COLOR]
    همانطور که در این مثال مشخص است دو متغیر با نام های global_var و local_var ایجاد شده است که هر دوی آن ها از نوع String هستند (پس مقدارشان Primitive به شمار می رود). مقدار این دو متغیر هم با علامت تخصیص (=) تعیین شده است. یکی از تفاوت های اصلی این دو متغیر حوزه تعریف آن هاست. در Actionscript حوزه تعریف و استفاده از هر متغیر به بلاکی که در آن قرار دارد محدود می شود. بنابراین متغیر local_var تنها در داخل تابع func قابل استفاده است. درحالیکه از متغیر global_var در همه جا (حتی داخل تابع func) نیز می توان استفاده کرد. استفاده از یک متغیر در خارج از حوزه تعریف آن منجر به بروز خطای access می شود.

    حال نوبت به این می رسد که موقعیت هر متغیر بر روی حافظه را تعیین کنیم. برای این کار در سایر زبان های برنامه نویسی مثل Python تابعی وجود دارد که آدرس هر متغیر را در حافظه مشخص می کند، اما در زبان Actionscript تابعی برای این منظور در نظر گرفته نشده است. با این حال با استفاده از برخی کدها (تخصیص غیر صحیح یک متغیر از یک نوع به نوع دیگر) می توان موقعیت متغیر را در حافظه مشخص کرد. به مثال زیر دقت کنید:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]import flash.utils.ByteArray;[/FONT][/COLOR]

    var list:Array = [1,2,3,4,5];
    [COLOR=#222222][FONT=Tahoma]trace(ByteArray(list));[/FONT][/COLOR]
    بعد از اجرای این مثال، خطای زیر را مشاهده خواهید کرد:

    کد (Text):
    TypeError: Error #1034: Type Coercion failed: cannot convert []@1d5e6191 to flash.utils.ByteArray.
    همونطور که مشاهده می کنید، در بخشی از این متن خطا یک کد 8 حرفی در مقابل علامت @ نوشته شده است، این کد همان موقعیت شی list در حافظه است. برای ساده تر شدن دسترسی به این کد می توانیم کلاسی داشته باشیم که این کد را به راحتی با یک تخصیص غیر صحیح استخراج کند:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]package ir.flashcenter {[/FONT][/COLOR]

        public class MemoryLocation {


            public function id(object:*):String {
                var _location:String = "";
                try {
                    CatchClass(object);
                } catch (e:TypeError) {
                    trace(e.errorID);
                    _location = String(e).replace(/.*([@|\$].*?) to .*$/gi, '$1');
                }
                return _location;
            }


        }


    }


    [COLOR=#222222][FONT=Tahoma]internal final class CatchClass { }[/FONT][/COLOR]
    در این کلاس، یک کلاس داخلی به نام CatchClass تعریف شده است و در هر مرتبه که یک شی به این کلاس معرفی شود، یک تخصیص نامعتبر به آن شی اعمال خواهد شد و متن خطا برگردانده خواهد شد. برای استخراج کد از دستورهای RegExp استفاده شده است (برای اطلاعات بیشتر RegExp in AS3 را در گوگل جستجو کنید).

    حال با استفاده از این کلاس، immutable بودن مقادیر Primitive را بررسی می کنیم. به مثال زیر دقت کنید:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]import ir.flashcenter.MemoryLocation;[/FONT][/COLOR]

    var a:int = 14;
    var b:int = 14;


    trace(new MemoryLocation().id(a));
    trace(new MemoryLocation().id(b));
    [COLOR=#222222][FONT=Tahoma]trace(new MemoryLocation().id(14));[/FONT][/COLOR]
    بعد از اجرای این کدها، خروجی زیر را مشاهده خواهید کرد:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]TypeError: Error #1034: Type Coercion failed: cannot convert 14 to CatchClass.[/FONT][/COLOR]TypeError: Error #1034: Type Coercion failed: cannot convert 14 to CatchClass.
    [COLOR=#222222][FONT=Tahoma]TypeError: Error #1034: Type Coercion failed: cannot convert 14 to CatchClass.[/FONT][/COLOR]
    همانطور که می بینید برای دو متغیر a و b خطای مشابه با مقدار 14 برگردانده شده است. زمانی که یک مقدار Primitive (مثل int) به یک متغیر با یک نام خاص (مثل a یا b) تخصیص داده می شود، در واقع نام این متغیر یک ارجاع (Reference) به آن مقدار Primitive در حافظه است. بنابراین اگر برای مثال مقدار متغیر را به صورت زیر تغییر دهیم:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]var a:int = 14;[/FONT][/COLOR]trace(new MemoryLocation().id(a));
    // Output: TypeError: Error #1034: Type Coercion failed: cannot convert 14 to CatchClass.


    a = 15;
    trace(new MemoryLocation().id(a));
    [COLOR=#222222][FONT=Tahoma]// Output: TypeError: Error #1034: Type Coercion failed: cannot convert 15 to CatchClass.[/FONT][/COLOR]
    در واقع در این حالت متغیر a را به یک مقدار Primitive دیگر (در این مثال 15) در حافظه ارجاع داده ایم و مقدار 14 در حافظه تغییر نکرده است (چون یک مقدار immutable است) و همچنان باقی است. زمانی که هیچ ارجاعی به یک مقدار Primitive داده نشود، این مقدار توسط Garbage Collector از حافظه پاک خواهد شد. پس اگر مقدار متغیر b را هم تغییر دهیم، دیگر ارجاعی به مقدار 14 وجود نخواهد داشت و این مقدار Primitive از حافظه حذف خواهد شد (طول عمر متغیر).

    اما در مورد مقادیر Complex که mutable هستند، نحوه آدرس دهی متغیر بر روی حافظه متفاوت است. به مثال زیر دقت کنید که در آن دو متغیر از نوع Array و با مقادیر یکسان ایجاد شده است:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]import ir.flashcenter.MemoryLocation;[/FONT][/COLOR]

    var c:Array = [1, 2, 3, 4, 5];
    var d:Array = [1, 2, 3, 4, 5];


    trace(new MemoryLocation().id(c));
    [COLOR=#222222][FONT=Tahoma]trace(new MemoryLocation().id(d));[/FONT][/COLOR]
    خروجی این کد به صورت زیر خواهد بود:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]@208b8191[/FONT][/COLOR][COLOR=#222222][FONT=Tahoma]@208b81c1[/FONT][/COLOR]
    همانطور که مشاهده می کنید، با اینکه هر دو متغیر c و d مقدار مشابهی داشتند اما آدرس آن ها کاملا متفاوت است. علت این امر تغییرپذیر بودن مقادیر Complex است. در این حالت حتی اگر یک مقدار جدید به آرایه c یا d اضافه شود، آدرس آن تغییر نخواهد کرد:

    کد (Text):
    [COLOR=#222222][FONT=Tahoma]import ir.flashcenter.MemoryLocation;[/FONT][/COLOR]

    var c:Array = [1, 2, 3, 4, 5];
    trace(new MemoryLocation().id(c)); // Output: @208b8191


    c.push(6);
    [COLOR=#222222][FONT=Tahoma]trace(new MemoryLocation().id(c)); // Output: @208b8191[/FONT][/COLOR]
    در این حالت مقدار Complex تغییر کرده است، اما آدرس آن در حافظه ثابت مانده است، یعنی مقادیر Complex به صورت mutable (تغییرپذیر) هستند. اما مقادیر Primitive مقدارشان ثابت است و قابل تغییر نیست، یعنی با تغییر ظاهری متغیر آدرس ارجاع آن نیز تغییر خواهد کرد.

    منبع:
    http://flashcenter.ir/fa/1392/08/03/نگاه-عمقی-به-متغیرها-در-actionscript-3-0/
     
    نوشته شده توسط yaa110 در ‏25 اکتبر 2013
    Dorhato، shahin_3000_v، raminhamidi53 و 8 نفر دیگر از این ارسال تشکر کرده اند.

به اشتراک بگذارید