对象在js中的定义即为:无序属性的集合,其属性可以包含基本值、对象或者函数。
每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型,也可以是开发人员定义的类型。
- 创建对象最简单的方式这里也已经介绍。
var person=new Object();
new操作符
1、Object()是一个构造函数,平常我们使用函数时,一个是直接调用Object(),另一种是通过对象的方法。其函数内部的this值指的是其所处的环境对象。
2、在函数前面加上new,可以改变函数的this值,还可以共享函数的原型。
new操作符具体干了什么?参照这
var obj = new Base();
这是一个基本的使用new操作符的代码。然而一个new操作符却悄悄的干了这些事。
var obj = {};//第一行创建了一个空对象obj
obj.proto = Base.prototype;
//proto是每个新建对象的内部属性,指向该实例对象的构造函数的原型对象。
//第二行、将这个空对象的proto成员指向了Base函数对象prototype成员对象
Base.call(obj);//我们将Base函数对象的this指针替换成obj,然后再调用Base函数原始创建对象的方式存在许多不足的地方
使用new操作符,Object构造函数,或是对象字面量。
问题1、导致一个接口创建很多个对象,以至于使得产生大量的重复代码。
问题2、函数作为特殊的对象,每一次创建对象都会实例化一个同功能的function对象,浪费。
var person1={
name:"xiaoming",
age:16,
sayname:function(){
alert(this.name);
}
}
var person2={
name:"xiaohong",
age:18,
sayname:function(){
alert(this.name);
}
}
person1.sayname();
person2.sayname();如何解决以上存在的问题
解决问题的道路不是马到成功,也是在提供解决方案后,又进行完善改进的过程。方案1:解决了重复代码的问题——工厂模式
function createPerson(name,age,job)
{
var obj=new Object();
obj.name=name;
obj.age=age;
obj.job=job;
obj.saynaName=function(){
alert(this.name);
};
return obj;
}
var person1=createPerson("dudu",22,"Painter");
person1.saynaName();
优点:通过传递参数,来使用同一个function实例createPerson,达到节省代码的效果。
缺点:1、无法知道person是一个怎样的对象类型。(即对象识别)2、同功能的sayName函数对象需要被重复创立。方案2:解决了对象识别问题以及函数对象重复创立问题——构造函数模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayname;
}
function sayname(){
alert(this.name);
}
var person1=new Person("dudu",22,"Painter");
person1.sayName();
解释:采用构造函数模式,函数名即为对象名,且首字母大写,配合new操作符使用。对象的方法的定义转移到了构造函数外部,每次实例化对象时,该对象的方法不用在进行实例,而是共享全局作用域中的sayname函数。
缺点:1、全局作用域的函数仅被某个对象调用,不合乎全局对象的设置效果。2、违背了对象的封装特性。方案3:解决了对象方法定义的封装问题——原型模式
function Person(){}
Person.prototype.name="Nicholas";
Person.prototype.age=29;
Person.prototype.job="Softwar Enginear"
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
person1.sayName();
根据上图,
解释:创建一个新的函数对象后,该函数对象就会拥有一个prototype属性,这个属性是一个指针,指向一个对象,该对象的用途是包含可以由特定类型的所有实例共享的属性和方法。包括一个构造函数(constructor)属性,这个属性包含一个指向prototype属性所在函数的指针。其他属性都是用户自己建立的。
另外,由该构造函数实例的对象,也包含一个指针[[prototype]],指向构造函数的原型对象。在FF、Safari、Chrome在每个对象上都支持一个属性
_proto_
。需要知道这个连接存在的是实例与构造函数的原型对象之间,而你不是存在于实例与构造函数之间。缺点:1、原型对象的属性与方法的添加不够封装。2、由于原型对象的共享,导致所有实例共享相同的属性值。
-
方案3.1:解决了原来原型模式的问题1——封装的原型模式
function Person(){}
var friend=new Person();
Person.prototype={
constructor:Person,
name:"Nichoas",
age:29,
job:"software ENgineer",
sayName:function(){
alert(this.name);
}
};
friend.sayName();//error
上面为重写前,下面为重写后
以上的代码实际上重写了原型对象,导致切断了构造函数与最初原型之间的关系,需要constructor:Person,
进行重新指定。
缺点:重写原型对象切断了现有原型对象与任何之前已经存在的对象实例之间的联系,他们仍引用的是最初的原型。 -
方案4:解决了原型模式的问题——构造函数与原型混成的模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["bobo","xixi"];
}
Person.prototype={
constructor:Person,
asyName:function(){
alert(this.name);
}
};
var person1=new Person("du",22,"painter");
var person2=new Person("susu",22,"singer");person1.friends.push("wawa"); alert(person1.friends);//bobo,xixi,wawa alert(person2.friends);//bobo,xixi
解释:对比两次alert结果,发现构造函数内部的内容不被对象实例共享。这也达到了对象之间个性化的设置。
缺点:还是不够封装啊!
-
方案4.1:弥补不够封装
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["bobo","xixi"];
//
if(typeof this.sayName!= "function")
{
Person.prototype.sayNmae=function(){
alert(this.name);
};
}
}
只有在sayName方法不存在的情况下,才会将他添加在原型中。这段代码只会在初次调用构造函数的时候才会执行。