1.JavaScript工厂模式
工厂模式的主要作用是用来创建对象的,降低代码冗余度
应用场景:当你想要批量生产同种类的对象的时候;比如,你想生成一个班级的40个学生,每个学生都有姓名、年龄等特征。这时候你创建一个“工厂”,把信息丢到工厂里,工厂就给你造一个人出来,非常方便。
创建对象的方式有:
- 字面量方式创建对象
// 字面量创建对象
var obj1={
name:'zhangsan',
age:18
}
- new Object()创建对象
// new Object()创建对象
var obj2=new Object();
- 工厂模式创建对象
// 使用工厂模式创建对象
// 1.创建一个函数 工厂函数
function createPerson(name,age,gender){
// 2.使用构造函数创建一个模板实例
var person=new Object();
// 3.给模板实例赋值
person.name=name;
person.age=age;
person.gender=gender;
person.sayName=function(){
console.log(this.name);
}
// 4.返回一个实例对象
return person;
}
// 使用工厂函数创建实例
var p1=createPerson('zhangsan',18,'male')
var p2=createPerson('lisi',19,'female')
console.log(p1);
console.log(p2);
p1.sayName()
p2.sayName()
工厂模式的优缺点
**优点:**只要我们往工厂函数里面塞参数,工厂函数就会像生产产品一样早个人出来
**缺点:**这种方式的本质上是将创建对象的过程进行了封装,本质并没有改变,我们创建一个student时无法知道其具体的数据类型,只知道这是一个对象,往往实际开发中我们需要确定这个对象到底时个Person的实例还是Dog的实例
这时可以使用自定义构造函数模式
2.构造函数模式
ECMAScript的构造函数是能够创建对象的函数
2.1自定义构造函数
// 创建一个自定义的构造函数
// 1.使用函数来创建一个自定义的构造函数 function 函数表达式
function Person(name,age,gender) {
// 2.直接使用this来进行一个赋值
this.name =name;
this.age=age;
this.gender=gender;
this.sayName=function(){
console.log(this.name);
}
}
// 3.使用new操作符 创建一个实例
var p1 =new Person('zhangsan',18,'male')
var p2 =new Person('lisi',19,'female')
person1.sayName(); // zhangsan
person2.sayName(); // lisi
这里的Person()与上面的工厂函数中的createPerson()内部的代码基本是一样的,只是有如下区别:
-
没有显式地创建对象
-
属性和方法直接赋值给了this
-
没有return
注意 :ECMAScript中区分构造函数与普通函数是:构造函数名称是以大写字母开头的,普通函数名称是以小写字母开头的
2.2创建Person实例
创建Person的实例,应使用new操作符
var p1 =new Person('zhangsan',18,'male')
var p2 =new Person('lisi',19,'female')
(1)在内存中创建一个新对象
(2)这个新对象内部的[[Prototype]]特性被赋值为构造函数的Prototype属性
(3)构造函数内部的this被赋值为这个新对象(即this指向新对象)
(4)执行构造函数内部的代码块(给新对象添加属性)
(5)如果构造函数返回非空对象,则返回该对象,否则,返回刚创建的新对象
以上的p1,p2分别保存着Person的不同实例。所有的对象都会从它的原型上继承一个constructor属性,对象的constructor属性指向Person
console.log(p1.constructor === Person); // true
console.log(p1.constructor === Object); // false
console.log(p2.constructor === Person); // true
2.3instanceof
constructor属性是用来标识对象类型的,只不过instanceof操作符能更可靠的确定对象的类型
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原链上,也可以说是判断一个对象是不是某个对象的实例
console.log(p1 instanceof Object);//true
console.log(p1 instanceof Person);//true
所有自定义对象都继承自Object 。与工厂模式相对比,定义自定义构造函数可以确保实例被标识为特定类型,工厂模式不行
2.4 使用函数表达式自定义构造函数
构造函数可以赋值给一个变量的函数表达式来表示
// 创建一个自定义的构造函数
// 1.赋值给一个变量来创建一个自定义的构造函数 function 函数表达式
var Person=function (name,age,gender) {
// 2.直接使用this来进行一个赋值
this.name =name;
this.age=age;
this.gender=gender;
this.sayName=function(){
console.log(this.name);
}
}
// 3.使用new操作符 创建一个实例
var p1 =new Person('zhangsan',18,'male')
var p2 =new Person('lisi',19,'female')
person1.sayName(); // zhangsan
person2.sayName(); // lisi
补充 在实例化时,如果不想传参数,那么构造函数后面的括号可加可不加。只要有new操作符,就可以调用相应的构造函数
2.5构造函数也是函数
构造函数与普通函数唯一的区别就是调用方式不同:
任何函数只要使用new操作符调用的就是构造函数,不使用new操作符调用的函数就是普通函数
function Person(name,age,gender) {
this.name =name;
this.age=age;
this.gender=gender;
this.sayName=function(){
console.log(this.name);
}
}
// 当作一个构造函数来使用
var p1 =new Person();
// 当作一个普通函数来使用
Person('lisi',19,'female');//添加到全局变量 浏览器 => window ; node => global
// console.log(global);
global.sayName();
var p2 =new Person();
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "wangwu", 25, "male");
o.sayName(); // wangwu
这个案例展示了典型的构造函数调用方式,即使用 new 操作符创建一个新对象。然后是普通函数的调用方式。
这时候没有使用 new 操作符调用 Person(),结果会将属性和方法添加到全局对象。这里要记住,在调用一个函数而没有明确设置 this 值的情况下(即没有作为对象的方法调用,或者没有使用call()/apply()调用),this 始终指向 Global 对象(在浏览器中就是 window 对象)。因此在上面的调用之后,Global 对象上就有了一个 sayName()方法,调用它会返回"lisi"。
最后的调用方式是通过 call()(或 apply())调用函数,同时将特定对象指定为作用域。这里的调用将对象 o 指定为 Person()内部的 this 值,因此执行完函数代码后,所有属性和 sayName()方法都会添加到对象 o 上面。