继承

假设在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. 理解原型对象


    继承_第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 及更早版本中例


继承_第2张图片
image.png
继承_第3张图片
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 指向的原型中不包含以该名字命名的属性


继承_第4张图片
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); 
        };
继承_第5张图片
image.png

你可能感兴趣的:(继承)