วิธีกำหนดออบเจ็กต์ JS นี้มีจุดประสงค์อะไรหรือไม่?


87

ฉันกำลังรักษารหัสเดิมอยู่และสังเกตเห็นว่ามีการใช้รูปแบบต่อไปนี้ในการกำหนดวัตถุ:

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

มีจุดประสงค์ใดในการนี้หรือไม่? เทียบเท่ากับการทำสิ่งต่อไปนี้หรือไม่?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

ฉันไม่ได้กำลังจะเริ่มต้นในภารกิจศักดิ์สิทธิ์เพื่อปรับโครงสร้างโค้ดเบสทั้งหมดตามความชอบของฉัน แต่ฉันอยากเข้าใจเหตุผลเบื้องหลังวิธีการกำหนดวัตถุในวงเวียน

ขอบคุณ!


1
ในตัวอย่างที่แน่นอนของคุณไม่มีความแตกต่าง หากคุณขยายมันอาจมีความแตกต่าง แต่ก็จะมีแนวทางต่างๆที่เข้ามาในการเล่นเช่นกัน
Travis J

It makes no difference, objects are passed as a copy of a reference so to speak, so even when defining the myFunction inside the IIFE, it's still accessible outside it.
adeneo

1
@adeneo Not for this example, by myFunction could use some variables defined outside of itself that would not be accessible from the outside. See my answer
Juan Mendes

2
possible duplicate of What is this JavaScript pattern called and why is it used? (not sure whether I should close). See also JavaScript Namespace Declaration or this one.
Bergi

คำตอบ:


116

It's called the module pattern http://toddmotto.com/mastering-the-module-pattern/

เหตุผลหลักคือคุณต้องสร้างวิธีการและตัวแปรที่เป็นส่วนตัวอย่างแท้จริง ในกรณีของคุณไม่มีความหมายเนื่องจากไม่ได้ซ่อนรายละเอียดการใช้งานใด ๆ

นี่คือตัวอย่างที่เหมาะสมในการใช้รูปแบบโมดูล

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

ในขณะที่คุณเพิ่งใช้ของตรงadderและvalueจะกลายเป็นสาธารณะ

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

และคุณสามารถทำลายมันได้โดยทำสิ่งต่างๆเช่น

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

แต่มีรุ่นโมดูล

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3

2
นี่ไม่ได้ตอบคำถามจริงๆว่าความแตกต่างระหว่างสองทางเลือกคืออะไร?

20
@torazaburo ตัวอย่างของ OP ไม่ใช่ตัวอย่างที่ดีฉันได้ให้ตัวอย่างจริงที่แสดงให้เห็นว่าเมื่อใดควรใช้รูปแบบโมดูล
Juan Mendes

สิ่งนี้ns.getNext: function () {จะไม่รวบรวม
ลงโทษ

ฉันจะมีถ้าฉันแน่ใจว่าจะแก้ไขได้อย่างไร delete MyNameSpace.getNextผมคิดว่ามีโครงสร้างบางส่วนเพื่อป้องกันไม่ให้
punund

2
@punund JS ไม่มีคอมไพเลอร์ แต่มีล่าม:)
frogatto

22

จุดประสงค์คือเพื่อ จำกัด การเข้าถึงของฟังก์ชันภายในการปิดเพื่อช่วยป้องกันไม่ให้สคริปต์อื่นเรียกใช้โค้ด โดยการพันรอบการปิดคุณกำลังกำหนดขอบเขตของการดำเนินการใหม่สำหรับโค้ดทั้งหมดภายในการปิดและสร้างขอบเขตส่วนตัวได้อย่างมีประสิทธิภาพ ดูบทความนี้สำหรับข้อมูลเพิ่มเติม:

http://lupomontero.com/using-javascript-closures-to-create-private-scopes/

จากบทความ:

ปัญหาที่ทราบกันดีที่สุดอย่างหนึ่งใน JavaScript คือการขึ้นอยู่กับขอบเขตทั่วโลกซึ่งโดยพื้นฐานแล้วหมายความว่าตัวแปรใด ๆ ที่คุณประกาศนอกฟังก์ชันอาศัยอยู่ในพื้นที่ชื่อเดียวกันนั่นคือวัตถุหน้าต่างที่เป็นลางไม่ดี เนื่องจากลักษณะของหน้าเว็บสคริปต์จำนวนมากจากแหล่งที่มาที่แตกต่างกันสามารถ (และจะ) ทำงานบนหน้าเดียวกันโดยใช้ขอบเขตร่วมกันทั่วโลกและนี่อาจเป็นสิ่งที่แย่มากเนื่องจากอาจนำไปสู่การชนกันของชื่อ (ตัวแปรที่มีชื่อเดียวกัน ถูกเขียนทับ) และปัญหาด้านความปลอดภัย เพื่อลดปัญหาเราสามารถใช้การปิดที่มีประสิทธิภาพของ JavaScript เพื่อสร้างขอบเขตส่วนตัวซึ่งเราสามารถมั่นใจได้ว่าตัวแปรของเราจะมองไม่เห็นสคริปต์อื่น ๆ บนหน้า



รหัส:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function

1
myFunctionไม่อยู่ในขอบเขตส่วนกลางในเวอร์ชันที่สอง วัตถุประสงค์คือเพื่อจัดเตรียมขอบเขตที่สามารถกำหนดฟังก์ชันเสริมภายในได้
Barmar

myFunctionmyObjectอยู่ในขอบเขตทั่วโลกเพราะมันถูกกำหนดไว้ในวัตถุโลก ในเวอร์ชันที่สองโค้ดอื่น ๆ ในแอปพลิเคชันสามารถดำเนินการmyFunctionได้ ในรุ่นแรกรหัสเท่านั้นภายในการปิดสามารถเข้าถึงmyFunction
Jonathan Crowe

ไม่myFunctionสามารถเรียกMyObject.myFunction()ใช้งานได้เหมือนกับเวอร์ชันแรกเท่านั้น
Barmar

@JonathanCrowe ตัวอย่างของ OP ไม่ใช่ตัวอย่างที่ดี แต่เป็นการเปิดเผยทุกสิ่งภายในโมดูลใช่มันสามารถเข้าถึงได้จากภายนอก ดูคำตอบของฉันสำหรับกรณีโมดูลที่มีประโยชน์
Juan Mendes

@JuanMendes จุดดีตัวอย่างของ OP ไม่ใช่การใช้รูปแบบโมดูลอย่างดีเยี่ยม
Jonathan Crowe

6

ข้อดี:

  1. รักษาตัวแปรในขอบเขตส่วนตัว

  2. คุณสามารถขยายการทำงานของวัตถุที่มีอยู่ได้

  3. ประสิทธิภาพเพิ่มขึ้น

ฉันคิดว่าสามจุดง่ายๆข้างต้นเพียงพอที่จะปฏิบัติตามกฎเหล่านั้น และเพื่อให้มันเรียบง่ายมันไม่มีอะไรนอกจากการเขียนฟังก์ชันภายใน


6

ในกรณีเฉพาะที่คุณแสดงไม่มีความแตกต่างในแง่ของฟังก์ชันการทำงานหรือการมองเห็น

เป็นไปได้ว่า coder ดั้งเดิมใช้แนวทางนี้เป็นเทมเพลตประเภทหนึ่งที่ทำให้เขาสามารถกำหนดตัวแปรส่วนตัวที่สามารถใช้ในการกำหนดสิ่งต่างๆเช่นmyFunction:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

สิ่งนี้หลีกเลี่ยงการคำนวณ seconds_per_dayทุกครั้งที่มีการเรียกใช้ฟังก์ชันในขณะเดียวกันก็ป้องกันไม่ให้เกิดมลพิษในขอบเขตทั่วโลก

อย่างไรก็ตามไม่มีอะไรแตกต่างไปจากนั้นและแค่พูด

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

โค้ดเดอร์ดั้งเดิมอาจต้องการให้สามารถเพิ่มฟังก์ชันให้กับอ็อบเจ็กต์โดยใช้ไวยากรณ์แบบประกาศroot.myFunction = functionแทนที่จะเป็นไวยากรณ์ของอ็อบเจ็กต์ / คุณสมบัติของmyFunction: functionทรัพย์สินของ แต่ความแตกต่างนั้นเป็นเรื่องของความชอบเป็นหลัก

อย่างไรก็ตามโครงสร้างที่ใช้โดย coder ดั้งเดิมมีข้อได้เปรียบที่สามารถเพิ่มคุณสมบัติ / วิธีการอื่น ๆ ในโค้ดได้อย่างง่ายดาย:

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

บรรทัดล่างไม่จำเป็นต้องใช้แนวทางนี้หากคุณไม่จำเป็นต้องทำ แต่ก็ไม่จำเป็นต้องเปลี่ยนเพราะมันทำงานได้ดีอย่างสมบูรณ์และมีข้อดีอยู่บ้าง


6
  1. รูปแบบแรกสามารถใช้เป็นโมดูลที่รับวัตถุและส่งคืนวัตถุนั้นด้วยการปรับเปลี่ยนบางอย่าง กล่าวอีกนัยหนึ่งคุณสามารถกำหนดโมดูลดังกล่าวได้ดังนี้

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }
    

    และใช้มันเช่น:

    var obj = {};
    module(obj);
    

    ดังนั้นข้อดีคือสามารถนำโมดูลนี้กลับมาใช้ใหม่เพื่อใช้ในภายหลังได้


  1. ในรูปแบบแรกคุณสามารถกำหนดขอบเขตส่วนตัวเพื่อจัดเก็บข้อมูลส่วนตัวของคุณเช่นคุณสมบัติส่วนตัวและวิธีการ ตัวอย่างเช่นพิจารณาตัวอย่างนี้:

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);
    

  1. รูปแบบนี้สามารถใช้เพื่อเพิ่มวิธีการหรือคุณสมบัติให้กับวัตถุทุกประเภทเช่นอาร์เรย์ตัวอักษรของวัตถุฟังก์ชัน

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12
    

ในความคิดของฉันข้อได้เปรียบหลักอาจเป็นข้อได้เปรียบประการที่สอง (การสร้างขอบเขตส่วนตัว)


5

รูปแบบนี้มีขอบเขตที่คุณสามารถกำหนดฟังก์ชันตัวช่วยที่มองไม่เห็นในขอบเขตส่วนกลาง:

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo เป็นฟังก์ชันที่ไม่ระบุตัวตนซึ่งไม่สามารถอ้างอิงจากภายนอกได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.