JavaScript หลีกเลี่ยงคำหลักใหม่

ฉันกำลังอ่านหน้านี้ (โดยเฉพาะในส่วนของโรงงาน)

มีการกล่าวถึงเพื่อหลีกเลี่ยงการใช้คีย์เวิร์ด new เพื่อป้องกันกรณีลืมโดยไม่ตั้งใจ แนะนำให้ใช้โรงงาน

ตัวอย่างใหม่ของเพจ:

function Bar() {
    var value = 1;
    return {
        method: function() {
            return value;
        }
    }
}
Bar.prototype = {
    foo: function() {}
};

new Bar(); 
Bar(); // These are the same.

ตัวอย่างโรงงานของเพจ:

function Foo() {
    var obj = {};
    obj.value = 'blub';

    var private = 2;
    obj.someMethod = function(value) {
        this.value = value;
    }

    obj.getPrivate = function() {
        return private;
    }
    return obj;
}

ข้อเสียจากโรงงาน:

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

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

วิธีแก้ปัญหาของฉัน:

var Foo = function () {
    var foo = function () {

    };
    foo.prototype = {
        bar: function () { }
    };
    return new foo();
};

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


person Shelby115    schedule 17.07.2014    source แหล่งที่มา
comment
ใช่ สิ่งนี้จะสร้างคอนสตรัคเตอร์ใหม่ทุกครั้งที่คุณเรียกใช้เมธอดของโรงงาน   -  person soktinpk    schedule 18.07.2014
comment
ฉันชี้แจงคำถามของฉันเล็กน้อย ฉันรู้ว่าฉันสร้างวัตถุใหม่ทุกครั้ง ฉันแค่อยากรู้ว่ามันจะลบข้อเสียของวิธีการจากโรงงานที่ระบุไว้ในไซต์ที่เชื่อมโยงหรือไม่ (ซึ่งฉันรวมไว้ในคำถามของฉันตอนนี้) หรือเพิ่มข้อดี/ข้อเสียในเรื่องนั้น   -  person Shelby115    schedule 18.07.2014
comment
อย่าเชื่อมัน! ดังที่ตัวอย่างโค้ดทั้งหมดนี้แสดงให้เห็น ยังมีสิ่งที่แย่กว่านั้นที่อาจผิดพลาดได้ มีวิธีที่ดีกว่าในการตอบโต้การลืม news   -  person Bergi    schedule 18.07.2014


คำตอบ (1)


เอาล่ะ มาดูตัวอย่าง new กัน:

function Bar() {
    var value = 1;
    // Whoops, sorry
    // As Bergi points out, if you have a return value then that overrides the normal object that is returned by new
    // Didn't even notice you have a return in here!
    /*
    return {
        method: function() {
            return value;
        }
    }
    */
    // This does the same thing (approximately) but now calling "(new Bar()) instanceof Bar" will be true
    this.method = function() {
        return value;
    };
}
Bar.prototype = {
    foo: function() {}
};

var b = new Bar();

ในคอนโซล Google Chrome b มีคุณสมบัติชื่อ __proto__ โดยทั่วไป เมื่อคุณโทร b.foo() ขั้นแรกเบราว์เซอร์จะมองหาวิธีการที่เรียกว่า foo หากไม่พบ จะปรากฏใน b.__proto__ และ b.__proto__.__proto__ ไปเรื่อยๆ

ประกาศ: b.__proto__ === Bar.prototype

นั่นหมายความว่าหากคุณโทร new Bar() ซ้ำๆ พวกเขาทั้งหมดจะมี __proto__ เหมือนกัน ซึ่งจะช่วยประหยัดหน่วยความจำ (สิ่งนี้มีผลข้างเคียงหากคุณกลายพันธุ์ Bar.prototype มันจะเปลี่ยนอินสแตนซ์ทั้งหมดของ Bar's __proto__ เช่นกัน)

มาดูวิธีการจากโรงงานของคุณกัน:

var Foo = function () {
    var foo = function () {

    };
    foo.prototype = {
        bar: function () { }
    };
    return new foo();
};

สิ่งนี้ไม่ช่วยประหยัดหน่วยความจำเพราะมันจะสร้าง prototype ใหม่ และตัวสร้างใหม่ ทุกครั้ง ที่คุณเรียก Foo() กล่าวอีกนัยหนึ่งถ้า

var f1 = new Foo(), f2 = new Foo();

คืนค่าเท็จต่อไปนี้: f1.constructor == f2.constructor และ f1.__proto__ == f2.__proto__ สิ่งนี้หมายความว่า? หมายความว่า f1 และ f2 ไม่ แชร์ prototype เหมือนกัน ดังนั้นจึงต้องทำซ้ำออบเจ็กต์ทุกครั้ง บางทีคุณอาจทำสิ่งนี้ได้:

var fooProto = {
  callFoo: function() { alert("test"); }
};
function Foo() {
    var foo = function() {};
    foo.prototype = fooProto;
    return new foo();
};

สิ่งนี้จะใช้จำนวนหน่วยความจำเท่ากันกับตัวสร้างปกติ

แก้ไขด้านข้าง: เบราว์เซอร์สมัยใหม่มีฟังก์ชันในตัวที่ทำงานคล้าย Foo ในตัวอย่างสุดท้าย คุณสามารถใช้ Object.create(fooProto) (แต่เฉพาะในเบราว์เซอร์รุ่นใหม่เท่านั้น)

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

person soktinpk    schedule 17.07.2014
comment
1. การย้าย foo & foo.prototype ออกไปนอกฟังก์ชัน Foo จะแก้ปัญหานั้นได้หรือไม่ 2. ขอขอบคุณสำหรับการอธิบายเกี่ยวกับ __proto__ มีประโยชน์ - person Shelby115; 18.07.2014
comment
b.__proto__ === Bar.prototype - เอ่อไม่? ตัวสร้าง returns วัตถุ ต้นแบบจะถูกละเว้น - person Bergi; 18.07.2014
comment
คุณควร เสมอ ใช้ Object.create เพื่อความชัดเจน และชิมในเบราว์เซอร์รุ่นเก่าหากจำเป็น - person Bergi; 18.07.2014
comment
@Bergi (ความคิดเห็นที่ 1) ฉันกำลังพูดถึงตัวอย่างแรกที่คุณใช้ var b = new Bar(); จากนั้น b.__proto__ === Bar.prototype - person soktinpk; 18.07.2014
comment
ใช่ ฉันก็ทำเหมือนกัน function Bar(){ return {}; } Object.getPrototypeOf(new Bar) !== Bar.prototype. ลองมัน! - person Bergi; 18.07.2014
comment
@ Shelby115: ใช่ มันจะช่วยได้ จริงๆ แล้ว ไม่ใช่เรื่องแปลกเลยที่จะมี function makeFoo() { return new Foo(); } ที่สามารถ - ตรงกันข้ามกับ Foo - ที่จะใช้โดยไม่ต้อง new โปรดสังเกตว่าชื่อตัวสร้างควรเป็นตัวพิมพ์ใหญ่เสมอ - person Bergi; 18.07.2014