javascript中创建对象和实现继承

# oo

##创建对象

1. 原型、构造函数、实例之间的关系

 * 原型的construct->构造函数;调用isPrototypeOf(obj)方法可以判定和实例的关系;
 * 构造函数的prototype->原型;
 * 实例的__proto__ ->原型(仅在chrome,safari,Firefox中存在,而共有的是[[prototype]]);用getPrototypeOf()方法可以返回[[prototype]]的值;
 * 构造函数后构造函数的‘子类’new产生一个实例,可以用instanceOf 操作符来检测构造函数是否出现在原型链上。

2. 说明:

 * 一个函数在声明之后就会有prototype对象,对象的constructor属性所存放的指针指向该函数。
 * 对原型进行赋值,就让这个prototype中的指针指向了一个新的对象,里面的constructor属性自然会丢失。如果把父类的值赋给了它,那么这个constructor就是父类实例的值。父类实例并没有这个属性,于是继承父类的原型中的constructor。所以,子类构造函数的(this.constructor)的值通过这样的步骤获得:
 
    子类实例(没有)--子类原型 (没有)--父类原型(有,为父类构造函数)

3. new关键字的理解

     new的作用就是新建一个作用域,将this指向该作用域,然后执行函数中的代码。不用new关键字,那么this将指向globle对象,在浏览器中是window对象。

4. 检测对象和属性的关系
 
(1)

 * in操作符 能够访问该属性
 * hasOwnProperty() 存在实例中
 * !obj.hasOwnProperty() && in 存在原型中

(2)

 * for-in循环 枚举出 包含实例中的属性和原型中 可枚举的属性;
 * Object.keys(obj) 返回一个数组 可枚举的实例属性;
 * object.getOwnPropertyNames(obj) 得到所有实例属性,无论是否可以枚举;




##实现继承

1. 原型链继承

2. 借用构造函数(又叫伪造对象,对象冒充法,经典继承等)

3. 组合继承(又叫伪经典继承)

        function Anm(name,age) {
            this.name = name;
            this.age = age;
        }
        Anm.prototype.sayBye = function () {
            console.log(this.name + ' say bye to you!');
        };
        function Dog(food, name, age) {
            //从父类中,通过call或apply方法继承属性
            Anm.call(this, name, age);
            this.food = food;
        }
        //子类通过设置原型等于父类的一个实例的方式,继承方法
        Dog.prototype = new Anm();
        Dog.prototype.constructor = Dog;
        Dog.prototype.sayHello = function() {
            console.log(this.name + ' say hello to me!')
        };
        dog1 = new Dog('shit', '小黄狗', 15);
        console.log(dog1);    //{name: "小黄狗", age: 15, food: "shit", constructor: function, sayHello: function…}
        dog1.sayBye();    //小黄狗 say bye to you!
        dog1.sayHello();    //小黄狗 say hello to me!

    这种继承,能够让子类产生的实例拥有自己的属性,又可以从父类的原型上面继承得到公用的方法。所以成为javascript中最常用的继承模式。


4. 原型式继承

        function Anm(klass) {
            this.klass = klass;
        }
        var dog1 = Object.create(gou, {
            name: {
                configurable: true,
                enumerable: true,
                writable: true,
                value: '小黄'
            },
            birthyear: {
                value: 2012
            },
            age: {
                get: function () {
                    var thisTime = new Date();
                    return thisTime.getFullYear() - this.birthyear;
                }
            }
        });
        console.log('修改前的对象');
        console.log(dog1);  //Object {name: "小黄", birthyear: 2012, age: 3, klass: "狗狗"}
        dog1.name = '小李';
        dog1.birthyear = 2010;
        console.log('修改后的对象');  //Object {name: "xiaoli", birthyear: 2012, age: 3, klass: "狗狗"}
        console.log(dog1);

    Object.create()方法接受两个参数,第一个是作为原型的对象,第二个是为新队想定义额外属性的对象,以JSON格式,每一个要新增的属性都是该对象中一个属性,它的值也是一个描述符对象。

    属性有数据属性(如上面的name和birthyear)和访问器属性(如上面的age),如果只写入一个特性,其余特性的值将会被设置为false。所以上面的birthyear是无法改动的。
    
    这种方式可以在指向让一个对象与另一个对象保持类似的情况下使用,不用去进行构造函数的创建。

5. 寄生式继承

    上面的Object.create()方法额可以只创第一个参数,然后在对返回的函数添加属性,再把这整个过程封装起来,这种方式叫作寄生式继承。
    
6. 寄生组合式继承

    子类的原型在Dog.prototype = new Anm()过程中,为它添加了Anm中属性klass(值为undifiend),同时Dog构造函数中,同样为实例添加了klass,相当于在原型和实例中都创造了属性。如果属性很多的话,这是一种浪费。如果能够不让子类的原型产生这些属性,直接让他的[[prototype]]指向父类的原型的话,那么就能够继承父类的方法了。
    我们先封装一个方法:

        function inheriyPrototype(subType, superType) {
            var newPrototype = Object.create(superType.prototype);
            subType.prototype = newPrototype;
            newPrototype.constructor = subType; //重写了subType.prototype后丢失了自己的constructor,这里进行补写
        }

    这个方法让子类不通过父类的实例赋值,而是让它的[[prototype]]直接指向父类的实例。
    
    实际上,这个子类的原型是一个空函数创建的,这个空函数的原型等于父类的原型。所以就让这个空函数的实例只有一个[[prototype]],再把这个实例赋值给了子类的原型,加上constructor属性。就实现了不创造多余属性的目的。

    接下来:

        function Anm(klass) {
            this.klass = klass;
        }
        Anm.prototype.sayKlass = function () {
            console.log(this.klass);
        };
        function Dog(name, klass) {
            Anm.call(this, klass);
            this.name = name;
        }
        inheriyPrototype(Dog, Anm);
        var dog1 = new Dog('小黄', '犬科动物');
        console.log(dog1);  //Dog {klass: "犬科动物", name: "小黄", constructor: function, sayKlass: function}
        dog1.sayKlass();    //犬科动物
        console.log(Dog.prototype);     //Dog {constructor: function, sayKlass: function}
        console.log(Dog.prototype.isPrototypeOf(dog1));        //true
        console.log(Anm.prototype.isPrototypeOf(dog1));        //true

    现在很多程序员普遍认为寄生组合式是最理想的继承范式。在YUI中的一些方法上已经开始在使用了。 

posted on 2015-09-15 01:03  袖珍汤锅  阅读(262)  评论(0编辑  收藏  举报

导航