JavaScript面向对象
一、函数(构造函数)、原型、实例对象之间的关系
A、函数与原型的关系
1、只要创建了一个函数,那么它就会根据一组特定的规则为该函数创建一个prototype属性(原型对象)
如:
function fun(){ } console.log(fun.prototype); //fun {},说明prototype是一个对象
注意:原型中定义的属性、方法是所有调用构造函数实例化对象所共享的
2、prototype属性都会自动获得一个constructor(构造函数)属性,constructor属性包含了prototype属性所在函数(fun)的指针,用来标识实例化对象的类型(并没有太大的实际意义),
如果构造函数中定义了原型中的同名属性方法时,那么实例就会调用重新定义的属性与方法了
如:
function fun(name){ console.log(fun.prototype.name == this.name);//true(yjh) this.name = "yjh1"; console.log(this.name);//yjh1 console.log(fun.prototype.name == this.name);//false(yjh,yjh1) } fun.prototype = { constructor: fun, name: "yjh" } var fun1 = new fun(); console.log(fun.prototype.constructor == fun); //true console.log(fun1.constructor == fun); //true
B、实例化对象与原型的关系
1、当函数使用new操作符实例化一个对象时,那么对象就包含了一个内在的指向原型的__proto__属性,它只存在实例对象与原型对象之间
如:
function fun(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert(this.name); } } fun.prototype = { constructor: fun, age: 22, sayName: function(){ alert(this.age); } } var fun1 = new fun("yjh","23"); console.log(fun1.__proto__) //fun { age="22", sayAge=function()} console.log(fun1.__proto__ == fun.prototype); //true
C、实例对象与函数(构造函数)的关系
1、当函数使用new操作符实例化一个对象时,构造函数中的this对象为实例对象,实例对象获取了绑定到this中的属性和方法
如:
function fun(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert(this.name); } } var fun1 = new fun("yjh","23"); fun1.sayName(); //yjh
D、函数(构造函数)与实例对象、原型三者之间的关系
1、在调用实例化对象属性,方法时,那么首先会搜索实例对象自身定义的属性和方法,如果没有的话,会继续搜索原型
如:
function fun(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert(this.name); } } fun.prototype.age = "22"; fun.prototype.sayAge = function(){ alert(this.age); } var fun1 = new fun("yjh","23"); fun1.age = 24; fun1.sayAge(); //24 ,调用了原型中的方法;
首先搜索sayAge方法,实例对象中没有,搜索原型,在原型中找到sayName方法,继续搜索age属性,发现在fun1实例对象中已有定义age值,因此直接使用,不再继续搜索了;如果没有直接在fun1实例对象定义age属性的话,那么结果就是23了,原因是因为构造函数重新定义了实例对象的age属性
总结:实例化对象,原型对象,构造函数三者之间的关系:
1、每个函数都有一个prototype属性,这个属性是构造函数通过new操作符实例化对象的原型对象,prototype对象定义的属性和方法是所有实例化对象所共享的;
2、其次,prototype对象还包含一个constructor属性,指向prototype对象所在的函数;
3、实例对象有一个内部属性__proto__,指向实例对象的prototype原型对象;
4、关于this对象,构造函数通过new操作符实例化一个对象时,其this对象就绑定为实例对象。(关于我对this的理解:http://www.cnblogs.com/yangjunhua/archive/2012/04/28/2475361.html)
二、面向对象模式:
对函数的初步理解:
a、JavaScript中的任何一个函数都是一个Function类型的一个实例,也是一个Object类型的实例,定义了一个function函数,那么它就是一个实例对象,实例对象的内部属性__proto__指向了Object构造函数的原型prototype属性,因此实例继承了Object对象的默认属性和方法;
b、普通函数默认返回undefined,构造函数返回一个实例对象。
1、创建对象,使用一个特定接口new Object()
缺点:使用同一个接口创建很多对象,会产生大量重复的代码
2、使用工厂模式,用函数来封装,以特定接口创建对象
如:
function createObj(name,age){ var o = new Object(); o.name = name; o.age = age; return o; } var o1 = createObj("yjh",23)
优点:解决了使用一个接口创建多个相似对象产生大量重复代码的问题
缺点:没有解决对象识别的问题,即o1是怎么样的一个对象类型
3、构造函数模式,JavaScript没有类概念
如:
function CreateObj(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert("hi" + this.name); } } var obj1 = new CreateObj("yjh1",23); var obj2 = new CreateObj("yjh2",23);
优点:解决了实例对象类型识别的问题,obj1,obj2对象为CreateObj类型
缺点:构造函数定义的属性和方法不是所有实例所共享的,各实例调用的方法是两个不同Function类型的实例(obj1.sayName != obj2.sayName)
4、原型模式
如:
function CreateObj(){ } CreateObj.prototype = { constructor: CreateObj, name: "yjh", age: 23, colors: ["a","b"], sayName: function(){ alert(this.name); } } var obj1 = new CreateObj(); var obj2 = new CreateObj(); alert(obj1.sayName == obj2.sayName);//true obj1.colors.push("c"); alert(obj2.colors);//a,b,c
说明:调用obj1,obj2实例的属性和方法,首先会搜索实例自身定义的属性和方法,如果没有,由于实例的__proto__内部属性指向原型,
因此会继续搜索原型中定义的属性和方法
优点:原型中定义的属性和方法是所有实例对象所共享的,解决了多个函数实现同一功能的问题
缺点:如果在原型中定义的属性包含的是引用类型的值,那么通过修改一个实例属性值会影响到另一个实例属性值,这正是由于原型的共享本质所导致的
5、组合模式(构造函数模式与原型模式)
如:
function CreateObj(name,age){ console.log(this.name);//yjhyjh this.name = name; this.age = age; this.colors = ["a","b"]; } CreateObj.prototype = { constructor: CreateObj, name: "yjhyjh", sayName: function(){ return this.name; } } var obj1 = new CreateObj("yjh1",23); var obj2 = new CreateObj("yjh2",23); alert(obj1.sayName == obj2.sayName);//true alert(obj1.sayName());//yjh1 alert(obj2.sayName());//yjh2 obj1.colors.push("c"); alert(obj2.colors);//a,b
说明:把所有实例不需要共享的属性定义在构造函数中,把需要共享的属性,方法定义在原型中,互补构造函数模式和原型模式的优缺点,
原型是所有实例化对象的原型对象,实例与原型之间是通过实例内部属性__proto__连接到原型,所有实例共享原型中的属性和方法,如果构造函数中重新定义了原型中的同名属性、方法,
那么实例对象将会调用构造函数中定义的属性与方法。
6、继承(实现继承,原型链)
就是把一个构造函数的原型作为另一个构造函数的实例化对象,那么这个实例化原型对象就会继承另一个构造函数的原型属性和方法,这就是所谓的原型链
如:
function Fun1(){ this.name = ["yjh1","yjh2"]; } Fun1.prototype = { constructor: Fun1, sayName: function(){ alert(this.name) } } function Fun2(){} Fun2.prototype = new Fun1(); var fun2 = new Fun2(); fun2.sayName();//yjh1,yjh2 fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"]; var fun3 = new Fun2(); alert(fun3.name);//yjh1,yjh2,yjh3
缺点:来自包含引用类型值的原型,原型中定义的属性,方法是所有实例所共享的,Fun2.prototype原型对象是Fun1类型的一个实例,
因此原型对象中有包含引用类型值的name属性,当实例化多个Fun2类型对象时,所有的实例对象都共享这个原型name属性,通过修改实例中的name属性值,
会直接修改原型中定义的name属性值
7、组合继承(继承与借用构造函数)
如:
function Fun1(){ this.name = ["yjh1","yjh2"]; } Fun1.prototype = { constructor: Fun1, sayName: function(){ alert(this.name) } } function Fun2(){ Fun1.call(this); } Fun2.prototype = new Fun1(); var fun2 = new Fun2(); fun2.sayName();//yjh1,yjh2 fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"]; var fun3 = new Fun2(); alert(fun2.name);//yjh1,yjh2,yjh3 alert(fun3.name);//yjh1,yjh2
说明:由于构造函数中定义的属性,方法不是所有实例共享的,而且会覆盖原型中定义的属性与方法,所以它不会由于实例化原型对象(Fun2.prototype)中包含了引用类型值的属性而
存在的问题