理解JavaScript中的面向对象
1、工厂模式:用函数来封装,以特定接口创建对象的细节
- 解决了创建多个相似对象的问题,
- 多用于创建多个含有相同属性,和方法的对象,避免代码的重复编写;
- 没有解决对象识别的问题(即怎么知道一个对象的类型)(它的 instanceof 只能是Object)
function creatPerson(name,age,job) {
var o = new Object();
o.name= name;
o.age= age;
o.job= job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var p1 = creatPerson("hh",23,"worker");
var p2 = creatPerson("xx",26,"worker1");
p1.__proto__===p2.__proto__===Object.prototype;
2、构造函数模式:
缺点:每个方法都在每个实例上重新创建了一例子中p1.sayName!=p2.sayName。
function Person(name,age,job){
this.age = age;
this.name = name;
this.job = job;
this.sayName = function(){
alert(this.name)
}
//this.sayName = sayName; //解决方式
}
var p1 = new Person("hh",23,"worker");
var p2 = new Person("xx",26,"worker1");
//function sayName(){alert(this.name)} //解决方式
new的执行:
{
var obj ={};
obj.__proto__ = Person.prototype;
Person.call(obj);
return obj
}
p1 instanceof Person //true;
p2 instanceof Person //true;
p1 instanceof Object //true;
p2 instanceof Object //true;
这样是得p1和p2实例有了特定的类型, Person;
3、 原型模式
- 优点:他省略了构造函数初始化参数这个环节,原型中所有属性都被很多实例共享,共享对函数非常合适,基本属性也还行
通过在实例上添加同名属性,可隐藏原型中的对应属性值; - 缺点: 他的共享属性,对于包含引用类型值的属性 如果实例重新赋值没什么影响,和基本类型一样,如果是操作修改 就有些问题了,会使得所有实例获取到的该属性都被修改, 所以也不单独使用
function Person(){}
Person.prototype={
constructor:Person,
name:"ss",
friends:["s1","s2"],
sayName:function(){alert(this.name)}
}
var p1= new Person();
var p2= new Person();
p1.name = "p1"
console.log(p1.name) //p1
console.log(p2.name) //ss
p1.friends.push("s3");
console.log(p1.friends) //["s1","s2","s3"]
console.log(p2.friends) //["s1","s2","s3"]
继承
1、组合继承:(常用继承模式)
- 这样可以使得实例分别拥有各自的属性(包括引用类型的,实例间互补影响),又可以使用相同的方法;
- 缺陷: 无论什么情况下,都会调用两次父类的构造函数
function Father(){
this.name ="ss";
this.friends=[1,2,3,4]
}
function Son(){
Father.call(this);
}
Son.prototype = new Father(); // Son 原型获得Father上的属性,name和friends
var son1 = new Son(); // 此时调用Son构造函数为son1 实例上添加了属性(name和friends), 这些属性就屏蔽了原型中的同名属性;
// 调用两次Father构造函数的结果就是有两组属性,一组在实例上,一组在原型上;
2、寄生组合式:(理想的继承实现方式)
- 解决组合继承中的缺陷,生成两组属性,只在实例上生成原型;
- 通过构造函数来继承属性,通过原型链的混成形式来继承方法(就是给子类原型,指定一个父类的副本即可)
function inherit(son,father){
var prototype = object(father.prototype);
// 上句话,创建父类原型的副本,prototype.__proto__ = Father.prototype;
// prototype 继承constructor prototype.constructor 取的是原型链上,原型的Father.prototype.constructor, 为 Father();即:prototype.constructor == prototype.__proto__.constructor // true
// prototype.hasOwnPrototyoe("constructor") //false
prototype.constructor = son; // 弥补重写原型造成的默认属性的修改;
//此时是给prototype 添加了constructor 属性,赋值为son, 屏蔽了原型上的constructor属性
// prototype.hasOwnPrototype("constructor") // true
// prototype.constructor = son;
// prototype.__proto__.constructor = father
son.prototype = prototype;
// 给子类原型赋值为父类原型的副本;
}
//使用:
function Father(){
this.name="11";
this.age=12;
}
Father.prototype.sayName=function(){
alert(this.name);
}
function Son(){
Father.call(this);
this.age=23;
}
inherit(Son,Father);
Son.prototype.sayAge=function(){
alert(this.age)
}