对象的定义:无序属性的集合,属性的值能够是基本值、对象或者函数.
每一个对象都是基于一个应用类型建立的,这个引用类型能够是内置的(例如Object Array Math),也能够是用户自定义的. web
全部的对象都是继承自Object的,所以咱们能够从Object着手建立对象. 设计模式
//经过new 关键字建立对象 var person = new Ojbect(); person.name = 'yuhualinfeng'; person.age = 30; person.job = 'web developer'; //经过对象字面量建立对象 var person = {}; person.name = 'yuhualinfeng'; person.age = 30; person.job = 'web developer';
基于Object建立对象有两种形式,一种是使用new关键字,另外一种是使用对象字面量.
使用这种方式建立对象的缺点是:当建立多个相同类型的对象时,会产生许多重复的代码,假如我要三个person对象,我就须要写三相同结构的代码,为了解决这个问题,咱们引入了工厂模式建立对象. ide
工厂模式是软件工厂领域一种广为认知的设计模式,这种模式抽象了建立具体对象的过程. 函数
function createPerson(name,age,job){ var obj = new Object(); obj.name = name; obj.age = age; obj.job = job; return obj; } var person1 = createPerson('yuhualingfeng',30,'web developer'); var person2 = createPerson('obama',45,'president');
咱们建立了两我的物对象,假如咱们基于Object建立对象,那么createPerson内的代码就会重复编码.
可是使用这种模式建立的对象任然有一个问题:没法得知建立的对象的类型名.解决这问题的可行方法是使用构造函数建立对象. this
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new Person('yuhualingfeng',30,'web developer'); var person2 = new Person('obama','45','president');
这里咱们建立了一个名为Person的引用类型,而后咱们用new 关键字实例化此引用类型,这个过程能够细化为如下四个过程: 编码
建立一个新对象 spa
将构造函数的做用域赋值给新函数(所以this就指向这个新对象) prototype
执行构造函数中的代码(为this对象赋值,等同于为新对象赋值) 设计
返回新对象 指针
咱们能够用instanceof来检测person1,person2的对象类型是否为Person.
alert(person1 instanceof Person); //true alert(person2 instanceof Person); //true alert(person1 instanceof Object); //true 由于Person继承自Object,因此这里同样成立.
注:细心的朋友应该会注意到,这里的构造函数的首字母是大写,这里们遵循一个规范,普通函数的首字母大写,普通函数的首字母小写.
构造函数也有本身的缺点,你们能够看到Person包含一个sayName的函数(方法),函数也是对象(函数式Function的实例),因此每实例化一个Person,就会产生一个sayName方法,也就是一个对象,
随着建立的person实例怎多,产生的对象也相应增多,最终致使更多的内存,那么咱们能不能找到更好的解决办法呢,是确定的.
咱们每建立一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象而这个特定对象的用途是包含能够由特定类型的全部实例共享的属性和方法.这就意味着原型对象不会由于实例的增多二占用
更多的内存.每一个原型对象都默认有一个constructor属性,故名思议,这个属性指向构造函数.下面展现了经过原型对象来建立对象.
function Person(){ } Person.prototype.name = "yuhualingfeng"; Person.prototype.age = 30; Person.prototype.job = "web developer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1 = new Person(); person.sayName(); //yuhualingfeng var person2 = new Person();
这里Person.prototype.constructor指向的是Person.固然你也能够向下面这样直接给原型对象赋值来建立对象.
Person.prototype={ constructor:Person, name:"yuhualingfeng", age:30, job:"web developer" };
这里之因此添加了constructor属性是应为直接给原型对象赋值会把原型对象的指针指向另外一个对象,之前默认的值将没法访问到.
顺便给你们介绍两个与原型对象相关的方法和in关键字:
isPrototypeOf:判断是某对象否为实例的原型.
alert(Person.prototype.isPrototypeOf(person)); //true
hasOwnProperty:检测某属性是存在于实例中,仍是原型对象中.
alert(person1.hasOwnProperty('name')); //false,由于属性存在于原型中.
in操做符:in操做符有两种使用方式,一种是单独使用,一种是和for搭配使用,单独使用的做用是判断某属性是否在某实例中访问到(不管是在实例自身的仍是原型对象中的),for-in是枚举(循环)中使用.
//判断属性是否存在原型中 function hasPrototypeProperty(object,name){ return object.hasOwnProperty(name) && (name in object); }
原型模式建立对象的缺点:实例的原型对象是共享的,当修改一个实例的属性,若是属性的值为方法或者基本类型时,不会有什么影响,当属性为引用类型时,会影响其余实例的属性值.
综合构造函数模式和原型模式建立对象,咱们结合他们的优势,去粗取精,咱们组合使用构造函数模式原型模式.
经过构造函数建立对象的缺点是每一个方法都会在实例上从新建立,形成没必要要的内存消耗;经过原型建立对象的缺点在于实例引用类型值的属性会相互影响.综上考虑,咱们能够把存储值得属性放在构造函数中,把方法放在原型对象中.这种模式是建立对象使用最普遍的一种,能够说是建立对象的默认模式.
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; } Person.prototype = { constuctor:Person, sayName:function(){ alert(this.name); } }; var person = new Person('yuhualingfeng',30,'web developer'); person.sayName();
以上就是建立对象的几种模式,你们能够结合它们的优缺点和你自身建立对象的用处进行权衡,而后选择适合你的建立对象的模式.