工厂模式
function createPerson(name, age){ var o = new Object(); // 创建一个对象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; // 返回这个对象 } var person1 = createPerson('ccc', 18) var person2 = createPerson('www', 18)
工厂函数的问题:
工厂模式虽然解决了创建多个相似对象的问题,但是没有解决对象识别问题(即怎样知道一个对象的类型)。如下
person1 instanceof createPerson // --> false person1 instanceof Object // --> true
构造函数模式
function Person(name , age){ this.name = name; this.age = age; this.sayName = function(){ console.log(this.name) } } var person1 = new Person('ccc', 18) var person2 = new Person('www', 18) person1.sayName() // --> 'ccc'
person1 和person2 分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性指向Person。这正是构造函数模式胜过工厂模式的地方。如下:
console.log(person1 instanceof Person) // --> true console.log(person1 instanceof Object) // --> true console.log(person2 instanceof Person) // --> true console.log(person2 instanceof Object) // --> true
构造函数模式与工厂模式的区别:
- 没有显式的创建对象
- 直接将属性和方法赋给了this对象
- 没有return 语句
要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历一下4个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
构造函数的问题:
使用构造函数的重要问题,就是每个方法都要在每个实例上重新创建一遍。person1和person2中都有一个名为sayName()的方法,但那两个方法不是同一个Function实例。因为在ECMAscript中函数就是对象,因此每定义一个函数,也就是实例化了一个对象。从逻辑角度上讲,此时的构造函数也可以你这样定义:
function Person(name , age){ this.name = name; this.age = age; this.sayName = new Function('console.log(this.name)') // eslint: The Function constructor is eval. (no-new-func) }
这会导致,创建的不同的实例上的同名函数是不相等的,比如:console.log(person1.sayName() === person2.sayName()) // -->false,然而创建两个完全相同的任务的Function实例是没有必要的。可以通过把函数定义转移到构造函数外部来解决这个问题。
function Person(name , age){ this.name = name; this.age = age; this.sayName = sayName } function sayName(){ console.log(this.name) } var person1 = new Person('ccc', 18) var person2 = new Person('www', 18)
这样,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。这样做确实解决了两个函数做同一件事的问题,可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。
带来的新问题:
如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言。
原型模式
关于原型,原型链内容不在此描述,只讨论原型设计模式
我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。即不必在构造函数中定义对象实例的信息,而是将这些信息直接添加到原型对象中。
function Person(){ } Person.prototype.name = 'ccc' Person.prototype.age = 18 Person.prototype.sayName = function(){ console.log(this.name) } var person1 = new Person() person1.sayName() // --> ccc var person2 = new Person() person2.sayName() // --> ccc console.log(person1.sayName === person2.sayName) // --> true
原型模式的问题:
它省略了为构造函数传递参数初始化参数的环节,结果所有的实例在默认情况下都将取得相同的属性值。另外,原型模式的最大问题是由其共享的本性所导致的。看如下问题:
function Person(){ } Person.prototype = { constructor: Person, name: 'ccc', age: 18, friends:['www', 'aaa'], sayName: function () { console.log(this.name) } } var person1 = new Person() var person2 = new Person() person1.friends.push('bbb') console.log(person1.friends) // --> ["www", "aaa", "bbb"] console.log(person2.friends) // --> ["www", "aaa", "bbb"] console.log(person1.friends === person2.friends) // --> true
带来的新问题:
如果我们的初衷就是这样,所有的实例共用一个数组,那么这个结果就是想要的。可是,实例一般都是要有属于自己的全部属性的,这个问题正是我们很少看到有人单独使用原型模式的原因所在。
组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这种方式还支持向构造函数传递参数。
function Person(name, age){ this.name = name; this.age = age; this.friends = ['aaa', 'bbb'] } Person.prototype = { constructor: Person, sayName: function(){ console.log(this.name) } } var person1 = new Person('ccc', 18) var person2 = new Person('www', 18) person1.friends.push('ddd') console.log(person1.friends) // --> ["aaa", "bbb", "ddd"] console.log(person2.friends) // --> ["aaa", "bbb"] console.log(person1.friends === person2.friends) // --> false console.log(person1.sayName === person2.sayName) // --> true
这种构造函数与原型混成的模式,是目前ECMAscript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认方式。
动态原型模式
动态原型模式就是可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
function Person(name, age){ // 属性 this.name = name this.age = age // 方法 if(typeof this.sayName !== 'function'){ Person.prototype.sayName = function(){ console.log(this.name) } } } var person1 = new Person('ccc', 18) person1.sayName() // --> ccc
这里只有在sayName()方法不存在的情况下,才会将它添加到原型中。这段代码只会在初次调用构造函数时才会执行。
注意:
- 在这里对原型所做的修改,能够立即在所有实例中得到反映。
- 使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。(参考原型与原型链中的内容)
其它模式
还有寄生构造函数模式和稳妥构造函数模式,可自行了解。以上所以知识内容来自《JavaScript高级程序设计》(第三版)。
以上就是详解js中的几种常用设计模式的详细内容,更多关于JS 设计模式的资料请关注其它相关文章!
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 潘安邦《思念精选集全纪录》5CD[WAV+CUE]
- 杨千嬅《千嬅新唱金牌金曲》金牌娱乐 [WAV+CUE][985M]
- 杨钰莹《依然情深》首版[WAV+CUE][1G]
- 第五街的士高《印度激情版》3CD [WAV+CUE][2.4G]
- 三国志8重制版哪个武将智力高 三国志8重制版智力武将排行一览
- 三国志8重制版哪个武将好 三国志8重制版武将排行一览
- 三国志8重制版武将图像怎么保存 三国志8重制版武将图像设置方法
- 何方.1990-我不是那种人【林杰唱片】【WAV+CUE】
- 张惠妹.1999-妹力新世纪2CD【丰华】【WAV+CUE】
- 邓丽欣.2006-FANTASY【金牌大风】【WAV+CUE】
- 饭制《黑神话》蜘蛛四妹手办
- 《燕云十六声》回应跑路:年内公测版本完成95%
- 网友发现国内版《双城之战》第二季有删减:亲亲环节没了!
- 邓丽君2024-《漫步人生路》头版限量编号MQA-UHQCD[WAV+CUE]
- SergeProkofievplaysProkofiev[Dutton][FLAC+CUE]