一. 创建对象
1. 工厂模式
解决了创建多个类似对象的问题,但是通过工厂模式创建的对象无法识别是一个什么样的对象类型
function objFactory(name,age){
var o = new Object(); //适合于原生构造函数创建实例
o.name=name;
o.age=age;
o.getAction=function(){console.log(this.name)};
return o;
}
var person1=objFactory("jasson",10);
var person2=objFactory("jim",8);
2. 构造函数模式
//自定义一类特性的类型
function Animal(name,age){
this,name=name;
this.age=age;
this.getAction=function(){
console.log(this.name);
}
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
构造函数可以用于创建特定类型的对象。除了Object、Array原生构造函数,还可以自定义构造函数。自定义构造函数可以用于将它的实例标识为一种特定的类型,如Dog、Cat。
问题:每个方法都会在每个实例上创建一遍。在ECMAscript中函数是对象,每定义一个函数相当于实例化了一个Function对象
即 this.getAction=function(){……}
逻辑上等价于 this.getAction=new Function(){……}
所以不同实例上的同名函数是不相等的如下:
console.log(dog.getAction==cat.getAction);
VM649:1 false
但是,创建两个可以完成同样任务的Function实例是没有必要的。大可将函数移到构造函数体外去,如下:
function Animal(name,age){
this,name=name;
this.age=age;
//此时this.getAction属性设为等于全局函数getAction,而getAction是一个指向函数的指针,因此解决了两个函数做同一件事的问题。
this.getAction=getAction;
}
function getAction(){
console.log(this.name);
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
问题:如果对象需要定义很多方法,那么会存在很多全局函数,那么自定义引用类型的封装性无法得以体现了。这一点可以用原型模式解决。
3. 原型模式
可以让所有对象实例共享它所包含的属性和方法。
function Animal(){
}
Animal.prototype.name="dog";
Animal.prototype.age=2;
**Animal.prototype.getAction=function(){
console.log(this.name);
}**
var dog = new Animal();
var cat=new Animal();
4. 组合构造模式
构造函数模式+原型模式的方法
使实例可以拥有各自的实例属性,而原型模式可以用于定义共同的属性和方法。
function Animal(name,age){
this,name=name;
this.age=age;
}
*Animal.prototype.getAction=function(){
console.log(this.name);
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
5. 动态原型模式
将所有信息封装在构造函数中,在必要情况下初始化原型。
function Animal(name,age){
this,name=name;
this.age=age;
**if(typeof this.getAction != "function"){
Animal.prototype.getAction=function(){
console.log(this.name);
}
}**
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
dog.getAction();
6. 寄生构造函数
function Animal(name,age){ //寄生的宿主构造函数
var o= new Object(); //寄生者
o.name=name;
o.age=age;
o.getAction=function(){console.log(this.name)};
return o;
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
二. 继承
实例、构造函数、实例的关系
1. 原型链继承
定义一个父类Animal
function Animal(){
this.name="animal";
}
Animal.prototype.getName=function(){return this.name};
//定义子类
function Dog(){
this.age=2;
}
Dog.prototype = new Animal(); //原型继承
var instance = new Dog(); //子类的实例
alert(instance.getName()); //输出为animal,继承了父类的方法
console.log(instance.constructor==Animal); //VM2342:1 true,
使用原型继承后,instance.constructor会指向父类Animal,因为Dog.prototype指向了Animal的原型,所以constructor指向了animal。
问题:1.创建子类型的实例时,无法在不影响其他对象实例的情况下向超类型的构造函数传递参数。2.无法实现多继承
特点:1.简单易于实现
2. 借用构造函数继承
function Animal(){
this.names=["animal"];
}
function Dog(){
**Animal.call(this);** // 借调了父类的构造函数
}
//每当创建一个实例时,都会利用call方法借调animal的构造函数,在实例对象上会执行animal构造函数中定义的初始化代码,因此Dog的每个实例上都会有names属性的副本。所以instance1和instance1的names打印出来结果会不一样
var instance1 = new Dog();
instance1.names.push("dog");
console.log(instance1.names); //(2) ["animal", "dog"]
var instance2 = new Dog();
console.log(instance2.names); //VM2406:12 ["animal"]
借用构造函数的另一点----子类型构造函数可以向父类构造函数传递参数
function Animal(name){
this.names=name;
}
function Dog(){
**Animal.call(this,"dog");** // 借调了父类的构造函数同时传递参数
this.age=2;
}
var instance = new Dog();
console.log(instance.name); //VM2406:12 "dog"
问题:1.函数无法复用。因此需要用组合继承 。 2.只能继承父类的实例和方法,无法继承原型属性和方法
特点:1.可以实现多继承。2.创建子类实例时可以向父类传参
3. 组合继承
借用构造函数继承+原型继承
function Animal(name){
this.name=name;
this.colors=["red","blue","green"];
}
Animal.prototype.getName=function(){
console.log(this.name);
}
function Dog(name,age){
Animal.call(this,name); //**第二次调用超类animal的构造函数**
this.age=age;
}
Dog.prototype=new Animal(); //**第一次调用超类animal构造函数**
Dog.prototype.constructor=Dog; //使得dog.prototype.constructor仍然指向dog
Dog.prototype.getAge=function(){console.log(this.age);};
var instance1 = new Dog("JIM",2);
instance1.colors.push("yellow");
console.log(instance1.colors); //(4) ["red", "blue", "green", "yellow"]
instance1.getName(); // JIM
instance1.getAge(); //2
var instance2 = new Dog("tom",11);
console.log(instance2.colors); //VM2911: (3) ["red", "blue", "green"]
instance2.getName(); // tom
instance2.getAge(); //11
如果注释掉Dog.prototype.constructor=Dog; 这句,那么
console.log(Dog.prototype.constructorDog);.//false
console.log(Dog.prototype.constructorAnimal); //true
问题:1.无论什么情况下,都会调用两次超类的构造函数,导致有两组超类中的属性name和colors,一组在实例上,一组在Dog原型中。------解决方案为寄生组合式继承
特点:1.可以继承父类的属性方法,也可以继承原型属性方法。2.可以传参 。 3.函数可以复用
4. 寄生式继承
利用createAnimal()函数封装继承过程,类似于工厂模式和寄生构造函数的思维。
function createAnimal(original){
var o=new Object(original); //original初始化的参数
o.getName=function(){console.log(original.name);};
return o;
}
var animal={
name: "JIm",
colors: ["red","bluer"]
};
var obj = createAnimal(animal); //利用函数封装继承过程,返回的结果是个对象
obj.getName(); //JIM
5. 寄生组合式继承
寄生式继承+组合式继承
通过借用构造函数继承属性,利用原型链的混成形式继承方法。
//借用构造函数方法
function Animal(name){
this.name=name;
this.colors=["red","blue","green"];
}
Animal.prototype.getName=function(){
console.log(this.name);
}
function Dog(name,age){
Animal.call(this,name); //**第二次调用超类animal的构造函数**
this.age=age;
}
Dog.prototype.getAge=function(){
console.log(this.age);
};
//寄生组合继承的重点!!!!!
function inheritPrototype(dog, animal){
var o = new Object(animal.prototype);
o.constructor = dog;
dog.prototype = o;
}
inheritPrototype(Dog,Animal);
var instance1 = new Dog("JIM",2);
console.log(instance1.colors); // ["red", "blue", "green"]
instance1.getName(); // JIM
instance1.getAge(); //2
重点部分!!!
function inheritPrototype(dog, animal){
var o = new Object(animal.prototype);
o.constructor = dog;
dog.prototype = o;
}
inheritPrototype(Dog,Animal);
问题:1. 实现较为复杂
特点: 1.整体实现较为理想