JS中批量创建对象的三种模式(小白笔记)
创建对象需要改进的地方:使用Object构造函数或者对象字面量可以方便地创建对象,但这些方式也有明显的不足: 创建具有同样属性的多个对象, 需要重复编写很多代码.
1. 工厂模式
工厂模式是一种常见的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程.
按照特定接口 创建对象的模式:
// 编写特定的接口
function createPerson(name,age,job){
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
return o;
}
// 利用接口 来创建对象
let person1 = creatPerson('nico',29,'lawyer');
let person2 = creatPerson('yue',29,'coder');
使用工厂模式 可以解决创建多个类似对象的问题, 但是这些新创建的对象是什么类型? 没有对应的标识!
2. 构造函数模式
ECMAScript中的构造函数 是用于创建特定类型的对象. 原生构造函数Object和Arrary等,运行时可以直接在执行环境中使用.
也可以自定义构造函数, 以函数的形式为自己的对象类型定义属性和方法.
function Person (name,age,job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
}
let person1 = new Person(..)
let person2 = new person(...)
按照惯例, 构造函数名称的首字母要大写,非构造函数首字母小写.
要创建构造函数Person的实例对象 ,需要使用new操作符.
使用new操作符 调用构造函数 会支持以下操作
① 在内存中创建一个新对象
② 新对象内部的 [[ Prototype ]] 特性 被赋值为 构造函数的prototype属性.
③ 构造函数内部的this指向新对象
④ 执行构造函数内部的代码,为新对象添加属性
⑤ 如果构造函数返回非空对象,则返回该对象. 否则返回刚创建的新对象.
在上面的例子中, person1和person2分别保存着Person的不同实例。这两个对象都有一个construction 属性指向 Person . construction: 建筑物 构造 结构
console.log( person1.construction == Person); //true
console.log( person2.construction == Person ); //true
construction 本来是用于标识对象类型的, 但是有更可靠好用的 instanceof 操作符来确定对象类型
console.log( person1 instanceof Person) //true person1是Person构造函数的实例
console.log( person1 instanceof Object) //true Person1也是Object构造函数的实例
// 因为所有的自定义对象都继承自 Object
console.log( person2 instanceof Person) //true
console.log( person2 instanceof Object) //true
相比于 工厂模式 , 自定义构造函数 有一个很大的好处, 就是可以确保实例被 标识为特定的类型. 比如上面的person1和person2 都被标识为 Person类型
关于构造函数的说明 :
①. 构造函数可以写成函数声明的形式 即 function Person(){...} ,也可以写成函数表达式的形式 即let Person = function (){...};
②. 使用构造函数实例化时 ,如果不想传参数,构造函数后面的括号可以不加,
即 let person1 = new Person;
③. 构造函数也是函数,函数也可以是构造函数.
构造函数和普通函数的唯一区别 就是调用方式不同. 任何函数只要使用new 操作符调用 就是构造函数,而不使用new操作符调用的函数就是普通函数.
// 使用new操作符 --构造函数
let person1 = new Person('nico',29,'lawyer');
person1.sayName(); //nico
// 作为普通函数调用 --全局作用域中this指向windows(在浏览器中)
Person("grep",30,"coder");
this.sayName(); //grep
// 在另一个对象中调用
let o = new Object();
//通过call调用 并将对象o指定为作用域 Person内部的this指向对象O
Person.call(o, 'kris',20,'actor');
o.sayName(); //kris
④. 构造函数可以改进的地方
构造函数的主要问题: 构造函数内部定义的方法,会在每个实例上都创建一遍. 比如上面的 person1和person2 内部都有sayName函数. JS中函数是对象,所以在实例中定义sayName函数时,都会初始化一个新的Function实例对象. 因为sayName函数都是做一样的事情,所以没有必要在两个Person实例中分别定义两个Function实例.
这个问题可以通过使用 原型模式 来解决.
3. 原型模式
每个函数都会创建一个prototype属性 ,这个属性是一个对象,包含 应该由特定引用类型的实例共享的属性和方法. (如Person.prototype)实际上 这个对象就是通过 调用构造函数创建的对象的原型.(如 person1.__proto__)
console.log(Person.prototype === person1.__proto__); //true
使用原型对象的好处 就是,在它上面定义的属性和方法 可以被对象实例共享.
原来在构造函数中直接赋值给对象实例的值 ,可以直接赋值给该构造函数的原型.
function Person(){}
Person.prototype.name = "Nicols";
Person.prototype.age = 19;
Person.prototype.job = "lawyer";
Person.prototype.sayName = function (){
console.log(this.name);
};
let person1 = new Person;
person1.sayName() //Nicols
let person2 = new Person;
person2.sayName() //Nicols
上面代码中, 所有属性和sayName()方法 都直接添加到了构造函数Person的prototype属性上. 构造函数体中 什么也没有. 调用该构造函数 创建的新对象仍然拥有相应的属性和方法.
和构造函数模式不同, 使用原型模式定义的属性和方法 是由所有的实例共享的.
因此 person1和person2 访问的是同一个属性和同一个sayName函数. 这样就解决了在实例对象中重复创建多个function函数对象的问题.
--------本文主要内容来自《JS高级程序设计》-------------------------