一、对象创建
1、new Object 方式
直接赋上属性和方法
var obj = new Object(); obj.name = '娃娃'; obj.showName = function(){ alert(obj.name); }
//调用 obj.showName();
缺点:每次使用都要写同样的代码,不能重用
2、工厂方法方式
把同样的代码封装在一个函数方法里,是对1、改进,减少重复代码。
function CreatePerson(name){ var obj = new Object(); //原料 obj.name = name; //加工 obj.showName = function(){ alert(this.name); } return obj;//出厂 } var p1 = CreatePerson('哇哇'); p1.showName(); var p2 = CreatePerson('哈哈'); p2.showName(); //其实就是简单的封装函数,整个过程像工厂的流水线,所以叫工厂方式
缺点:无法识别创建的对象的类型。因为全部都是Object,没有区分度,不像Date、Array等,因此出现了构造函数模式。
3、构造函数方式
函数名首字母大写,这是为了和普通函数区分,而且有this指针。
function CreatePerson(name){ this.name = name; this.showName = function(){ alert(this.name); } } var p1 = new CreatePerson('娃娃'); p1.showName(); var p2 = new CreatePerson('哈哈'); p2.showName();
构造函数本身也是普通函数,取决于使用的方式,可以new(当作构造函数),也可以直接调用(当作普通函数),两者区别是this指针指向不一样。
new CreatePerson('haha'); //CreatePerson CreatePerson('haha'); //window
new 内部操作
function CreatePerson(name){ var obj = {}; //声明一个空对象obj obj._proto_= CreatePerson.prototype; //把这个对象的_proto_属性指向构造函数的原型对象,这样obj就可以调用CreatePerson原型对象下的所有方法 。 CreatePerson.apply(obj); //用apply方法让this指向obj对象 this.name = name; //obj对象添加属性,方法 this.showName = function(){ alert(this.name); }; return obj;//返回这个对象 }
缺点:可见这两个对象并不是共用一个方法,每new一次,系统都会新创建一个内存,这两个对象各自有各自的地盘,但他们具有相同的功能,还不共用。
alert(p1.showName==p2.showName);//false
4、原型+构造函数方式 最优方式
原型:
每个函数都有一个prototype属性,它是一个对象,也称作原型对象,我们可以把方法和属性写在它上面(不过原型对象不仅仅有我们写的属性和方法,还有别的),设计时概念。
而通过这个函数创建出来的实例对象,都能共享这个原型对象下的方法和属性。
编写方法:
把共享的属性和方法定义在函数的prototype下,不共享的内容通过构造函数来创建。
function CreatePerson(name){ //定义不共享内容 this.name = name; } //定义共享内容 CreatePerson.prototype.showName = function(){ alert(this.name); } var p1 =new CreatePerson('娃娃'); p1.showName(); var p2 = new CreatePerson('哈哈'); p2.showName(); alert(p1.showName==p2.showName);//true
由此也可以看出,showName()方法是共享的,也就是说他们共用一个内存,更进一步的说它们存在引用关系,也就是说你更改了p1的showName也会影响p2的showName。
_proto_属性:
运行时属性,每个实例化对象都有_proto_属性,它是一个指针,指向函数的prototype(设计时属性),保存了函数的prototype的地址,通过该_proto_属性可以让同一构造函数的多个实例对象能共享这个构造函数的prototype(设计时定义)下的方法和属性。
js中任何对象的值都是保存在堆内存中,我们声明的变量只是一个指针,保存了这个对象的实际地址,所以有了地址就能找到对象。
所以,_proto_属性实际就是实例化对象和原型对象之间的连接。
二、原型链
每个函数都可以成为构造函数,每个函数都有原型对象,每个原型对象也可以是一个实例化对象。
创建了构造函数Function的实例化对象fun,而Function的原型对象(Function.prototype),又是Object的实例对象,根据前边介绍,fun有个_proto_属性,指向了Function.prototype,而Function.prototype(是Object的实例对象)又指向了Object.prototype。
所以,通过_proto_属性,就形成了一条原型链。每个实例化对象都可以访问到链子上方的方法和属性,所以fun是可以访问Object原型对象下的方法和属性的。实际上所有对象都可以访问到Object的原型对象。
function Aaa(){} Aaa.prototype.num = 3; var a1 = new Aaa(); a1.num =10; alert(a1.num); //10