首页 > 其他分享 >JS单例模式; super return 改变this

JS单例模式; super return 改变this

时间:2024-08-28 11:15:01浏览次数:11  
标签:Singleton return extraData JS instance 单例 constructor data

简单的单例

首先 js 中的函数是一种特殊的对象,这使得他可以存储属性

function aaa(params) {}
//undefined
aaa.lalala = 123
//123

js中的类是通过原型链继承的,因此类就是函数的集合体,可以通过babel编译看看。

因此可以写出简单的单例模式

class Singleton {
    constructor(data) {
        if (Singleton.instance) {
            return Singleton.instance;
        }
        Singleton.instance = this;
        this.data = data;
    }

    getData() {
        return this.data;
    }
}
const Singleton = require('./Singleton');
const obj1 = new Singleton('Data for the first call');
const obj2 = new Singleton('Data for the second call');

console.log(obj1.getData()); // "Data for the first call"
console.log(obj2.getData()); // "Data for the first call"

可以看到,我们其实是将创建好的实例挂载到了Singleton这个函数对象上,以此维持它的引用。

这样的单例模式十分简单,但若是要继承一下,以便复用呢?

继承的单例

class Singleton {
    constructor(data) {
        if (this.constructor.instance) {
            return this.constructor.instance;
        }
        this.constructor.instance = this;
        this.data = data;
    }

    getData() {
        return this.data;
    }
}

class ChildSingleton extends Singleton {
    constructor(data) {
        super(data);
    }
}


new ChildSingleton() //会发生什么?

继承时,情况发生了变化,我们回顾new关键字的工作原理,分析它的过程。

首先,new创建一个新对象,执行原型连接,并将执行函数的this绑定为该对象,就像下面这样

let obj = Object.create(Singleton.prototype)
obj.prototype.constructor = ChildSingleton //其实这里还得设置enumerable: false, writable: true
ChildSingleton.constructor.call(obj,data)

ChildSingleton.constructor()中只有一句 super()​,所以将constructor()压入函数调用栈,执行父类的构造方法,即super()。

constructor(data) {
    if (this.constructor.instance) {
        return this.constructor.instance;
    }
    this.constructor.instance = this;
    this.data = data;
}

在这里,this指向的依然是new关键字创建的新对象,该对象的原型对象上保存着对函数对象ChildSingleton 的引用,因此 this.constructor.instance​实际上是ChildSingleton.instance​,所以我们能将对象存放在子类函数对象的内部,而不是放在父类中,否则继承时不就乱了。

接下来便是平平无奇的赋值。

然后constructor()从函数调用栈弹出,恢复到子类的构造方法。第一次执行就完成了。

那第二次呢?

前面的步骤都差不多,但是第二次进入时 this.constructor.instance 为true

这导致了return this.constructor.instance; 的执行。

由于return了 父类构造方法直接结束,回到子类构造方法中,由于也没有语句执行了,直接结束。此时,由于父类的return 已经返回了一个对象,所以整个new调用的返回对象就是我们的super() return回来的对象,而不是new创建的那个对象,这里需要注意。

还是不对

如果给子类也添加属性呢?

class Singleton {
    constructor(data) {
        if (this.constructor.instance) {
            return this.constructor.instance;
        }
        this.constructor.instance = this;
        this.data = data;
    }

    getData() {
        return this.data;
    }
}

class ChildSingleton extends Singleton {
    constructor(data, extraData) {
        super(data);
        // 添加子类特有的属性
        this.extraData = extraData;
    }

    // 你也可以添加子类特有的方法
    getExtraData() {
        return this.extraData;
    }
}

new ChildSingleton(123,321) === new ChildSingleton() //仔细想想第二次?

这样对吗?对也不对。

虽然控制台打印了true,证明是同一个对象,但是如果打印两次的对象,就会发现extraData属性被改变了!

我直说了:super内的return语句会改变this的指向。

在 return this.constructor.instance; 一句返回之后,this就从new关键字自动创建的新对象被替换成了this.constructor.instance指向的,挂载在ChildSingleton的原型对象上的单例对象。而后代码仍在继续执行,this.extraData = extraData;

因此发现extraData属性的值发生了变化。

解决方式就是防止他在初始化后被构造函数改变。

class ChildSingleton extends Singleton {
    constructor(data, extraData) {
        super(data);
        // 如果 extraData 属性不存在,那么我们才设置它
        if (!this.extraData) {
            this.extraData = extraData;
        }
    }

    // 你也可以添加子类特有的方法
    getExtraData() {
        return this.extraData;
    }
}

如此便好。

标签:Singleton,return,extraData,JS,instance,单例,constructor,data
From: https://www.cnblogs.com/lacia/p/18384238/js-singular-mode-super-return-changes-this-1n8ugm

相关文章

  • js实现刷新页面后回到记录时滚动条的位置
    第二种方案1.通过div的onscroll事件记录滚动条的scrollTop值,设置到document.cookie2.页面加载时再读取document.cookie的值,设置给div的scrollTop<scripttype="text/javascript">functionKeepScrollBar(){varscrollPos;if(typeofwindow.pageYOffset!='undefi......
  • js练习--用户管理API
    需要node.js运行环境,创建2个文件:user.js,server.jsuser.js:letusers={};module.exports=users;server.js:consthttp=require('http');//导入user模块letusers=require('./user');//创建HTTP服务器constserver=http.createServer((req,res)=......
  • three.js低代码 编辑器 ,和 相应 3d 功能案例 ,cesium
    开发历程低代码的开发是非常曲折的,尤其是早期根本没有任何参考作品或者相关的一些知识博客去引领我们去做,会经历多次的推到再到重头再来,又需要开发者有很高的知识储备,举步维艰。以下是我的一些的功能分布,希望能对你有一些启发,让你少走一些弯路。预览查看https://z25863......
  • js 正则列表
    匹配空的if代码块,匹配空的else把下面的if换成else就行了 /if(\s)*\(.*\)(\s)*\{[\s\n]*\}$/ 匹配手机号/^1[3-9]\d{9}$/ 经度(保留六位小数)/^[\-\+]?(0(\.\d{1,6})?|([1-9](\d)?)(\.\d{1,6})?|1[0-7]\d{1}(\.\d{1,6})?|180)$/ 纬度(保留六位小数)/^[\-\+]?......
  • Flask-RESTFul 之 RESTFul 的响应处理 之定制返回的 json格式
    Flask-RESTFul之RESTFul的响应处理之定制返回的json格式使用`marshal_with`和`fields`定制响应结构重写Flask的`jsonify`方法在返回前手动构造JSON使用Flask-RESTful的`Response`类自定义输出处理器结论在Flask-RESTful中,如果你......
  • 设计模式--单例模式
    1.单例模式:关注于对象的创建。分为懒汉式和饿汉式。懒汉式:调用方法的时候才创建;classSingleton{privateSingleton()//1.私有构造方法{Console.WriteLine("构造方法被创建");}privatestaticvolatileS......
  • 基于Node.js+vue社区医疗服务系统(程序+论文+开题报告)-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着社会老龄化的加剧和居民健康意识的提升,社区医疗服务作为连接居民与医疗资源的重要桥梁,其重要性与日俱增。然而,当前许多社区医疗服务存在资源分配不均、......
  • 基于Node.js+vue网课视频课设(程序+论文+开题报告)-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,特别是在全球疫情的影响下,线上教育成为了教育领域不可或缺的一部分,网课视频课程以其灵活便捷、资源丰富等优势迅速崛起。然而,当前......
  • 基于Node.js+vue社区常驻居民信息管理软件(程序+论文+开题报告)-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着城市化进程的加速,社区作为城市的基本单元,其管理效率与居民生活质量息息相关。然而,传统社区管理方式往往依赖于纸质档案和人工操作,存在信息更新不及时、......
  • 基于Node.js+vue外卖鲜花系统(程序+论文+开题报告)-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着互联网的飞速发展和人们生活节奏的加快,线上消费已成为现代生活不可或缺的一部分,尤其是在追求即时满足与个性化体验的今天,传统鲜花行业正经历着深刻的变......