Javascript将构造函数扩展为简单工厂

一般而言,在Javascript中创建对象时需要使用关键字new(按构造函数去调用),但是某些时候,开发者希望无论new关键字有没有被显式使用,构造函数都可以被正常调用,即构造函数同时还具备简单工厂的职能。Javascript的一个特性使得这种『简单工厂式的构造函数』变得可行:如果构造函数中返回了对象,无论有没有使用new关键字,最终返回的值都是函数return的值。

基于这点特性,本文介绍了四种实现方式,抛砖引玉,欢迎拍砖~

1. 在构造函数中返回对象字面量

复制代码
 1 function Person(name) {
 2     return {
 3         name: name,
 4         getName: function () { 
 5             return this.name;
 6         }
 7     };
 8 }
 9 console.log(new Person('Ralph').getName()); //Ralph
10 console.log(Person('Ralph').getName()); //Ralph
复制代码

缺点:
不方便控制prototype属性,不利于高效扩展对象方法,instanceof操作符失效且constructor属性丢失

 

2. 在构造函数中使用内部函数构造对象

复制代码
 1 function Person(name) {
 2     // lazy loading,在Person函数第一次被调用时初始化内部函数_Person
 3     if (!Person.inited) {
 4         Person._Person = function (name) {
 5             this.name = name; 
 6         };
 7         // 可以利用prototype进行方法扩展
 8         Person._Person.prototype = {
 9             // 正常使用constructor属性
10             constructor: Person,
11             getName: function () {
12                 return this.name;
13             }
14         };
15         // 可以正常使用instanceof操作符 
16         Person.prototype = Person._Person.prototype;
17         // 标记为已初始化
18         Person.inited = true;
19     }
20     return new Person._Person(name);
21 }
22 console.log(new Person('Ralph').getName()); //Ralph
23 console.log(Person('Ralph').getName()); //Ralph
复制代码

缺点:
编码相对较为复杂,需要_Person作为辅助的内部构造函数,且需要手动修改prototype和constructor等属性

 

3. 利用instanceof操作符

复制代码
 1 function Person(name) {
 2     // 如果使用了new,this指向新生成的Person实例
 3     // 如果直接调用Person没有使用new,这里的this指向window对象 
 4     if (!(this instanceof Person)) {
 5         return new Person(name);
 6     }
 7     this.name = name;
 8 }
 9 Person.prototype.getName = function () {
10     return this.name;
11 };
12 console.log(new Person('Ralph').getName()); //Ralph
13 console.log(Person('Ralph').getName()); //Ralph
复制代码

缺点:
在判断this instanceof Person时需要指明构造函数名称Person,抽象程度不够高,修改构造函数名称时需要手动修改该语句

 

4. 利用callee属性和constructor属性

复制代码
 1 function Person(name) {
 2     // arguments.callee指向Person函数
 3     // this.constructor仅在使用了new的情形下指向Person函数
 4     if (arguments.callee !== this.constructor) {
 5         return new arguments.callee(name);
 6     }
 7     this.name = name;
 8 }
 9 Person.prototype.getName = function () {
10     return this.name;
11 };
12 console.log(new Person('Ralph').getName()); //Ralph
13 console.log(Person('Ralph').getName()); //Ralph
复制代码

缺点:
strict模式下无法使用callee属性

(全文完)

posted @   ralph_zhu  阅读(1037)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示