当前位置: 首页 > news >正文

对象创建的4种模式

1. 工厂模式

这种模式抽象了创建具体对象的过程,用函数来封装以特定接口创建对象的细节

缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)

function createPerson(name, age, job) {var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function() {alert(this.name)};return o;
}
const personOne = createPerson('lee', 29, 'Software Engineer')
const personTwo = createPerson('Gred', 24, 'Doctor')

2. 构造函数模式

 (1)与工厂模式相比构造函数模式

  • 没有显示地创建对象
  • 直接将属性和方法赋值给了this对象
  • 没有return语句
  • 区别于非构造函数,构造函数以大写字母开头

(2)要创建Person的新实例,必须使用new操作符

         以这种方式调用构造函数实际上会经历以下4个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
  3. 执行构造函数的代码(为这个新对象添加属性) 
  4. 返回新对象
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.sayName = function() {alert(this.name)};
}
const personOne = new Person('lee', 29, 'Software Engineer')
const personTwo = new Person('Gred', 24, 'Doctor')

(3)personOne和personTwo分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person

console.log(personOne.constructor == Person) // true
console.log(personTwo.constructor == Person) // true

(4)以上例子中创建的所有对象既是Object的实例,同时也是Person的实例,这一点通过instanceof操作符可以得到验证。

console.log(personOne instanceof Person) // true
console.log(personOne instanceof Object) // true
console.log(personTwo instanceof Person) // true
console.log(personTwo instanceof Object) // true

(5)构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。

// 当作构造函数使用
const person = new Person('lee', 29, 'Software Engineer');
person.sayName(); // 'lee'// 作为普通函数调用
Person('Gred', 24, 'Doctor') // 添加到window
window.sayName(); // 'Gred'// 在另外一个对象的作用域中调用
const o = new Object();
Person.call(o, 'Alice', 27, 'Nurse');
o.sayName(); // 'Alice'

缺点:每个方法都要在每个实例上重新创建一遍。

解决方法:把函数转移到构造函数外。

function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.sayName = sayName
}
function sayName() {alert(this.name)};
const personOne = new Person('lee', 29, 'Software Engineer')
const personTwo = new Person('Gred', 24, 'Doctor')

如上,两个实例的sayName共享了一个全局的sayName方法。解决了2个函数做同一件事的问题。但是新问题又来了

在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实
如果对象需要定义很多方法,那么就要定义很多全局函数,导致我们这个自定义的引用类型丝毫没有封装性可言。

3. 原型模式

(1) 针对以上构造函数的痛点,原型模式可以解决这些问题。

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的作用是包含可以由特定类型的所有实例共享的属性和方法。prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处就是不用在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。

function Person() {}
Person.prototype.name = 'lee';
Person.prototype.age = 29;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function() {alert(this.name)
};
const personOne = new Person();
personOne.sayName(); // 'lee'
const personTwo = new Person();
personTwo.sayName(); // 'lee'
console.log(personOne.sayName == personTwo.sayName); // true//简写
function Person(){}
Person.prototype = {constructor: Person,name: 'lee',age: 29,job: 'Software Engineer',sayName: function(){alert(this.name)}
};

(2)默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性这个属性是一个指向prototype属性所在函数的指针。如上例子,Person.prototype.constructor指向Person。

Person.prototype.constructor === Person // true

(3)当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个内部属性[ [ Prototype ] ],虽然无法访问到。但是可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。如果[ [ Prototype ] ]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true。

console.log(Person.prototype.isPrototypeOf(personOne)); // true
console.log(Person.prototype.isPrototypeOf(personTwo)); // true

(4)Object.getPrototypeOf() 这个方法返回[ [ Prototype ] ]的值

console.log(Object.getPrototypeOf(personOne) == Person.prototype); // true
console.log(Object.getPrototypeOf(personOne).name); // 'lee'
console.log(Object.getPrototypeOf(personTwo) == Person.prototype); // true
console.log(Object.getPrototypeOf(personTwo).name); // 'lee'

(5)使用delete可以删除实例中的属性

personOne.name = 'Alice';
console.log(personOne.name) // 'Alice'  来自实例
console.log(personTwo.name) // 'lee'  来自原型 
delete personOne.name;
console.log(personOne.name) // 'lee'  来自原型 

(6)hasOwnProperty()方法可以检测一个属性是存在于实例中还是存在于原型中。如果该属性存在于实例中,则返回true。

personOne.name = 'Alice';
console.log(personOne.hasOwnProperty('name')) // true
console.log(personTwo.hasOwnProperty('name')) // false

(7)in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

personOne.name = 'Alice';
console.log('name' in personOne) // true name在实例中
console.log('name' in personTwo) // true name在原型中

(8)getOwnPropertyNames()方法得到所有实例属性,无论它是否可枚举。

const keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys); // ['constructor','name','age','job','sayName']

(9)可以随时给原型添加属性和方法,但是如果是重写整个原型对象,就相当于把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。

function Person(){}
const friend = new Person();
Person.prototype = {name: 'lee',age: 29,job: 'Software Engineer',sayName: function(){alert(this.name)}
};
friend.sayName(); // error
//因为这时的friend指向的是以前的Person,而以前的是没有sayName方法的,所以报错。
//而且这里旧的原型对象和新的原型对象的指针都指向Person

缺点:

(1)所有实例在默认情况下都将取得相同的属性值。(上面例子中定义的name,age等)(2)原型中存在引用类型,会导致每一个实例共享这个引用类型。如下例子:每一个实例中的friends会因为其中一个实例的修改,都共享了这个修改后的结果。

function Person(){}
Person.prototype = {constructor: Person,name: 'lee',age: 29,friends: ['Jack', 'Bob'],job: 'Software Engineer',sayName: function(){alert(this.name)}
};
const personOne = new Person();
const personTwo = new Person();
personOne.friends.push('Ivan');
console.log(personOne.friends); // ['Jack', 'Bob', 'Ivan']
console.log(personTwo.friends); // ['Jack', 'Bob', 'Ivan']
console.log(personOne.friends === personTwo.friends); // true

4. 组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。

function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.friends = ['Jack', 'Bob'];
}
Person.prototype = {constructor: Person,sayName: function() {alert(this.name);}}
const personOne = new Person('lee', 29, 'Software Engineer')
const personTwo = new Person('Gred', 24, 'Doctor')
personOne.friends.push('Van');
console.log(personOne.friends); // ['Jack', 'Bob', 'Ivan']
console.log(personTwo.friends); // ['Jack', 'Bob']
console.log(personOne.friends === personTwo.friends); // false
console.log(personOne.sayName === personTwo.sayName); // true

http://www.lryc.cn/news/369176.html

相关文章:

  • 如何判断 是否 需要 CSS 中的媒体查询
  • 设计模式-装饰器模式(结构型)
  • 升级HarmonyOS 4.2,开启健康生活篇章
  • 给gRPC增加负载均衡功能
  • 【优选算法】详解target类求和问题(附总结)
  • 【数据结构】图论入门
  • 11_1 Linux NFS服务与触发挂载autofs
  • 开发uniapp 小程序时遇到的问题
  • 怎样快速获取Vmware VCP 证书,线上考试,voucher报名优惠
  • LeetCode 1141, 134, 142
  • 华为FPGA工程师面试题
  • Windows11上安装docker(WSL2后端)和使用docker安装MySQL和达梦数据库
  • UnityXR Interactable Toolkit如何实现Climb爬梯子
  • sqli-labs 靶场 less-11~14 第十一关、第十二关、第十三关、第十四关详解:联合注入、错误注入
  • 国内外网络安全现状分析
  • vscode copilot git commit 生成效果太差,用其他模型替换
  • 计算机毕业设计hadoop+spark+hive舆情分析系统 微博数据分析可视化大屏 微博情感分析 微博爬虫 微博大数据 微博推荐系统 微博预测系统
  • 【MySQL】(基础篇二) —— MySQL初始用
  • 计算机网络 期末复习(谢希仁版本)第4章
  • 如何使用Pandas处理数据?
  • Error: spawn xdg-open ENOENT
  • 写给大数据开发,如何去掌握数据分析
  • 大数据湖一体化运营管理建设方案(49页PPT)
  • 大模型训练的艺术:从预训练到增强学习的四阶段之旅
  • Linux 网络设置
  • 交易中的群体行为特征和决策模型
  • Android14之向build.prop添加属性(二百一十九)
  • Cargo
  • 大学生如何学习node.js?
  • 速盾:服务器遭受ddos攻击如何防御