构造函数
在JS中,使用构造函数时要注意以下两点:
构造函数用于创建某一类对象,其首字母要大写 构造函数要和new一起使用才有意义
new在执行时会做四件事情:
[1] 在内存中创建一个新的空对象。
[2] 让this指向这个新的对象。
[3] 执行构造函数里面的代码,给这个新对象派加属性和方法。
[4] 返回这个新对象(所以构造函数里面不需要 return)。
实例函数和原型函数的关系图示:
_ _ proto_ _作用:指向构造函数的原型属性constructor
通过这两个属性就可以访问到原型里的属性和方法
【3】、工厂模式
为了解决多个类似对象声明的问题,我们可以使用一种叫工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。
function createObject(name,age){
var obj=new Object(); //创建对象
obj.name=name; //添加属性
obj.age=age;
obj.run=function(){ //添加方法
return this.name+this.age+'运行中...';
};
return obj; //返回对象引用
};
【4】、构造函数模式
(可以理解为改良后的工厂化)
function Box(name,age){ //创建一个对象,所有构造函数的对象就是object
this.name=name; //添加一个属性
this.run=function(){
return this.name+this.age+'运行中...';
};
};
function Desk(name,age){ //创建一个对象,所有构造函数的对象就是object
this.name=name; //添加一个属性
this.run=function(){
return this.name+this.age+'运行中...';
};
};
var box1=new Box('Lee',100); //实例化,必须new出Box才能实例化
var box2=new Box('jack',200); //实例化
alert(box1.run);
alert(box2.run);
构造函数方法的一些规范:
1.构造函数也是函数,但函数名第一个字母大写
2.必须使用new关键字构造函数名(),new Box()
3.必须使用new运算符
函数构造与工厂模式的不同:
1.构造函数没有new Object ,但它后台会自动var obj=new Object
2.直接将属性和方法赋值给this对象(this就相当于obj)
3.没有return语句(构造函数不需要返回对象引用,它是后台自动返回的)
构造函数与普通函数的区别:
调用方式不同,构造函数必须使用new运算符来调用
构造函数也是函数,但函数名第一个字母大写
构造函数,用普通函数调用一般是无效的,必须使用new关键字
对象冒充
function Box(name,age){ //创建一个对象,所有构造函数的对象就是object
this.name=name; //添加一个属性
this.run=function(){
return this.name+this.age+'运行中...';
};
};
var o=new Object();
Box.call(o,'Lee',100); //对象冒充
alert(o.run());
var box1=new Box('Lee',100); //实例化后地址为1
var box2=new Box('Jack',200); //实例化后地址为2
alert(box1.name==box2.name); //true
alert(box1.age==box2.age); //true
alert(box1.run()==box2.run()); //true
alert(box1.run==box2.run); //false,因为他们比较的是引用地址
【5】、原型
我们创造的每一个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法
构造函数中的属性和方法叫做实例属性、实例方法
function Box(name,age){
this.name=name; //实例属性
this.age=age;
this.run=function(){ //实例方法
return this.name+this.age+'运行中...';
};
};
原型
function Box(){}; //构造函数函数体内什么都没有,这里如果有,叫实例属性、实例方法
Box.prototype.name='Lee'; //原型属性
Box.prototype.age=100; //原型属性
Box.prototype.run=function(){ //原型方法
return this.name+this.age+'运行中...';
};
var box1=new Box();
var box2=new Box();
alert(box1.run());
alert(box2.run());
alert(box1.constructor); //构造属性,可以获取构造函数本身
如果是实例方法,不同的实例化,它们的方法地址是不一样的,是唯一的
如果是原型方法,那么它们的地址是共享的,大家都是一样的
alert(box1.run==box2.run); //true
alert(box1.prototype); //这个属性是一个对象,是访问不到的
alert(box1.proto); //这个属性是一个指针,指向prototype原型对象,
判断一个对象实例是否指向了对象的原型对象
alert(Box.prototype.isPrototypeOf(box1)); //只要实例化对象,即都会指向
原型模式的执行流程:
1.先查找构建函数实例里的属性或方法,如果有,立刻返回
2.如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回
function Box(){
this.name='Jack'; // 实例中的属性
};
Box.prototype.name='Lee'; //原型中的属性
var box1=new Box();
var box2=new Box();
alert(box1.name); //Jack
alert(box2.name); //因为实例属性不会共享,只能访问原型属性,原型属性是可以共享的,因此访问到Lee
delete box1.name; //删除实例中的属性
delete Box.prototype.name; //删除原型中的属性,一般不用
Box.prototype.name='kkk'; //覆盖原型中的属性
alert(box1.name);
如何判断属性是否在构造函数的实例里
alert(box.hasOwnProperty('name')); //实例有返回true,否则返回false
只要能访问到就返回true,无论是实例属性还是原型属性
alert('name' in box1);
判断只有原型中有属性
function isProperty(object,property){
return !object.hasOwnProperty(property)&&(property in object);
}
【6】、原型的字面量写法
为了让属性和方法更好地体现封装的效果,并且减少不必要的输入
原型的创建可以使用字面量的方式
function Box(){};
Box.prototype={ //使用字面量的方式创建原型对象,这里的{}就是对象,相当于object,{}表示new object
constructor:Box, //强制指向Box
name:'Lee',
age:100,
run:function(){
return this.name+this.age+'运行中...';
}
};
var box=new Box();
alert(box.name);
重写会带来问题
内置引用类型的功能扩展(不推荐)
String.prototype.addstring=function(){
return this+',被添加了!';
};
var box='Lee';
alert(box.addstring());
【7】、原型的缺点:
1.无法保持独立性
2.共享
3.不能重写,重写就会被覆盖
function Box(){}; Box.prototype={ constructor:Box, name:'Lee', age:20, family:['哥哥','姐姐','弟弟','妹妹'] }; var box=new Box(); box.family.push('妈妈'); //在第一个实例修改后,引用类型保持了共享
alert(box.family);
var box2=new Box();
alert(box2.family); //共享了box1添加后的引用类型的原型
因为共享的原因,很多开发者放弃使用原型
组合构造函数+原型模式
function Box(name,age)={ //保持独立地用构造函数
constructor:Box,
name:'Lee',
age:20,
family:['哥哥','姐姐','弟弟','妹妹']}
Box.prototype={ //保持共享的用原型
constructor:Box,
run:function(){
return this.name+this.age+'运行中...';
}
};
var box1=new Box();
box1.family.push('妈妈');
alert(box1.family);
var box2=new Box();
alert(box2.family); //在第一个实例修改后,引用类型没有使用原型,所以没有共享
【8】动态原型模式
将原型封装到构造函数里
function Box(name,age)={
constructor:Box,
name:'Lee',
age:20,
family:['哥哥','姐姐','弟弟','妹妹']
alert('原型初始化开始');
Box.prototype.run=function{
return this.name+this.age+'运行中...';
}
alert('原型初始化结束');
};
var box1=new Box();
box1.family.push('妈妈');
alert(box1.family);
var box2=new Box();
alert(box2.family);
出现问题:原型初始化多次,会浪费资源,但原型初始化一次即可
解决方法:
将原型封装到构造函数里
function Box(name,age)={
constructor:Box,
name:'Lee',
age:20,
family:['哥哥','姐姐','弟弟','妹妹']
if(typeOf this.run != 'function'){ //仅在第一次调用的时候初始化
alert('原型初始化开始');
Box.prototype.run=function(){
return this.name+this.age+'运行中...';
}
alert('原型初始化结束');
};
};
var box1=new Box();
box1.family.push('妈妈');
alert(box1.family);
var box2=new Box();
alert(box2.family);
当第一次调用的时候,run不存在,当第二次调用,run就存在了,不会再初始化了
【9】寄生构造函数
寄生构造函数=工厂模式+构造函数
function Box(name,age){
var obj=new Object(); //创建对象
obj.name=name; //添加属性
obj.age=age;
obj.run=function(){ //添加方法
return this.name+this.age+'运行中...';
};
return obj; //返回对象引用
};
var box1=new Box('Lee',100);
alert(box1.run());
var box2=new Box('Jack',200);
alert(box2.run());
【10】稳妥构建函数
在一些安全环境中,比如禁止使用this和new,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。
function Box(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.run=function run(){
return this.name+this.age+'运行中...';
};
return obj;
}
var box1=Box('Lee',100);
alert(box1.run());
var box2=Box('Jack',200);
alert(box2.run());