簡體   English   中英

沒有 this、new 或原型的 Javascript 工廠函數

[英]Javascript factory functions without this, new or prototype

我越來越多地使用這種奇怪的(至少對我而言)方法,我開始喜歡它,但與此同時,我不確定在更大的背景下是否有什么可怕的錯誤.

簡化示例:

function createCat(initName) {
  let name = initName;
  const getName = () => name;
  function setName(newName) {
    name = newName;
  }
  function meow() {
    console.log("MEOW");
  }
  return {
    meow,
    setName,
    getName
  };
}

function createSuperCat(name) {
  const proto = createCat(name);
  const getName = () =>
    `Super secret! But hey - they used to call himself ${name}`;
  const getSecretName = () => proto.getName();
  function bark() {
    console.log("WHOOF");
  }
  return { ...proto, bark, getName, getSecretName };
}

const someCat = createCat("Hugo");
const someSuperCat = createSuperCat("Fluffy");

// someCat
console.log(someCat.getName()); // Hugo
someCat.setName("Tom");
console.log(someCat.getName()); // Tom
someCat.meow(); // MEOW

// someSuperCat
console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
console.log(someSuperCat.getSecretName()); // Fluffy
someSuperCat.setName("Mittens");
console.log(someSuperCat.getSecretName()); // Mittens
console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
someSuperCat.meow(); // MEOW
someSuperCat.bark(); // WHOOF

我知道沒有唯一正確的方法,如果可行,它就是一個有效的解決方案。

但是 - 我只是想知道......你在這種方法中看到任何警告嗎?

到目前為止,它似乎可以完成所有花哨的事情,例如繼承、擴展或覆蓋繼承的方法,並且您可以省略舊式 OOP 的this ,您不必使用new關鍵字實例化標准工廠函數,沒有原型模式(嗯,實際上有點 - 因為它是 Javascript,所以總會有),沒有任何或其他語法糖。

我可能是錯的。 我不是一個真正的程序員或其他什么人,所以我會感謝有經驗的人的意見,因為我可能會遺漏一些可能會在以后引起不適的東西。

因此,這種方法的最大警告之一首先是,給您的代碼增加了不必要的復雜性,因為 JS 已經為您正在做的事情提供了解決方案,因此您通過這種方式並沒有真正實現任何目標。 但隨着對象數量的增加,這種方法也會變得更加低效和浪費內存。 原型繼承和this關鍵字的一個好處是所有函數都可以存儲在單個對象引用中

您的方法為每個對象重新定義了所有功能。 因此,每次創建新的cat對象時,您都在重新定義所有通用函數。 每個對象都存儲在內存中。

即使您想避免使用類並使用new ,最好利用原型繼承和this關鍵字。

我重寫了你的代碼:

 const catProto = { getName: function () { return this.name; }, setName: function (newName) { this.name = newName; }, meow: function () { console.log("MEOW"); }, }; function createCat(name) { const cat = Object.create(catProto); cat.name = name; return cat; } function createSuperCat(name) { const cat = createCat(name); cat.getName = () => `Super secret...`; cat.getSecretName = catProto.getName; cat.bark = () => console.log("WHOOF"); return cat; } const someCat = createCat("Hugo"); const someSuperCat = createSuperCat("Fluffy"); // someCat console.log(someCat.getName()); // Hugo someCat.setName("Tom"); console.log(someCat.getName()); // Tom someCat.meow(); // MEOW // someSuperCat console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy console.log(someSuperCat.getSecretName()); // Fluffy someSuperCat.setName("Mittens"); console.log(someSuperCat.getSecretName()); // Mittens console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy someSuperCat.meow(); // MEOW someSuperCat.bark(); // WHOOF

該模式很好,只要您意識到以下功能的丟失:

  • 操作符instanceof
  • Object.getPrototypeOf ,
  • prototypeconstructor屬性
  • 沒有原型上​​的函數副本

似乎對性能也有影響。

這是與現代語法中的 OOP 實現的比較。 請注意,它使用私有字段,在撰寫本文時,並非所有引擎都支持該字段,尤其是 FireFox 不支持。 但例如在 Chrome 中它運行良好:

 // Parasitic implementation function createCat(initName) { let name = initName; const getName = () => name; function setName(newName) { name = newName; } function meow() { return "MEOW"; } return { meow, setName, getName }; } function createSuperCat(name) { const proto = createCat(name); const getName = () => `Super secret! But hey - they used to call himself ${name}`; const getSecretName = () => proto.getName(); function bark() { return "WHOOF"; } return { ...proto, bark, getName, getSecretName }; } // OOP implementation class Cat { #name constructor(initName) { this.#name = initName; } get name() { return this.#name; } set name(newName) { this.#name = newName; } meow() { return "MEOW"; } } class SuperCat extends Cat { #name constructor(initName) { super(initName); this.#name = initName; } get name() { return `Super secret! But hey - they used to call himself ${super.name}`; } set name(newName) { this.#name = newName; } get secretName() { return this.#name; } bark() { return "WHOOF"; } } function testParasitic() { // First produce the output as by OP let output = []; const someCat = createCat("Hugo"); const someSuperCat = createSuperCat("Fluffy"); // someCat output.push(someCat.getName()); // Hugo someCat.setName("Tom"); output.push(someCat.getName()); // Tom output.push(someCat.meow()); // MEOW // someSuperCat output.push(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy output.push(someSuperCat.getSecretName()); // Fluffy someSuperCat.setName("Mittens"); output.push(someSuperCat.getSecretName()); // Mittens output.push(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy output.push(someSuperCat.meow()); // MEOW output.push(someSuperCat.bark()); // WHOOF console.log(output.join(", ")); // Then run a performance test: let start = performance.now(); for (let i = 0; i < 1000; i++) { const someCat = createCat("Hugo"); someCat.setName("Tom"); someCat.getName(); // Tom someCat.meow(); // MEOW for (let j = 0; j < 100; j++) { const someSuperCat = createSuperCat("Fluffy"); someSuperCat.setName("Mittens"); someSuperCat.getSecretName(); someSuperCat.getName(); someSuperCat.meow(); someSuperCat.bark(); } } return performance.now() - start; } function testOOP() { // First produce the output as by OP let output = []; const someCat = new Cat("Hugo"); const someSuperCat = new SuperCat("Fluffy"); // someCat output.push(someCat.name); // Hugo someCat.name = "Tom"; output.push(someCat.name); // Tom output.push(someCat.meow()); // MEOW // someSuperCat output.push(someSuperCat.name); // Super secret! But hey - they used to call him Fluffy output.push(someSuperCat.secretName); // Fluffy someSuperCat.name = "Mittens"; output.push(someSuperCat.secretName); // Mittens output.push(someSuperCat.name); // Super secret! But hey - they used to call him Fluffy output.push(someSuperCat.meow()); // MEOW output.push(someSuperCat.bark()); // WHOOF console.log(output.join(", ")); // Then run a performance test: let start = performance.now(); for (let i = 0; i < 1000; i++) { const someCat = new Cat("Hugo"); someCat.name = "Tom"; someCat.name; someCat.meow(); for (let j = 0; j < 100; j++) { const someSuperCat = new SuperCat("Fluffy"); someSuperCat.name = "Mittens"; someSuperCat.secretName; someSuperCat.name; someSuperCat.meow(); someSuperCat.bark(); } } return performance.now() - start; } let dur1 = testParasitic(); let dur2 = testOOP(); console.log("duration parasitic test", Math.round(dur1)); console.log("duration OOP test", Math.round(dur2));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM