JS创建对象的模式

JS工厂模式

使用字面量创建对象。

let person = {
  name: "LBJ",
  age: 23,
  gender: 'male',
  sayName: function(){
      console.log(this.name);
  }
}
var animal = new Object();
person.name = "wugui";
person.age = 18;
person.gender = 'male';
person.sayName = function(){
    console.log(this.name)
}

如果只是平时练习,没有什么问题,但是当我们需要数量庞大的对象时,重复的写相同的代码是完全没有必要的。

使用工厂模式创建对象
function sayName() {
  console.log(this.name);
}

function Person(name, age, gender) {
  return {
    name,
    age,
    gender,
    sayName:sayName
  }
}
let p1 = Person('Teddy', 12, 'male') 
console.log(p1);//{ name: 'Teddy', age: 12, gender: 'male',sayName: [Function: sayName] }

我们只需要把参数传入给工厂函数,就可以得到很多的对象。但是它也有着明显的缺点。

工厂函数的缺点:
function Cat(name, age, gender) {
  return {
    name,
    age,
    gender,
    sayName:sayName
  }
}
function Dog(name, age, gender) {
  return {
    name,
    age,
    gender,
    sayName:sayName
  }
}
function sayName() {
  console.log(this.name);
}
let cat1 = Cat('Teddy', 12, 'male') 
let dog1 = Dog('Teddy', 23, 'female')
console.log(cat1);//{ name: 'Teddy', age: 12, gender: 'male',sayName: [Function: sayName] }
console.log(dog1);// { name: 'Teddy', age: 23, gender: 'female',sayName: [Function: sayName] }

在上面的例子中,有一个Dog工厂函数,和Cat工厂函数,此时如果我们想创建一个cat1和dog1的对象,但是得到的结果是完全相同的(如果传入相同的参数,例子的不同是为了区别)。我们无法知道一个函数到底是Cat的实例对象还是Dog实例对象。再看看下面的例子。

构造函数模式
function Cat(name, age, gender) {
  this.name = name
  this.age = age
  this.gender = gender
  this.sayName = sayName
}
function Dog(name, age, gender) {
  this.name = name
  this.age = age
  this.gender = gender
  this.sayName = sayName
}
function sayName() {
  console.log(this.name);
}
let cat1 = new Cat('Teddy', 12, 'male') 
let dog1 = new Dog('Teddy', 12, 'male') 
console.log(cat1);
/**
 *Cat { 
  name: 'Teddy',
  age: 12,
  gender: 'male',
  sayName: [Function: sayName]
} */
console.log(dog1);
/**
 * Dog {
  name: 'Teddy',
  age: 12,
  gender: 'male',
  sayName: [Function: sayName]
}
 */

使用构造函数模式,即使我们传入相同的参数创建对象,我们也可以很直观的知道该对象到底是Dog对象的实例还是Cat对象的实例(前面写着Cat 和 Dog的标识)

工厂模式和构造函数的最直接的区别是new操作符。当使用new操作符创建一个实例时:

  • 在内存中开辟一块空间
  • 该对象实例的[[prototype]]__proto__指向构造函数的原型prototype,所以新的对象可以使用构造函数原型上的方法(理解继承很关键的一点)
  • this指向了new出来的新对象
  • 进行函数内部的赋值
  • 一般不使用return返回值

在上面的代码中:

console.log(cat1.sayName === dog1.sayName);//true
//他们sayName都指向同一个地址

sayName是同一个全局的方法,这解决了代码复用的问题。但是以后如果有了更多的构造函数,他们也需要一个sayName方法,而且需要这个sayName方法实现更多的功能。此时解决的方法一是让sayName换一个名字,二是把sayName完善,但是这又带来了新的问题,如果再有新的对象需要sayName实现跟多事情呢?我们不可能一直取新的名字,也不可能让一个sayName实现所有的功能,这也不能和我们自定义的构造类型结合起来。我们需要原型模式来解决这一问题。

原型模式:

方式一:

function Person() {
  Person.prototype.name = 'zhangsan',
  Person.prototype.height = '1.78',
  Person.prototype.weight = '70KG',
  Person.hobby = ['basketball', 'football', 'piano']
  Person.sayName = function() {
    console.log(this.name);
  }
}

方式二:

Person.prototype = {
  name: 'zhangsan',
  height: '1.78',
  weight: '80KG',
  hobby:['basketball', 'football', 'piano'],
  sayName: function() {
  console.log(this.name);
  }
}
console.log(p1.sayName === p2.sayName);//true,他们使用的都是原型上的方法,所以是一样的
console.log(p1.hobby === p2.hobby);//true,理由同上
//当p1喜欢上了打乒乓球,那么p2也会被强制喜欢上
p1.hobby.push('tennis')
console.log(p1.hobby);//[ 'basketball', 'football', 'piano', 'tennis' ]
console.log(p2.hobby);//[ 'basketball', 'football', 'piano', 'tennis' ]

原型模式解决了共享方法的问题,sayName的方法是在Person的原型对象上,当再有一个’Dog‘的构造函数也希望有一个sayName的方法,两个sayName在不同的构造函数的原型上,也就解决了自定义构造函数模式所带来的问题,可以同名,而且可以实现不同的功能。

当然共享所带来的问题就是,不是什么东西都希望共享的,就如同上面的’hobby爱好‘。

如果希望p1,p2爱好不相互影响,可以通过这样解决(不好的方法)

p1.hobby = ['dance', 'rap', 'music']
p2.hobby = ['study', 'sleep', 'eat']
p1.hobby.push('tennis')
console.log(p1.hobby);//[ 'dance', 'rap', 'music', 'tennis' ]
console.log(p2.hobby);//['study', 'sleep', 'eat']
console.log(p1.hobby === p2.hobby);//flase
//此时p1 p2的hobby是他们各自的属性,把Person原型的hobby覆盖了
//一个对象访问其属性时,会先查找自己本身有没有,如果没有则去[[prototype]]原型上找(就是问自己老爹要钱)有才给。
//当p1 new 出来时 他的[[prototype]]就指向了Person

虽然这种方式可以解决这一问题,但是其实这不就是我们开头说是的字面量创建对象的方法吗,一个个添加这并不是我们所需要的。

到这里,我们的目的已经很明显,我们的目的是把希望共享才的共享,也不使用字面量创建的方式添加我们想要的属性。

我们使用自定义构造函数模式的优点和原型模式的优点相结合。

组合模式:
function Person(name, height, hobby) {
  this.name = name
  this.height = height
  this.hobby = hobby
}
Person.prototype.sayName = function() {
  console.log(this.name);
}
let p1 = new Person('LBJ', '2.06', ['study', 'sleep', 'eat'])
let p2 = new Person('KOBE', '1.98', ['dance', 'rap', 'music'])
console.log(p1);
/**
 * Person {
  name: 'LBJ',
  height: '2.06',
  hobby: [ 'study', 'sleep', 'eat' ]
}
 */
console.log(p2);
/**
 * Person {
  name: 'KOBE',
  height: '1.98',
  hobby: [ 'dance', 'rap', 'music' ]
}
 */
console.log(p1.sayName === p2.sayName);//true,他们使用的都是相同的sayName方法
console.log(p1.hobby === p2.hobby);//false,他们拥有各自的爱好
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值