工厂函数和构造函数的区别
下面是给出的两个都是实现 “定义使用值的范围” 的函数, 第一个工厂函数(工厂模式)用以创建并初始化类的实例,而且给出了一个表示“值的范围”的类定义了原型对象;第二个是使用构造函数代替工厂函数来实现相同功能的代码段 。
下面是“inherit()通用写法
function inherit(d){
if(d == null) throw TypeError(); //d 是一个对象,但不能是null;
if(Object.create) //如果Object.create()存在
return Object.create(d); //直接使用d
var t = typeof d; //否则进行进一步检测
if(t !== 'object' && t !== 'function') throw TypeError();
function f(){}; //定义一个空的构造函数
f.prototype = d; //将其原型属性设置为 d;
return new f(); //使用f()创建d的继承对象
}
下面是“工厂函数”的一段代码
function range(from,to){
//使用inherit()函数创建对象,这个对象继承自在下面定义的原型对象
//原型对象作为函数的一个属性存储,并定义所有“范围对象”所共享的方法。
var r = inherit(range.methods);
//存储新的“范围对象”的起始位置和结束位置
//这两个属性是不可继承的,每个对象都拥有唯一的属性。
r.from = from;
r.to =to;
return r;//返回这个新建立的对象
}
//原型对象定义方法,这些方法为每个范围对象所继承
range.methods = {
includes: function(x){ //如果x在范围内,返回 true 否则返回false
if(this.from <= x && x <= this.to){
return x;
}
return x;
},
foreach: function(f){ //对范围内的每个整数都调用一次 f
for(var x = Math.ceil(this.from);x <= this.to; x++)
f(x);
},
toString: function(){
return "("+this.from +".."+ this.to + ")";
}
};
var r = range(1,4);
r.foreach(console.log); // 输出 1 2 3 4
console.log(r.toString()); // 输出(1..4)
r.includes(2); //返回2
这段代码是用来定义一个工厂方法 range(),用来创建新的范围对象。 rang()函数定义了一个属性range.methods,用来快捷地存放定义类的原型对象。把原型对象挂在函数上不是惯用做法。而且range()函数给每个范围对象都定义了from 和to 属性,用来定义范围的起始位置和结束位置,这两个属性是非共享的,也不可继承。
使用构造函数代替“工厂函数”
function Range(from, to){ //构造函数初始化
this.from = from;
this.to = to;
}
Range.prototype = { //所有范围对象都继承自这个对象
includes: function(x){
return this.from <= x && this.to >= x;
},
foreach: function(f){
for(var x = Math.ceil(this.from); x <= this.to; x++)
f(x)
},
toString: function(x){
return "(" + this.from + ".." + this.to + ')';
}
};
var t = range(1,4);
t.foreach(console.log); // 输出 1 2 3 4
console.log(t.toString());// 输出 (1..4);
t.includes(3); // 输出 3
上述两个函数的不同之处
Range()构造函数 | range()工厂函数 |
---|---|
通过new关键字调用,不必调用inherit() | 不需要使用new,但是需要调用inherit() |
使用构造函数调用创建对象 | 调用普通函数创建对象 |
通过this关键字获取这个新对象,不必返回新对象。 | 函数需要返回新建立的对象 |
构造函数会自动创建对象,然后将构造函数作为这个对象的方法来调用一次,最后返回这个新对象 | 函数里面的属性是不可继承的,每个对象的属性都拥有唯一的属性 |
原型对象的命名Range.prototype是强制的命名,对该函数的调用会自动使用Range.prototype作为新Range对象的原型 | 原型是range.methods,这种命名方式方便且具有很好的语义,担又过于随意。 |
Range()构造函数与range()工厂函数的相同之处 |
---|
两者的范围方法定义和调用方式是完全一样的 |
2、构造函数是用来初始化新创建的对象的。
使用关键字new来调用构造函数会自动创建一个新的对象,因此构造函数本身只需初始化这个新对象的状态即可。
调用构造函数的一个重要的特征: 构造函数的prototype属性被用作新对象的原型。这意味着通过一个构造函数创建的所有对象都继承自一个相同的对象,因此他们都是同一个类的成员。
3、构造函数的内部原理、
1、在函数体最前面隐式的加上this = {}
2、执行this.xxx = xxx;
3、隐式的返回 this
4、在用new操作符创造实例时,会经历如下4个阶段:
- 创建一个新对象,此时该对象为空对象。
- 将构造函数的作用域赋给这个空对象(因此构造函数中的this就指向了该空对象)
- 执行构造函数内的代码(由于this指向了该空对象,此步骤为新对象添加属性)
- 返回新对象(这里针对构造函数没有返回值或返回值为基本类型时。如果构造函数内有引用类型的返回值,那么该返回值会替代第一步创建的对象作为构造函数的返回值。)