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 关键字实例化标准工厂函数,没有 prototype 模式(好吧,实际上有点 - 因为它是 Javascript,总会有),没有任何 classes 或其他语法糖。
我可能是错的。我不是真正的程序员之类的,所以我很感激有经验的人的意见,因为我可能遗漏了一些可能会在以后引起不适的东西。
因此,这种方法最大的警告之一是,首先,给您的代码增加了不必要的复杂性,因为 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
,
prototype
和 constructor
属性
- 没有原型上的函数副本
似乎对性能也有影响。
这里是与现代语法中的 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));
我越来越频繁地使用这种奇怪的(至少对我而言)方法,我开始喜欢它,但与此同时,我不确定是否有什么可怕的错误大局背景。
简化示例:
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 关键字实例化标准工厂函数,没有 prototype 模式(好吧,实际上有点 - 因为它是 Javascript,总会有),没有任何 classes 或其他语法糖。
我可能是错的。我不是真正的程序员之类的,所以我很感激有经验的人的意见,因为我可能遗漏了一些可能会在以后引起不适的东西。
因此,这种方法最大的警告之一是,首先,给您的代码增加了不必要的复杂性,因为 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
,prototype
和constructor
属性- 没有原型上的函数副本
似乎对性能也有影响。
这里是与现代语法中的 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));