继承

作者: 巴拉巴啦 | 来源:发表于2018-07-22 16:50 被阅读3次

    假设在es5要实现继承,首先定义一个父类:

    //父类
    function sup(name) {
        this.name = name
    }
    //定义父类原型上的方法
    sup.prototype.printName = function (){
        console.log(this.name)
    }
    

    现在再定义他sup的子类,继承sup的属性和方法:

    function sub(name,age){
        sup.call(this,name)    //调用call方法,继承sup超类属性
        this.age = age
    }    
    
    sub.prototype = new sup   //把子类sub的原型对象指向父类的实例化对象,这样即可以继承父类sup原型对象上的属性和方法
    sub.prototype.constructor = sub    //这时会有个问题子类的constructor属性会指向sup,手动把constructor属性指向子类sub
    //这时就可以在父类的基础上添加属性和方法了
    sub.prototype.printAge = function (){
        console.log(this.age)
    }
    

    这时调用父类生成一个实例化对象:

        let jack = new sub('jack',20)
        jack.printName()    //输出 : jack
        jack.printAge()    //输出 : 20
    

    这就是es5中实现继承的方法。
    而在es6中实现继承:

        class sup {
            constructor(name) {
                this.name = name
            }
    
            printName() {
                console.log(this.name)
            }
        }
    
    class sub extends sup{
        constructor(name,age) {
            super(name) // super代表的事父类的构造函数
            this.age = age
        }
    
        printAge() {
            console.log(this.age)
        }
    }
    
    let jack = new sub('jack',20)
        jack.printName()    //输出 : jack
        jack.printAge()    //输出 : 20
    

    对比es5和es6可以发现在es5实现继承,在es5中实现继承:

    1. 首先得先调用函数的call方法把父类的属性给继承过来
    2. 通过new关键字继承父类原型的对象上的方法和属性
    3. 最后再通过手动指定constructor属性指向子类对象

    而在es6中实现继承,直接调用super(name),super是代替的是父类的构造函数,super(name)相当于sup.prototype.constructor.call(this, name).

    6 面向对象的程序世纪

    对象的属性:数据属性,访问器属性
    1.数据属性有4个描述其行为的特性
    [[configurable]] 能否通过delete删除属性 从而定义新的属性 默认值true
    [[enumberable ]]可枚举 通过for-in 默认值true
    [[writable]] 能否修改这个属性的数据值 默认值true
    [[value]] 这个属性的数据值

    要修改属性的默认的值要用过 Object.definedProperty()

    2 访问器属性
    不包含数据值,只包含一堆getter和setter函数(这两个函数不是必需的)
    有4个特性
    [[configurable]] 能否通过delete删除属性 从而定义新的属性 默认值true
    [[enumberable ]]可枚举 通过for-in 默认值true
    [[Get]] 读取属性时调用的函数 默认值为undifined
    [[Set]] 写入属性时调用的函数 默认值为undifined

    Object.definedProperty() 修改属性默认的特性 ,接受三个参数:属性所在的对象,属性的名字和一个描述符对象
    Object.definedProperties()通过描述符一次性定义多个属性

    读取属性的特性
    Object.getOwnPropertyDescriptor()可以取得给定属性的描述符,接受两个参数:属性所在的对象,要读取其描述符的属性名称。返回值是一个对象

    原型模式下构造函数、原型和实例的关系:
    每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型
    对象的内部指针

    1. 理解原型对象


      image.png

    person1.proto 的值是 Person.prototype

    展示了 Person 构造函数、Person 的原型属性以及 Person 现有的两个实例之间的关系。
    在此,Person.prototype 指向了原型对象,而 Person.prototype.constructor 又指回了 Person。
    原型对象中除了包含 constructor 属性之外,还包括后来添加的其他属性。Person 的每个实例——
    person1 和 person2 都包含一个内部属性,该属性仅仅指向了 Person.prototype;换句话说,它们
    与构造函数没有直接的关系
    虽然在所有实现中都无法访问到[[Prototype]],但可以通过 isPrototypeOf()方法来确定对象之
    间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调用 isPrototypeOf()方法的对象
    (Person.prototype),那么这个方法就返回 true,如下所示:
    alert(Person.prototype.isPrototypeOf(person1)); //true
    alert(Person.prototype.isPrototypeOf(person2)); //true
    ECMAScript 5 增加了一个新方法,叫 Object.getPrototypeOf(),在所有支持的实现中,这个
    方法返回[[Prototype]]的值。例如:
    alert(Object.getPrototypeOf(person1) == Person.prototype); //true

    function Person(){ 
    } 
    Person.prototype.name = "Nicholas"; 
    Person.prototype.age = 29; 
    Person.prototype.job = "Software Engineer"; 
    Person.prototype.sayName = function(){ 
     alert(this.name); 
    }; 
    var person1 = new Person(); 
    var person2 = new Person(); 
    alert(person1.hasOwnProperty("name")); //false 
    alert("name" in person1); //true 
    person1.name = "Greg"; 
    alert(person1.name); //"Greg" ——来自实例
    alert(person1.hasOwnProperty("name")); //true 
    alert("name" in person1); //true 
    alert(person2.name); //"Nicholas" ——来自原型
    alert(person2.hasOwnProperty("name")); //false 
    alert("name" in person2); //true 
    delete person1.name; 
    alert(person1.name); //"Nicholas" ——来自原型
    alert(person1.hasOwnProperty("name")); //false 
    alert("name" in person1); //true 
    

    在使用 for-in 循环时,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,其中
    既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性(即将
    [[Enumerable]]标记为 false 的属性)的实例属性也会在 for-in 循环中返回,因为根据规定,所
    有开发人员定义的属性都是可枚举的——只有在 IE8 及更早版本中例


    image.png image.png

    要取得对象上所有可枚举的实例属性,可以使用 ECMAScript 5 的 Object.keys()方法。这个方法
    接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组

    function Person(){ 
    } 
    var friend = new Person(); 
    
    Person.prototype = { 
    constructor: Person, 
    name : "Nicholas", 
    age : 29, 
    job : "Software Engineer", 
    sayName : function () { 
    alert(this.name); 
    } 
    }; 
    friend.sayName(); //error 
    

    在这个例子中,我们先创建了 Person 的一个实例,然后又重写了其原型对象。然后在调用
    friend.sayName()时发生了错误,因为 friend 指向的原型中不包含以该名字命名的属性


    image.png

    6.2.4 组合使用构造函数模式和原型模式
    6.2.5 动态原型模式
    6.2.6 寄生构造函数模式
    6.2.7 稳妥构造函数模式

    6.3 继承

    ?
    继承是 OO 语言中的一个最为人津津乐道的概念。许多 OO 语言都支持两种继承方式:接口继承和
    实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。如前所述,由于函数没有签名,
    在 ECMAScript 中无法实现接口继承。ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链
    来实现的。

    总结

    6.2 创建对象
    6.2.1 工厂模式

        function createPerson(name, age, job){ 
         var o = new Object(); 
         o.name = name; 
         o.age = age; 
         o.job = job; 
         o.sayName = function(){ 
         alert(this.name); 
         }; 
         return o; 
        } 
        var person1 = createPerson("Nicholas", 29, "Software Engineer"); 
        var person2 = createPerson("Greg", 27, "Doctor"); 
    

    6.2.2 构造函数模式

        function Person(name, age, job){ 
             this.name = name; 
             this.age = age; 
             this.job = job; 
             this.sayName = function(){ 
            alert(this.name); 
         }; 
        } 
        var person1 = new Person("Nicholas", 29, "Software Engineer"); 
        var person2 = new Person("Greg", 27, "Doctor"); 
    

    6.2.3 原型模式

        function Person(){ 
            } 
        Person.prototype.name = "Nicholas"; 
        Person.prototype.age = 29; 
        Person.prototype.job = "Software Engineer"; 
        Person.prototype.sayName = function(){ 
         alert(this.name); 
        }; 
        var person1 = new Person(); 
        person1.sayName(); //"Nicholas" 
        var person2 = new Person(); 
        person2.sayName(); //"Nicholas" 
        alert(person1.sayName == person2.sayName); //true 
    

    6.2.4 组合使用构造函数模式和原型模式

        function Person(name, age, job){ 
             this.name = name; 
             this.age = age; 
             this.job = job; 
             this.friends = ["Shelby", "Court"]; 
        } 
        Person.prototype = { 
         constructor : Person, 
         sayName : function(){ 
         alert(this.name); 
         } 
        } 
        var person1 = new Person("Nicholas", 29, "Software Engineer"); 
        var person2 = new Person("Greg", 27, "Doctor"); 
        person1.friends.push("Van"); 
        alert(person1.friends); //"Shelby,Count,Van" 
        alert(person2.friends); //"Shelby,Count" 
        alert(person1.friends === person2.friends); //false 
        alert(person1.sayName === person2.sayName); //true 
    

    6.2.5 动态原型模式

        function Person(name, age, job){ 
         //属性
         this.name = name; 
         this.age = age; 
         this.job = job; 
        //方法
         if (typeof this.sayName != "function"){ 
         
             Person.prototype.sayName = function(){ 
             alert(this.name); 
         }; 
         
         } 
        } 
        var friend = new Person("Nicholas", 29, "Software Engineer"); 
        friend.sayName(); 
    
        6.2.6 寄生构造函数模式
        function Person(name, age, job){ 
         var o = new Object(); 
         o.name = name; 
         o.age = age; 
         o.job = job; 
         o.sayName = function(){ 
         alert(this.name); 
         }; 
         return o; 
        } 
        var friend = new Person("Nicholas", 29, "Software Engineer"); 
        friend.sayName(); //"Nicholas" 
    

    6.2.7 稳妥构造函数模式

    function Person(name, age, job){ 
             //创建要返回的对象
             var o = new Object();
            //可以在这里定义私有变量和函数
             //添加方法
             o.sayName = function(){ 
             alert(name); 
         }; 
         
         //返回对象
         return o; 
        } 
    
    注意,在以这种模式创建的对象中,除了使用 sayName()方法之外,没有其他办法访问 name 的值。
    可以像下面使用稳妥的 Person 构造函数。
    var friend = Person("Nicholas", 29, "Software Engineer"); 
    friend.sayName(); //"Nicholas" 
    这样,变量 friend 中保存的是一个稳妥对象,而除了调用 sayName()方法外,没有别的方式可
    以访问其数据成员。即使有其他代码会给这个对象添加方法或数据成员,但也不可能有别的办法访问传
    入到构造函数中的原始数据。稳妥构造函数模式提供的这种安全性,使得它非常适合在某些安全执行环
    境——例如,ADsafe(www.adsafe.org)和 Caja(http://code.google.com/p/google-caja/)提供的环境下使用。
    

    6.3 继承

        6.3.1原型链继承
        function SuperType(){ 
         this.colors = ["red", "blue", "green"]; 
        } 
        function SubType(){ 
        } 
        //继承了 SuperType 
        SubType.prototype = new SuperType(); 
        var instance1 = new SubType(); 
        instance1.colors.push("black"); 
        alert(instance1.colors); //"red,blue,green,black" 
        var instance2 = new SubType(); 
        alert(instance2.colors); //"red,blue,green,black" 
    
        6.3.2 借用构造函数
        function SuperType(){ 
         this.colors = ["red", "blue", "green"]; 
        } 
        function SubType(){ 
         //继承了 SuperType 
         SuperType.call(this); 
        } 
        var instance1 = new SubType(); 
        instance1.colors.push("black"); 
        alert(instance1.colors); //"red,blue,green,black" 
        var instance2 = new SubType(); 
        alert(instance2.colors); //"red,blue,green" 
    

    6.3.3组合继承

            function SuperType(name){ 
             this.name = name; 
             this.colors = ["red", "blue", "green"]; 
            } 
            SuperType.prototype.sayName = function(){ 
             alert(this.name); 
            }; 
            function SubType(name, age){ 
             //继承属性
             SuperType.call(this, name); 
             
             this.age = age; 
            } 
            
            SubType.prototype = new SuperType(); 
            SubType.prototype.constructor = SubType; 
            SubType.prototype.sayAge = function(){ 
             alert(this.age); 
            }; 
            var instance1 = new SubType("Nicholas", 29); 
            instance1.colors.push("black"); 
            alert(instance1.colors); //"red,blue,green,black" 
            instance1.sayName(); //"Nicholas"; 
            instance1.sayAge(); //29 
            var instance2 = new SubType("Greg", 27); 
            alert(instance2.colors); //"red,blue,green" 
            instance2.sayName(); //"Greg"; 
            instance2.sayAge(); 
    

    6.3.4原型式继承

        function object(o){ 
         function F(){} 
         F.prototype = o; 
         return new F(); 
        } 
    
    ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一
    个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,
    Object.create()与 object()方法的行为相同。
    var person = { 
     name: "Nicholas", 
     friends: ["Shelby", "Court", "Van"] 
    }; 
    var anotherPerson = Object.create(person); 
    anotherPerson.name = "Greg"; 
    anotherPerson.friends.push("Rob"); 
     
    var yetAnotherPerson = Object.create(person); 
    yetAnotherPerson.name = "Linda"; 
    yetAnotherPerson.friends.push("Barbie"); 
    alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 
    
    Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相
    同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属
    性。例如:
    var person = { 
     name: "Nicholas", 
     friends: ["Shelby", "Court", "Van"] 
    }; 
    var anotherPerson = Object.create(person, { 
     name: { 
     value: "Greg" 
     } 
    }); 
     
    alert(anotherPerson.name); //"Greg" 
    

    6.3.5 寄生式继承

        function createAnother(original){ 
         var clone = object(original); //通过调用函数创建一个新对象
         clone.sayHi = function(){ //以某种方式来增强这个对象
         alert("hi"); 
         }; 
         return clone; //返回这个对象
        }
    
    
        //寄生组合继承
            function SuperType(name){ 
             this.name = name; 
             this.colors = ["red", "blue", "green"]; 
            } 
            SuperType.prototype.sayName = function(){ 
             alert(this.name); 
            }; 
            function SubType(name, age){ 
             SuperType.call(this, name); //第二次调用 SuperType() 
             
             this.age = age; 
            } 
            SubType.prototype = new SuperType(); //第一次调用 SuperType() 
            SubType.prototype.constructor = SubType; 
            SubType.prototype.sayAge = function(){ 
             alert(this.age); 
            };
    
    image.png

    相关文章

      网友评论

          本文标题:继承

          本文链接:https://www.haomeiwen.com/subject/nxtrmftx.html