likes
comments
collection
share

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

作者站长头像
站长
· 阅读数 25

在传统的面向对象过程中,我们都知道new关键字是用来构造一个新对象的;同样的,在js中,new关键字也可以构造出一个新对象,但是你知道jsnew过程都干了些什么吗?今天就让我们一起探讨一下 new一个对象的过程以及编程模式中的单例模式

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

new 的过程发生了什么

js中秉承着一切皆对象的概念,es5里面通常是用首字母大写的函数(也是对象)来代表类的构造函数;通过new Duck()的方式,就可以构造出一个新的对象。

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

原型式面向对象

但是,js中不同于传统的面向对象,它是原型式的面向对象,我们在给类添加方法时,只能往类的构造函数身上的原型上添加方法,这样new出来的实例对象才能访问到。

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

在原型之中还存在着一条规则:类构造函数的显示原型等于它实例对象的隐式原型,即yw.__ proto__ = Duck.prototype

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

this 指向

在类的构造函数中,我们可以看到内部还有this关键字,关于它的用法我们可以先看下面一段代码;

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

当对象字面量中的函数方法被以对象的方法调用时,显然这时的this就会指向这个对象。然而;

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

当我们把对象中的方法拿出来以普通函数的方式运行,此时的this指向的就是全局。

所以,在函数里面,this的指向是由它的运行方式来决定的;当函数以对象的方法调用,此时的this动态的,指向调用的那个对象;而把函数用最普通的函数方式来运行时,this就会指向全局(window)。

那有没有什么方法可以改变关键字this的指向呢?当然有,call,apply,bind等方法都能改变this的指向,在这里我们就以apply为例;

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

上面的代码中,我们就用apply方法把this的指向绑定到了对象obj上,输出obj.name

隐式原型

new实例化对象后,我们知道实例化的对象都是可以访问到类身上的属性和方法的;但是在js中,我们是用函数对象来表示一个类的,我们通常把实例对象要访问的方法都写在构造函数的原型上;那在new过程中,我们就要有这样一个步骤,让新实例化的对象的隐式原型能够等同于构造函数的显示原型

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

在上面的代码片段中,其中obj3.__ proto__ 就叫做obj3隐式原型,我们把对象obj2赋给了obj3的隐式原型,也就是说虽然对象obj3中没有任何属性,但是由于原型,打印obj3.name时,还是能访问到其原型obj2上的属性name

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

手写 new

结合以上的内容,其实我们大概可以知道,new过程到底干了些什么?

  • 先创建一个空对象 {}
  • 让 this 指向实例对象,通过构造函数往实例中逐渐添加属性
  • 再把 实例对象的隐式原型__proto__ = 构造函数(Duck) 的显示原型prototype
  • 返回实例对象

手写new过程如下:

function myNew(Fun,...args) {
    let obj = {}; // 对象,内存在堆内存中,地址引用
    
    Fun.apply(obj, args); // 改变this指向
    
    obj.__proto__ = Fun.prototype; // 继承 原型对象
    
    return obj;
}

利用我们手写的myNew函数也能够实例化对象;

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字 单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

单例模式

有时候,我们在实例化对象时,只需要实例化一次就够了,并不需要多次实例化新对象出来,比如在很多网站中,99%的是用不到注册的,登录注册弹窗不需要每次实例化的,这样网站会更快;这时候就要引入我们单例模式的应用了。那何为单例模式呢?

单例模式(Singleton Pattern)是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁创建和销毁、开销大或者必须共享单一资源的情况,比如线程池、缓存、日志对象、对话框、注册表设置等。

new 会创建新对象

先来看下面一段代码;

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

我们用new关键字创建了两个新对象,obj1 明显不会等于 obj2,因为new过程是会为新对象开辟新的内存空间的,这两个显然不是同一个对象。

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

那要实现单例模式,我们显然不能使用new去构造我们的实例对象;那要怎么实现呢?

静态方法 getInstance

我们可以通过静态方法getInstance和静态属性去实现;

静态方法是一种特殊的类成员方法,它并不与类的任何特定实例绑定,而是与类本身绑定。这意味着静态方法可以在不创建类的实例的情况下被调用。静态方法通常用于执行与类相关但不依赖于任何特定实例状态的操作。

// 单例模式 有的类只实例化一次,性能比较好
var Singleton = function (name) {
    this.name = name
}

Singleton.prototype.getName = function () {
    console.log(this.name);
}
// 静态方法 属于类的
Singleton.getInstance = function (name) {
    if (!this.instance){
        // 静态属性
        this.instance = new Singleton(name)
    }
    return this.instance
}

var s1 = Singleton.getInstance('kunkun')

var s2 = Singleton.getInstance('kunfc')

console.log(s1 === s2, s1.name,s2.name); // true 单例模式保证只有一个实例

在上面的代码中,我们通过静态方法getInstance,在每一次创建对象实例时,先判断静态属性instance是否存在,如果不存在,则创建新的对象并赋给静态属性instance,存在则直接返回instance。这样,我们就能确保每次只能实例化一次了。

单例模式:为什么我只能new一次啊???new 的过程发生了什么 在js中秉承着一切皆对象的概念,es5里面通常是用首字

小结

js有着强大的表现力;可以通过直接创建对象字面量来新建对象;但是有一些对象,有着共同的行为(如果每个对象字面量都有自己的函数,内存开销太大),创建类的性能比较好;而每个对象肯定要有自己的属性,而且是不一样的,这时候构造函数constructor就可以通过this帮我们构造不同的属性值;函数方法则放到prototype原型对象上,就能被所有实例共享

转载自:https://juejin.cn/post/7389913889331380276
评论
请登录