欢迎使用CSDN-markdown编辑器

在javascript最初设计之时,c++大行其道,java也即将出世,面向对象编程思想盛行。因此最初的设计者Brendan决定设计一门面向对象的编程语言,但考虑到当时设计的主要目的是作为网景公司浏览器的表单验证,没有必要将其设计的十分正式,所以Brendan并不打算引入类的概念,但是如果不引入类的话,又如何实现继承呢?Brendan考虑到java和c++中使用new 类名的方法创建类,他借用了这个方法,使用new 构造函数的方法来表示原型,就像这样

function Person(name,age){
    this.name=name;
    this.age=age;
}
var person = new Person("harry",21);

但这样做有一个缺点,就是对象与对象之间不能实现数据的共享,如果我创建了1000个person,每个person都有一个相同的属性(比如说nationality都是chinese),那么每一个person都会有一个独立的nationality,而且值都是chinese,这对于内存资源是很大的浪费,而且如果说这1000个人都转了国籍,还要一个一个的去改它们的nationality属性值,非常麻烦,那该如何实现数据的共享呢,Brendan决定在每个构造函数中引入一个prototype属性,这个属性的值是一个对象,所有需要共享的属性和方法放入这个对象中就可以了,之后每当创建实例对象的时候,就会自动引用构造函数的prototype属性,也就是说每个实例对象中的属性一部分是本地自由的,还有一部分是从构造函数的prototype中共享的,这样如果需要修改共有的属性,只需要从构造函数的prototype中修改就会应用到所有的实例对象,例如

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype = {nationality: "chinese"};
var person1 = new Person("harry",21);
var person2 = new Person("sam",22);
alert(person1.nationality);//chinese
alert(person2.nationality);//chinese
Person.prototype.nationality = "china";
alert(person2.nationality);//china

由于javascript中并没有引入类的概念,因此它并不算是一门真正的面向对象的编程语言,可以将它理解为一门基于对象的编程语言。
那么javascript中如果创建对象呢?
一,采用对象直接量的方式,例如

var person1 = {};
person1.name = "harry";
person1.age = 21;
var person2 = {};
person2.name = "sam";
person2.age = 22;
...

采用这种方法的话每生成一个新的对象,都需要这么几个步骤,一方面比较麻烦(比如说我要生成100个对象呢?),另一方面,我们不知道实例和原型对象之间是什么关系(换句话说:person1和person2是同一个原型对象吗?)。
如果我们把生成对象的步骤放在一起的话,可以构建一个函数,每次建立对象的时候只需要调用这个函数就可以了。

function person(name,age){
    return {
        name: name,
        age: age
    }
}
var person1 = person("harry",21);
var person2 = person("sam",22);

这种方法简化了对象的创建过程,但是依然看不出person1和person2是否继承自同一个原型
不过至少这给了我们思路,想要方便的创建对象,我们需要用函数创建,一个函数能创建一类对象,那么我们能否通过这个函数给我们一些原型的信息呢?
为了解决原型和实例的关系,javascript提供了一个用构造函数来创建对象的方法
例如

function Person(name,age){
    this.name = name;
    this.age = age;
}

之后就可以通过构造函数Person() 来生成对象了

var person1 = new Person("harry",21);
var person2 = new Person("sam",22);

person1和person2默认含有一个constructor属性指向它们的构造函数
每次调用构造函数都会创建一个新对象,每个对象都会拥有一堆各自的属性,即便这些属性可能是相同的,例如:

    function Person(name,age){
        this.name = name;
        this.age = age;
        this.kind= "哺乳动物";
    }
    var person1 = new Person("harry",21);
    var person2 = new Person("sam",22);
    ... ...

每个对象都会拥有一个type属性,尽管我们知道它们是相同的。这对于内存是很大的浪费,同时修改起来也很麻烦,联想到之前我们用到的原型prototype,我们仍然可以将共有的属性放在原型中。
javascript规定,每一个构造函数都拥有一个prototype属性,这个属性指向另一个对象,通过这个构造函数创建的实例对象都会从构造函数的prototype指向的这个对象继承属性和方法。
例如:

function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.kind = "哺乳动物";
var person1 = new Person("harry",21);
var person2 = new Person("sam",22);
alert(person1.kind);//哺乳动物
alert(person2.kind);//哺乳动物

———————————————————–

接下来是用javascript来模拟java等真正的面向对象语言中的继承
下面有两个构造函数:Animal()和Cat();

    function Animal(){
        this.species = "animal";
    }
    function Cat(name,sex){
        this.name = name;
        this.sex = sex;
    }

现在的问题是如何让Cat()继承自Animal()呢?
1.在子类中调用父类的构造函数

    function Cat(name,sex){
        Animal.apply(this,arguments);
        this.name = name;
        this.sex = sex;
    }
    var cat1 = new Cat("Tom","male");
    alert(cat1.species);//animal

2.使用prototype属性,让cat.prototype指向animal的一个实例

                        Cat.prototype = new Animal();//
    Cat.prototype.constructor = Cat;/**/
    var cat1 = new Cat("Tom","male");
    alert(cat1.species);//animal
将Cat.prototype对象指向一个Animal的实例,相当于删除了Cat()构造函数之前的prototype,用animal这个实例来替代了,
每一个prototype对象都有一个constructor指向其构造函数,在默认情况下Cat.prototype.constructor = Cat,但是由于删除了之前的prototype,现在这个prototype指向animal的一个实例,(考虑到每个实例也有一个constructor,默认调用其prototype.constructor,),所以如果没有这一行,那么Cat.prototype.constructor==Animal&&cat1.constructor == Animal,这会导致继承链的混乱,因为cat1是调用Cat()构造函数生成的。所以必须手动纠正,所以就有了这一行。
这一点在编程时要格外注意,如果替换了prototype,那么一定要重新指定prototype.constructor
o.prototype = {};
o.prototype.constructor = o;

3.考虑到animal实例中不变的属性都放入了Animal.prototype中,所以我们可以直接从Animal.prototype中继承,例如:
先对Animal进行改写:

function Animal(){};
Animal.prototype.species = "animal";
    Cat.prototype = Animal.prototype;
    Cat.prototype.constructor = Cat;
    var cat1 = new Cat("Tom",21);
    alert(cat1.species);//animal
    alert(Animal.prototype.constructor);//Cat

这种方法的有点是效率高,节省内存,缺点是由于子类的原型指向了父类的原型,那么子类原型所做的任何修改,父类的原型也会受到影响。例如上面,第二行,修改了子类原型的constructor,此时父类的原型的constructor也变为了Cat,
4,可以用一个空对象来做中继

var F = function (){    };
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

F是空对象,几乎不占内存,而且对Cat.prototype的修改只会影响到F的实例,对Animal和Animal.prototype没有任何影响
5.之前采用的都是原型继承,除此之外我们还可以采用拷贝继承的方式(把父对象的所有属性和方法拷贝进子对象)

还是先将Animal的不变的属性放进prototype

    function Animal(){};
    Animal.prototype.species = "animal";

然后再写一个函数实现属性拷贝的目的

    /**
     将父对象的原型中的属性都拷贝进子对象的原型
     */
    function extend2(Child,Parent){
        var c = Child.prototype;
        var p = Parent.prototype;
        for(var i in p){
            c[i] = p[i];
        }
    }

使用的时候

    extend2(Cat,Animal);
    var cat1 = new Cat("Tom","male");
    alert(cat1.species);

采用非构造函数的继承
下面有两个普通对象:

var person = {
    planet: "earth"
}
var chinese = {
    nationality: "chinese"
}

注意到这两个对象都不是构造函数,那么如何让chinese继承person呢
1,使用object()方法

function object(o){
    function F(){};
    F.prototype = o;
    return new F();
}

这个object只是让返回对象的原型指向了参数对象
使用的时候

var chinese = object(person);
chinese.nationality = "chinese";
alert(chinese.planet);//earth

2.浅拷贝:将父对象的属性拷贝入子对象

    function extendCopy(p){
        var c = {};
        for(var i in p){
            c[i] = p[i];
        }
        return c;
    }

使用的时候,这样写:

    var chinese = extendCopy(person);
    chinese.nationality = "chinese";
    alert(chinese.planet);

但是这样的拷贝,如果person中有一个属性是对象类型,那么这里的赋值就是引用赋值,所以如果在子对象中改变了这个属性,也会影响到父对象。
例如:

    person.birthPlace = ['北京','天津','西安'];
    var chinese = extendCopy(person);
    chinese.birthPlace.push("杭州");
    alert(person.birthPlace);//['北京','天津','西安','杭州']

深拷贝,就是真正的能实现数组和对象的拷贝,它的实现可以递归调用之前的浅拷贝

function deepCopy(p,c){
    var c = c || {};
    for(var i in p){
        if(typeof p[i] == 'object'){
            c[i]=(p[i].constructor === "Array")? [] : {};
            deepCopy(p[i],c[i]);
        }else {
            c[i] = p[i];
        }
    }
    return c;
}

使用方式是:

    person.birthPlace = ['北京','天津','西安']
    var chinese = deepCopy(person);
    chinese.birthPlace.push("杭州");
    alert(person.birthPlace);
    alert(chinese.birthPlace);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值