JavaScript-形象理解面向对象、原型和继承

       这是第二次整体复习JavaScript的面向对象与原型方面的知识。这部分的知识比较抽象但是又十分重要。它对后期类库的封装,整体架构的实现非常重要。在这节我将对每个方式进行形象全面的讲解。

       本节要点

//JS面向对象和原型
//1.创建类:a.利用new Object()创建。b.使用字面量创建(直接实例化对象,不需要new运算符)c.利用构造函数创建
//2.实例化属性和原型属性的区别.实例对象和类的关系
//3.原型链的理解,prototype和__proto__的区别
//4.继承模式:a.工厂模式.b.原型链模式.c.组合模式.d.原型模式.e.原型组合模式.f.构造寄生

    1.JavaScript创建类

     利用object()创建

//1.利用Object
var obj = new Object();
obj.a = "A";
obj.b = "B";
obj.func = function(){
	console.log(this.a + this.b);	//这里可以用this
};
//cosole.log(this.a);				//这里的this是window下的
//obj.func();

     使用工厂模式

//集中实例化对象,工厂模式
var createObj = function(a,b){
	var obj = new Object();
	obj.a = a;
	obj.b = b;
	obj.func =function(){
		return this.a + this.b;
	};
	return obj;
}

var obj1 = createObj("a1","b1");
var obj2 = createObj("a2","b2");

console.log(obj1.func());
console.log(obj2.func());
console.log(obj1 instanceof Object);	//无法判断obj1的具体类型

    使用构造函数

//2.利用构造函数
//利用构造函数能集中实例化,但是没有new Object(),但它内部自动进行new Object();
//this直接表示这个对象
//不需要返回对象,后天会直接返回
//delete方法可以删除实例属性和方法及原型属性和方法
//hasOwnProperty()判断方法或属性是否在构造函数中。而in则表示既在原型也在实例中
//instanceof 和 isPrototypeOf分别表示是否为一个对象的实例和是否为一个函数的原型。
//使用构造函数时,首字母要大写
//必须使用new运算符
/*
function Obj(a,b){
    this.a = a;
    this.b = b;
    this.func = function(){
        return this.a + this.b;
    };
}
*/
//var obj = new Obj("a1","b1");
//console.log(obj.func());
//console.log(obj instanceof Obj);        //可以判断具体的类
//var o = new Object();
//call方法的理解:相当于把Obj方法放在对象o中
//即改变Obj的上下文对象为o
//Obj.call(o,"a2","b2");
//console.log(o);
//理解构造函数内部函数的关系
//var objTest = new Obj("a1","b1");
//console.log(obj.func() == objTest.func());    //计算过后的值相等
//console.log(obj.func == objTest.func);        //函数的引用地址是不同的,是两个不同的实例化对象

//使用原型。原型prototype是函数的一个自带属性。可以看作是构造函数在实例化时创作的那个对象。
//使用原型的好处:可以共享变量和方法
//function Aaa(){}
//var aaa = new Aaa();
//Aaa.prototype.b = "b";        //必须要var aaa = new Aaa()过后才能访问到这个b
//aaa.__proto__.b = "b1";        //共享属性
//console.log(aaa.b);            //b1
//Aaa.b = "b1";                //相当于静态属性,必须通过Aaa.b才能访问到,aaa.b是访问不到的
//console.log(Aaa.b);
//Aaa.prototype.func = function(){            //原型方法的地址是相同的,实例方法每次实例化时都要开辟新的空间
//    console.log(this);            //这个this代表构造函数实例化的原型
//}
//__proto__和prototype的区别
//__proto__是原型的指针,而prototype是函数的一个属性
//var aaa = new Aaa();
//console.log(aaa.__proto__ == Aaa.prototype);    //true。
//constuctor是原型的一个属性,可以得到构造函数
//console.log(Aaa.prototype.constructor == aaa.__proto__.constructor);    //true

   2.原型

    构造函数创建原型和字面量创建原型的区别

//1.构造函数创建:
function Person(){};
Person.prototype.name = "Wu";
Person.prototype.age = 12;
console.log(Person.prototype.constructor);        // Person()
//2.字面量创建
function Person(){};
Person.prototype ={
    //constructor : Person,                        //将constructor指针指向Person
    name : "wu",
    age : 12
};
console.log(Person.prototype.constructor);        // Object()

     原型的缺点

function Family(){};
Family.prototype = {
	size : 3,
	member : ["mother","father"],
	show : function(){
		return this.size;	
	}
};

//首先查看原型的数据共享问题
var family1 = new Family();
console.log(family1);
family1.size = 4;							//虽然Family.prototype中有size属性,但这里实际上是在family1对象中创建了一个size属性。
console.log(family1.show());				//这里由于family1中有实例属性size,根据就近原则,所以输出是4
//family1.member = ["mother","father","me"];//这里实际上也是创建了一个实例属性
family1.member.push("me");					//而这个则是对引用属性即原型属性的值进行了修改,而原型属性具有共享的功能
var family2 = new Family();
console.log(family2.show());				//这里的family2中没有size的实例属性,只有原型属性
console.log(family2.member);				//数组是修改后的

      使用构造函数+原型链的模式

//使用构造函数+原型的组合模式,可以解决构造函数传参和数据私有化的问题,但是感觉封装性不好
function Family(member){
	this.member	= member;
};
Family.prototype = {
	constructor : Family,
	size : 3,
	show : function(){
		return this.size;
	}
};

//使用动态原型模式
function Family(member){
	this.member = member;
	if(typeof this.show != "function"){			//避免重复实例化,这里的方法不能带括号,不然会出错
		console.log("****");
		//Family.prototype = {					//不能用字面量的方式
		//	constructor : Family,
		//	size : 3,
		//	show : function(){
		//		return this.size;
		//	}
		//};
		Family.prototype.show = function(){
			return this.member;
		};
	}
};

var family1 = new Family(["father","mother"]);
console.log(family1);
console.log(family1.show());
var family2 = new Family(["father","mother","me"]);
console.log(family2.show());

      使用寄生构造函数

//使用寄生构造函数。即构造函数 + 工厂模式
//每次实例化构造函数都创建了一个新的Obj,保持的对象属性的私有化
function Family(member){
	var obj = new Object();
	obj.member = member;
	obj.show = function(){
		return this.member;
	};
	return obj;
}
var family1 = new Family(["father","mother"]);
family1.member.push("sister");
var family2 = new Family(["father","mother","me"]);
console.log(family1.show());
console.log(family2.show());

     3.继承

      基本继承

//基本继承
function Man(){};				//子类型
function Person(){				//超类型
	this.age = 2;
};
Man.prototype = new Person();	//利用原型链实现继承
var man = new Man();
console.log(man.age);

    对象冒充继承

//使用对象冒充实现继承
function Person(age){			//一般来说,需要共享的数据放在原型中,需要私有化的数据放在构造函数中
	this.age = age;
};
Person.prototype.func = function(){
	return this.age;			//使用对象冒充无法访问到这个方法
};
var person = new Person(3);
function Man(age){
	Person.call(this,age);		//使用call函数。call函数相当如将Person方法作为Person的实例方法。即改变Person的上下文对象为Man
};								//但是使用对象冒充的方式只能继承实例属性和方法,无法继承原型中的属性和方法
var man = new Man(3);
console.log(man.age);

     使用对象冒充 + 原型链的方式

//使用对象冒充 + 原型链的方式解决对象冒充无法继承原型数据和方法的问题
//其中对象冒充可以继承实例方法和属性,原型链可以继承原型方法和属性

function Person(age){
	this.age = age;
};
Person.prototype.func = function(){
	return this.age;
};
function Man(age){
	Person.call(this,age);
};
Man.prototype = new Person();	//增加了原型链
var person = new Person(3);
var man = new Man(3);
console.log(man.func());

//使用原型继承
function obj(o){
	function F(){};
	F.prototype = o;
	return new F();
};
var person = {
	age : 2,
	member : ["father"]
};
var man = obj(person);
man.member.push("mother");		//这里改变的是引用类型的值,即改变的是person中的member,能实现数据的共享
var woman = obj(person);
console.log(woman.member);

     原型组合模式

//在使用原型链和构造组合模式时出现了:对象冒充时需要Person.call(this);然后在原型链时Man.prototype = new Person();
//出现了两次调用超类型的情况。为了解决这种情况,可以利用一个中转函数把超类型中的原型属性复制到子类型中,实例属性和方法则通过对象冒充来继承
function obj(o){				//中转函数
	function F(){};
	F.prototype = o;
	return new F();
};
function create(father,son){	//工厂模式
	var f = obj(father.prototype);
	f.constructor = son;		//constructor是原型的一个属性,在复制原型属性和子类型之后,应该还是修改它的constructor指向
	son.prototype = f;
};
function Person(age){			//超类型
	this.age = age;
};
Person.prototype.show = function(){
	return this.age;
};
function Man(age){				//子类型
	Person.call(this,age);
};
create(Person,Man);
var man = new Man(4);
console.log(man.show());


你可能感兴趣的:(JavaScript学习,javascript,面向对象,继承,原型,prototype)