说清楚javascript面向对象、原型、继承

27次阅读

共计 6683 个字符,预计需要花费 17 分钟才能阅读完成。

这是两年前写的笔记,自己都有遗忘,相信也会有人不明白,贴在这里。都是红宝书上的内容,在比较难理解的地方加了一些示例和说明。
es 中定义对象为:无需属性的集合、其属性可以包含基本值、对象或者函数。
创建对象
Object 实例
创建对象最简单的方法是创建一个 Object 的实例。
var person =new Object();
person.name=”Mike”;
person.age=12;
person.job=”student”;
person.sayName=function(){
};
对象字面量
var person={
name:”mike”,
age:10,
job:”student”,
sayName:function(){
alert(this.name);
}
};
Object 实例和对象字面量都可以用来创建单个对象,但是当要创建多个对象就会产生大量重复的代码,由此产生了工厂模式。
工厂模式
工厂模式的特点是用函数来封装创建对象的细节,对外以接口的方式调用。
function createPerson(name,age,job){
var obj = new Object();
obj.name=name;
obj.age=age;
obj.job=job;
obj.sayName=function(){
alert(this.name);
}
return obj;
}
var person1=createPerson(“Mike”,12,”student”);
工厂模式虽然解决了创建相似对象重复代码的问题,但同时还存在一个问题:如何知道对象的类别?由此产生构造函数模式
构造函数模式
function CreatePerson(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
}
}
var person1= new CreatePerson(“Mi”,13,”student”);
var person2= new CreatePerson(“Mike”,12,”student”);
alert(person2.constructor==CreatePerson);//true
alert(person1 instanceof CreatePerson);//true
alert(person2 instanceof CreatePerson);//true
alert(person2 instanceof Object);//true 使用 instanceof 来检测对象的类型

构造函数没有返回值,没有显示地创建对象。使用 new 调用构造函数,将构造函数的作用域赋给新对象(this 指向新对象)构造函数模式的特点:使用构造函数创建的对象(实例)有特定的类型。这也是构造函数模式胜过工厂模式的地方构造函数模式的问题:构造函数中方法,在不同的实例中是不同的 funciton 实例,也就是说不同实例中有不同 function(虽然他们的功能相同),但是这并不是我们希望看到的,更何况 function 中还有 this(指向实例的作用域),所以完全没有必要把功能相同 function 绑定到特定的实例上。由此产生了原型模式!大 boss 来了,接着说下去
原型模式
关于原型的定义,摘自红宝书:每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。好长一句话!有点晕?直接上代码:
function Person(){
}
Person.prototype.name=”mike”;
Person.prototype.age=12;
Person.prototype.job=”Software Engneer”;
Person.prototype.sayName=function(){
alert(this.name);
}

var person1=new Person();
alert(Person1.prototype.isPrototypeOf(person5));//true 检验实例与原型之间的关系
person1.sayName();//mike
var person2=new Person();
alert(person1.sayName()==person2.sayName());//true

var person6=new Person();
person6.name=”ssss”;
alert(person6.hasOwnProperty(“ssss”));//true

原型链查找属性或方法:
首先查找实例属性:即定义在子类构造函数中或直接添加在实例上的属性然后查找子类原型属性:包括子类自有的原型属性和,父类的实例属性(子类的原型是父类的实例)(这也就是原型链继承的缺点,父类的实例属性全变成子类的原型属性,那就是所有子类实例共享的,如果又是引用类型,就出大事了。所以很少单独使用原型链)最后查找父类的原型属性
function SuperType(){
this.name=”super”;
this.job=”student”;
}
SuperType.prototype.getSuperValue=function(){
alert(this.name);
}
SuperType.prototype.sex=”man”;
function SubType(){
this.age=12;
}
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
alert(this.age);
}
//var person=new SubType();
var instance1=new SubType
alert(instance1 instanceof SubType);//true
alert(instance1 instanceof SuperType);//true
alert(SubType.prototype.isPrototypeOf(instance1));//true
alert(SuperType.prototype.isPrototypeOf(instance1));//true
person.getSuperValue();
alert(person.constructor==SubType);//false 子类的实例的原型对象没有 constructor 属性
alert(person instanceof SubType);
alert(person instanceof SuperType);
alert(person.job);
alert(person.constructor);//sub 的原型指向 super 的原型,super 的原型的 constuctor 是 superType,所以 person 的
alert(person.hasOwnProperty(“name”));//false
alert(“name” in person);//true 说明 name 是 sub 的原型属性。
alert(person.hasOwnProperty(“sex”));//false
alert(“sex” in person);//true 说明 name 是 sub 的原型属性。, 父类中的全部属性都是子类的原型属性
var person1=new SuperType();;
alert(person1.constructor);//

解决原型链缺点的方法:借用构造函数
借用构造函数
function SuperType(name){
this.colors=[“red”,”yellow”,”green”];
this.name=name;
}
function SubType(){
SuperType.call(this,”Mike”);
this.age=10;
}
var instance1=new SubType();
instance1.colors.push(“aa”);
alert(instance1.colors);
alert(instance1 instanceof SubType);//true
alert(instance1 instanceof SuperType);//false
alert(SubType.prototype.isPrototypeOf(instance1));//true
alert(SuperType.prototype.isPrototypeOf(instance1));//false
var instance2=new SubType();
alert(instance2.colors);//[“red”,”yellow”,”green”];
在新实例的环境中调用 Super 构造函数
var instance3=new SubType();
alert(instance3.hasOwnProperty(“name”));
alert(instance3.hasOwnProperty(“age”));
在子类中 call(借调)父类的构造函数实际上就是为子类添加了实例属性(原来在父类中的实例属性)即父类的实例属性变成了子类的实例属性(原型链继承中,父类的实例属性变成了子类的原型属性)
组合继承
function SuperType(name){
this.colors=[“red”,”yellow”,”green”];
this.name=name;
}
SuperType.prototype.sayName=function(){
alert(this.name);
}

function SubType(name,age){
SuperType.call(this,”Kiko”);
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
alert(this.age);
}
var instance1=new SubType(“Kiko”,12);
//instance1.sayName();
//instance1.colors.push(“ss”);
//alert(“colors” in instance1);// 有属性
//alert(instance1.hasOwnProperty(“colors”))
// 实例属性 没有这句话的时候 //SuperType.call(this,”Kiko”);,,是原型属性
// 有了这句话变成了实例属性
//alert(instance1.colors);
//instance1.sayAge();
alert(instance1 instanceof SubType);//true
alert(instance1 instanceof SuperType);//true
alert(SubType.prototype.isPrototypeOf(instance1));//true
alert(SuperType.prototype.isPrototypeOf(instance1));//true
alert(Object.keys(SubType.prototype));//colors,name,constructor,sayAge
alert(Object.keys(instance1));//colors,name,age
这两句说明第一:SubType.prototype=new SuperType();父类的所有属性都变成了子类的原型属性第二:SuperType.call(this,”Kiko”);,在子类的构造函数中重写了父类的实例属性,即父类的实例属性变成了子类的实例属性第三:p149: 每当代码读取对象中的某个属性时,都会执行一次搜索,先查找实例属性,再查找原型属性第四:因此,组合继承中父类中的实例属性既是子类的实例属性也是子类的原型属性,但是在每次使用对象时,查找到实例属性就截止了,所以表现为父类中的实例属性变成了子类中的实例属性(实例属性覆盖了原型对象上的同名属性)
var instance2=new SubType(“hihi”,13);//alert(instance2.colors);
原型式继承
function object(o){
function F(){}
F.prototype=o;
return new F();
}

var person={
name:”Mike”,
friends:[“kiko”,”Court”,”Van”]
};
var anotherPerson=object(person);
alert(anotherPerson.name);//
anotherPerson.friends.push(“ss”);

var yetAnotherPerson=object(person);
alert(yetAnotherPerson.friends);//[“kiko”,”Court”,”Van”,”ss”]

寄生式继承
function createAnother(original){
var clone=object(original);
clone.sayHi=function(){
alert(“hi”);
}
return clone;
}
var anotherPerson1=createAnother(person);
anotherPerson.sayHi();//hi
使用寄生式继承来为对象添加函数,会由于不能复用而降低效率,这一点与构造函数模式很类似(同名的方法是不同的 function 实例)
寄生组合式继承
组合式的缺点是两次调用超类的构造函数:一,SubType.prototype=new SuperType();父类的所有属性都变成了子类的原型属性二:SuperType.call(this,”Kiko”);,在子类的构造函数中重写了父类的实例属性,即父类的实例属性变成了子类的实例属性寄生组合式目的是为了省掉第一步多继承的实例属性,在第一步时只继承父类的原型属性,第二步继承实例属性
function object(o){
function F(){}
F.prototype=o;
return new F();
}

function inheritPrototype(SubType,SuperType){
var prototype=object(SuperType.prototype);
prototype.constructor=SubType;
SubType.prototype=prototype;
}
function SuperType(name){
this.name=name;
this.colors=[“red”,”blue”,”yellow”];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
SuperType.prototype.job=”student”;

function SubType(name,age){
SuperType.call(this,name);//(2)继承实例属性
this.age=age;
}
inheritPrototype(SubType,SuperType);//(3)继承原型属性
SubType.prototype=new SuperType();//(1)把父类的原型属性和实例属性都继承为原型属性了
SubType.prototype.sayAge=function(){
alert(this.age);
}
var instance1=new SubType();
alert(“name” in instance1);// 有属性 true
alert(instance1.hasOwnProperty(“name”));//false 不是实例属性
这两句话检验是否是原型属性,1true,2false, 原型属性,1ture,2true 实例属性寄生组合式,name 会是实例属性组合式,把 (3) 换成 (1)name 确实也是实例属性,那是因为(2) 重新调用了 SuperType,覆盖了原型中的同名属性。可以把 (2) 去掉,发现 name 变成了原型属性。

正文完
 0