Js创建对象及继承

1.创建对象 (通过字面量或者Object构造函数)

var person = {}; or new Object();
person.age = 12;
person.say = function() {
    alert(this.age)
}

缺点:使用同一个接口创建很多对象,会产生大量重复代码

2.工厂模式 (通过函数来封装特定接口创建对象)

function creatPerson(age) {
    var o = new Object();
    o.age = age;
    o.say = function() {
        alert(this.age);
    }
    return o;
}
var person1 = createPerson(23);
var person2 = createPerson(21);

缺点:虽然解决了创建多个相似对象问题,但却没有解决对象识别问题(即怎样知道一个对象的类型)

3.构造函数模式 (创建自定义构造函数,从而定义自定义对象类型属性和方法)

function Person(age) {
    this.age = age;
    this.say = function() {
        alert(this.age);
    }
}     
var person1 = new Person(23);
var person2 = new Person(21);

function say() { alert(this.age); }  //  全局函数

缺点:不同实例上的同名函数不相等。person1.say == person2.say // false
将this.say改成指针指向全局函数say()。这样就不用创建不同Function实例,但是say()是全局函数,没有封装性可言。

4.原型模式(通过原型对象共享属性和方法)

function Person() {
}
Person.prototype.age = 23;
Person.prototype.say = function() {
    alert(this.age)
}

var person1 = new Person();
var person2 = new Person();

person1.say == person2.say   // true

创建函数,自动拥有prototype属性,这个属性指向函数的原型对象。
实例与构造函数的原型对象是有关系的,与构造函数没关系。

person1.age = 1;
person1.age // 1
person2.age // 23

person1先访问实例属性,再访问原型属性。通过delete person1.age 删除实例属性, 随后即可访问原型属性。

判断实例是否拥有属性
“age” in person1

判断是否实例属性
person1 hasOwnProperty(“name”)

更简单的原型语法

 function Person() {
 }
 Person.prototype = {
     age: 23,
     say: function() {
         alert(this.age);
     }
 }

Person.prototype.name  =>  person1.constructor = Person 
Person.prototype =  {}    =>  字面量赋值,改写了person1.constructor = Object
Person.prototype = {
     constructor:  Person,
     age: 23
     ...
}

原生对象的原型:
Array.prototype.sort

Person.prototype.friend = [‘tony’]
person1.friends.push(‘van’) // tony, van
person2.friends // tony,van

缺点:省略了为构造函数传递初始化参数这一环节,所有实例在默认情况下将取得相同的属性值。

5.构造函数+原型模式

function Person(age) {
    this.age = age;
    this.friend = ['tony']
}

Person.prototype = {
    constrostor: Person,
    say: function() {
        alert(this.name);
    }
}

var person1 = new Person(1);
var Person2 = new Person(2);

person1.friends.push("van");

person1.friends  // tony, van
person2.friends  // tony

person1.say === person2.say // true

6.动态原型模式(由于开发人员看到独立的构造函数和原型时困惑,所以把所有信息都封装在构造函数中)

function Person(age) {
    this.age = age;
    if( typeof this.say != 'function') {
        Person.prototype.say = function() {
            alert(this.age);
        }
    }
}  

使用动态原型模型时,不能使用对象字面量重写原型。

7.寄生构造函数模式

与工厂模式相似,只是通过new 构造函数不一样。
构造函数默认返回新对象实例,通过在构造函数末尾添加return语句,重写调用构造函数时返回的值。

8. 稳妥构造函数模式 (不使用this,new)

========================

继承

1.原型链继承

function SuperType() {
    this.colors = ['red']
} 

function SubType() {}

SubType.prototype = new SuperType()  // 继承了SuperType

var instance1 = new SubType();
instance1.colors.push('blue');
instance1.colors  // red, blue

var instance2 = new SubType();
instance2.colors // red, blue

问题: SuperType每个实例都有各自colors属性,SubType所有实例共享同一个colors属性。在创建子类型的实例时,不能向超类型的构造函数传递参数。

2.借用构造函数

function SuperType(name) {
    this.colors = ['red'];
    this.name = name;
} 

function SubType() {
    SuperType.call(this,'wu');  // 继承了SuperType
}

在子类型构造函数内部调用超类型构造函数。(call(), apply()执行构造函数)

问题: 方法都在构造函数中定义,因此无法函数复用。(在超类型的原型中定义的方法,对子类型而言不可见)

3.组合继承(原型链+借用构造函数)

function SuperType(name) {
    this.colors = ['red'];
    this.name = name;
} 

SuperType.prototype.say = function() {
    alert(this.name);
}

function SubType() {
    SuperType.call(this,'wu');  // 继承属性
}

SubType.prototype = new SuperType(); // 继承方法

此处原型链也继承了属性

var instance1 = new SubType(‘wu’);

instance1.name = wu // 实例name
delete instance1.name
instance1.name = wu // 原型对象name
“name” in instance1 // true
instance1.hasOwnProperty(‘name’) // false

4.原型式继承

5.寄生式继承

6.寄生组合式继承