普通创建对象的方法
一、基本模式
var people = new Object();
people.name = '吉吉国王';
people.weapon = '金箍棒';
//this是指的当前作用域下的对象,注意和谁调用这个方法有关
//也可以简单理解为this就指的这个函数属于谁,属于谁this指的就是谁
people.run = function(){
return this.name+'的武器是'+this.weapon
}
alert(people.name)
alert(people.run()) //注意方法的调用需要加()
缺陷:
- 如果创建多个对象会比较繁琐,效率低
- 实例与原型之间,没有任何办法,可以看出有什么联系
二、工厂模式
- 工厂模式:使用创建并返回特定类型的对象的工厂函数(其实就是普通函数,叫法不同而已)
- 创建过程类似于工厂生产的过程,即:原材料--加工--产品...
var creatPeople = function (name,weapon){
var obj = Object();
obj.name = name;
obj.weapon = weapon;
obj.run = function (){
return name+"的武器是"+weapon;
}
return obj;
}
var people1 = creatPeople("xiaomiang","gun");
alert(people1.run());
- 解决了多次重复创建多个对象的麻烦
- 问题:
- 创建出的实例之间,没有内在的联系,不能反映出它们是同一个原型对象的实例
- 创建的时候没有使用new关键字
- 会造成资源浪费,因为每生成一个实例,都增加一个重复的内容,多占用一些内存
三、构造函数模式
- new调用的函数为构造函数,狗在函数和普通函数区别仅在于是否使用了new来调用
- 所谓“构造函数”,就是专门用来生成“对象”的函数。它提供模板,作为对象的基本结构
- 构造函数内部使用了this变量。对构造函数使用new运算,就能生成实例,并且this变量会绑定在实例上
- instanceof验证原型对象与实例对象之间的关系
- 使用call和apply方法实现对象的冒充
function People(name,weapon){
this.name = name;
this.weapon = weapon;
this.run = function(){
return this.name+"的武器是"+weapon;
}
}
var xiaoZhang = new People("Zhang","knife");
var xiaoZhang = new People("San","棒");
alert(xiaoZhang.run()+'\n'+San.run());
alert(xiaoZhang.run==San.run);
//两个实例对象的地址是不同的,证明两个对象会占用两个地址空间的内存
//对象冒充
var monster = Object();
People.call(monster,'妖怪','葫芦');
alert(monster.run());
- 问题:浪费内存--使用构造函数每生成一个实例,都增加一个重复的内容,多占用一些内存。这样既不环保也缺乏效率
四、原型模式
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。可以把那些不变的属性和方法,直接定义在prototype对象上
function Peopleobj(){
Peopleobj.name = '赵云';
Peopleobj.weapon = '涯角枪';
Peopleobj.run = function(){
return this.name+"的武器是"+weapon;
}
}
var m1 = new Peopleobj();
//alert(m1.constructor);
//字面量形式写法
function Peopleobj1(){ }
Peopleobj1.prototype = {
constructor : Peopleobj1, //强制指回Peopleobj1
name : '小兵', //原型字面量形式会将constructor变为Object,
weapon : '锣',
job : ['巡山','打更'],
run : function() {
return this.name+"的武器是"+weapon;
}
}
var m2 = new Peopleobj1();
//alert(m2.constructor);
-
prototype方式定义的方法,函数不会拷贝到每一个实例中,所有实例共享prototype中的定义,节省了内存
-
prototype模式的验证方法
- isPrototypeOf() 用来判断某个prototype对象和某个实例之间的关系
- hasOwnPrototype() 每个实例都有这个方法,用来判断某一个属性到底是本地属性还是继承自prototype对象的属性
- in运算符,可以用来判断某个实例是否含有某个属性,不管是否为本地属性。还可以用来遍历某个对象的所有属性
-
对象的constructor属性用于返回创建该对象的构造函数
在JavaScript中,每个具有原型的对象都会自动获得constructor属性
- 缺陷:
- 构造函数没有参数。使用原型方式,不能通过给构造函数传递参数来初始化属性的值
- 属性指向的是对象,而不是函数时、函数共享不会造成问题,但对象却很少被多个实例共享,如果共享的是对象就会造成问题
五、构造函数和原型组合模式
- 定义:用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)。结果是,所有函数都只创建一次,而每个对象都具有自己的对象属性实例。
function Monster(name,arr) {
constructor : Monster,
this.name = name,
this.job = arr
}
Monster.prototype = {
run : function() {
return this.name+"的工作是"+this.job;
}
}
var monster1 = new Monster('小兵',['巡山','骑车','打牌']);
alert(monster1.run());
- 此外,组合模式还支持向构造函数传递参数,可谓集两家之长。
- 在接触的JS库中,jQuery类型的封装就是使用组合模式来实现的。
六、动态原型方法
-
基本思想与组合的构造函数原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义
-
组合模式中实例属性与共享方法(由原型定义)是分离的,这与纯面向对象语言不太一致;动态原型模式将所有的构造信息都封装在构造函数中,又保持组合的有点。
-
其原理就是通过判断构造函数的原型中是否定义了共享的方法和属性,如果没有则定义,否则不再执行定义过程。该方法只在原型方法或属性上定义一次,且将所有的构造过程都封装在构造函数中,对原型所做的修改能立即体现所有实例
function MonsterGo(name,arr) {
this.name = name;
this.job = arr;
// 创建多个实例时,只要初始化一次,提高效率
// 即判断run方法是否已经存在,存在就是function
if (typeof this.run != "function"){
alert('对象初始化开始');
MonsterGo.prototype.run = function() {
return this.name+"的工作是"+this.job;
}
alert('对象初始化结束');
}
}
var monster2 = new MonsterGo('浪',['巡山','骑车','打人']);
var monster3 = new MonsterGo('风',['喝酒','骑车','打人']);
alert(monster2.run());
alert(monster3.run());