`ฟังก์ชันใหม่ ()` ด้วยตัวพิมพ์เล็ก“ f” ใน JavaScript


106

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

    var someObj = new function () {
        var inner = 'some value';
        this.foo = 'blah';

        this.get_inner = function () {
            return inner;
        };

        this.set_inner = function (s) {
            inner = s;
        };
    };

ทันทีที่ใช้ "this" จะกลายเป็นสมบัติสาธารณะของ someObj ดังนั้น someObj.foo, someObj.get_inner () และ someObj.set_inner () จึงมีให้บริการแบบสาธารณะ นอกจากนี้ set_inner () และ get_inner () เป็นวิธีการที่ได้รับสิทธิพิเศษดังนั้นจึงสามารถเข้าถึง "inner" ได้โดยการปิด

อย่างไรก็ตามฉันไม่เห็นการอ้างอิงถึงเทคนิคนี้ที่ใดเลย แม้แต่ JSLint ของ Douglas Crockford ก็บ่นเกี่ยวกับเรื่องนี้:

  • การก่อสร้างแปลก ๆ ลบ "ใหม่"

เรากำลังใช้เทคนิคนี้ในการผลิตและดูเหมือนว่าจะทำงานได้ดี แต่ฉันค่อนข้างกังวลเกี่ยวกับเรื่องนี้เพราะไม่ได้บันทึกไว้ที่ใด ไม่มีใครรู้ว่านี่เป็นเทคนิคที่ถูกต้องหรือไม่?


6
ฉันชอบโครงสร้างของคุณมากกว่า IIFE ('ฟังก์ชันเรียกใช้ทันที') 1: คุณไม่จำเป็นต้องมีวัตถุ 'อินสแตนซ์' ที่ชัดเจนนั่นคือสิ่งที่ 'นี่' คือใน JavaScript 2: คุณไม่จำเป็นต้องคืนอะไรซึ่งหมายความว่าคุณไม่จำเป็นต้องจำ แม้แต่ผู้เขียนคำตอบที่ยอมรับก็ลืมส่งคืนวัตถุอินสแตนซ์ในตอนแรก! ผู้คนมักชอบใช้ IIFE หากพวกเขาเกลียดสิ่งใหม่และสิ่งนี้ด้วยเหตุผลที่ดี - หากคุณมีฟังก์ชันที่จัดการกับเหตุการณ์ DOM thisจะอ้างถึงองค์ประกอบที่ทำให้เหตุการณ์เริ่มต้นขึ้นไม่ใช่วัตถุของคุณ แต่คุณสามารถมีได้var instance = thisแทน
Lee Kowalkowski

1
เหตุใดคำถามจึงสำคัญที่ต้องระบุ "ตัวพิมพ์เล็ก f"
ClearCloud8

7
เนื่องจากใน Javascript มีฟังก์ชัน 'Function' อยู่ด้วย (ตัวพิมพ์ใหญ่ F) ซึ่งแตกต่างกัน: Functionคือฟังก์ชันตัวสร้างที่สามารถสร้างอ็อบเจกต์ฟังก์ชันใหม่ได้ในขณะที่ฟังก์ชันเป็นคีย์เวิร์ด
Stijn de Witt


@Bergi ฉันอ่านลิงค์ของคุณ ฉันไม่เห็นเหตุผลที่จะทำให้เสียชื่อเสียงรูปแบบนี้ มันใช้ได้ มันง่าย มีอะไรผิดปกติ JSLint บ่นเกี่ยวกับ BTW ทุกอย่าง :)
Stijn de Witt

คำตอบ:


64

ฉันเคยเห็นเทคนิคนั้นมาก่อนมันถูกต้องคุณกำลังใช้นิพจน์ฟังก์ชันราวกับว่ามันเป็นฟังก์ชันตัวสร้าง

แต่ IMHO คุณสามารถบรรลุสิ่งเดียวกันได้ด้วยนิพจน์ฟังก์ชันเรียกใช้อัตโนมัติฉันไม่เห็นจุดที่จะใช้ตัวnewดำเนินการในลักษณะนั้น:

var someObj = (function () {
    var instance = {},
        inner = 'some value';

    instance.foo = 'blah';

    instance.get_inner = function () {
        return inner;
    };

    instance.set_inner = function (s) {
        inner = s;
    };

    return instance;
})();

วัตถุประสงค์ของตัวnewดำเนินการคือการสร้างอินสแตนซ์ออบเจ็กต์ใหม่การตั้งค่า[[Prototype]]คุณสมบัติภายในคุณสามารถดูว่าสิ่งนี้ทำโดย[Construct]คุณสมบัติภายในอย่างไร

โค้ดด้านบนจะให้ผลลัพธ์ที่เทียบเท่า


1
ข้อกำหนด ECMAScript 262 ในส่วนที่ 13 อธิบายเพิ่มเติมอย่างเป็นทางการเล็กน้อย สิ่งที่ต้องการfunction foo () {}ส่งคืนผลลัพธ์ของการสร้างFunctionวัตถุ [สันนิษฐานว่ามีฟังก์ชันใหม่ ()] มันคือน้ำตาลไวยากรณ์
Clinton Pierce

3
ฉันคิดว่าคุณหายไปreturn instance;ในตอนท้าย มิฉะนั้นจะเป็นเพียงsomeObj undefined:-)
Matthew Crumley

1
ฉันขอแนะนำว่าหากคุณสนใจเกี่ยวกับการแยกส่วนและการซ่อนข้อมูลคุณก็เลิกใช้สิ่งนี้และเริ่มใช้สิ่งที่ต้องการเช่น require.js คุณมาครึ่งทางแล้วทำไมหยุดที่นี่ นิยามโมดูลอะซิงโครนัส (ซึ่งเป็นสิ่งที่ต้องใช้ js) สนับสนุนกรณีการใช้งานนี้และให้ชุดเครื่องมือทั้งหมดแก่คุณเพื่อจัดการกับการกำหนดขอบเขตเนมสเปซและการจัดการการพึ่งพา
Stijn de Witt

โปรดทราบว่าวงเล็บที่อยู่รอบ ๆ การประกาศฟังก์ชันนั้นไม่จำเป็นเนื่องจากคำสั่งดังกล่าวเป็นนิพจน์แล้วเนื่องจากมี=
Explosion Pills

@StijndeWitt ครึ่งหนึ่งหมายความว่าคุณจะต้องทำงานสองครั้งเพื่อใช้ require.js แต่นี่อาจเป็นสิ่งที่คุณต้องการในกรณีง่ายๆ
xr280xr

15

รหัสของคุณคล้ายกับโครงสร้างที่แปลกน้อยกว่า

function Foo () {
    var inner = 'some value';
    this.foo = 'blah';

    ...
};
var someObj = new Foo;

9
ไม่เพียง แต่คล้ายเท่านั้น แต่ยังเหมือนเดิมทุกประการ ... โดยมีข้อยกเว้นเพียงอย่างเดียวคือพวกเขาจะไม่สามารถนำ Foo กลับมาใช้ใหม่เพื่อสร้างวัตถุอื่นได้
kikito

3
รุ่น OP อาจจะนำกลับมาใช้ผ่านทางsomeObj.constructor ใหม่ ที่นี่ตัวสร้างจะถูกเพิ่มลงในเนมสเปซอย่างชัดเจน สไตล์ที่เหมาะสมขึ้นอยู่กับวัตถุประสงค์ที่ตั้งใจไว้ของฟังก์ชัน นอกจากนี้รูปแบบนี้ - แม้ว่าแน่นอนมาตรฐาน - ช่วยให้คนเติม namespace โลกถ้าพวกเขาลืมใหม่ก่อนที่จะฟู
ราคา J Bryan

@kikito คุณหมายความว่าอย่างไรสิ่งนี้ไม่อนุญาตให้ใช้ Foo ซ้ำเพื่อสร้างวัตถุอื่น var newObj = new Foo () ควรจะสร้างอินสแตนซ์ใหม่
Bill Yang

1
@BillYang นั่นก็ 5 ปีแล้ว ไม่มีความเห็น. ฉันไม่ได้แตะจาวาสคริปต์เลยตั้งแต่นั้นมา
kikito

12

เพื่อชี้แจงบางแง่มุมและทำให้ JSLint ของ Douglas Crockford ไม่บ่นเกี่ยวกับโค้ดของคุณนี่คือตัวอย่างบางส่วนของการสร้างอินสแตนซ์:

1. o = new Object(); // normal call of a constructor

2. o = new Object;   // accepted call of a constructor

3. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
})(); // normal call of a constructor

4. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
}); // accepted call of a constructor

ในตัวอย่างที่ 3 นิพจน์ใน (... ) เป็นค่าเป็นฟังก์ชัน / ตัวสร้าง ดูเหมือนว่าใหม่ (ฟังก์ชัน () {... }) () ดังนั้นหากเราไม่ใส่วงเล็บปิดท้ายตามตัวอย่างที่ 2 นิพจน์ยังคงเป็นการเรียกตัวสร้างที่ถูกต้องและดูเหมือนตัวอย่างที่ 4

JSLint ของ Douglas Crockford "คิดว่า" คุณต้องการกำหนดฟังก์ชันให้กับ someObj ไม่ใช่อินสแตนซ์ และท้ายที่สุดมันเป็นเพียงคำเตือนไม่ใช่ข้อผิดพลาด

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