JavaScript高级程序设计02

面向对象

重点:原型

创立对象

Javascript中尚无类的定义,对象是性质的会聚,能够想象成成散列表。

目的实例

创建object实例,为它助长方法

    var person = new Object();
    person.name = "Yuki";
    person.age = 23;
    person.job = "Student";
    person.sayName = function(){
        alert(this.name);
    };

目的字面量

实在就是创办object实例的简写格局

    var person = {
        name : "Yuki",
        age : 23,
        job : "Student",
        sayName : function(){
            alert(this.name);
        }
    };
    console.log(typeof person); //object

厂子形式

    function creatPerson(name,age,job){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = job;
        return o;
    }
    var person1 = creatPerson("Yuki",23,"Student");

构造函数形式

    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
    }
    var person1 = new Person("Yuki",23,"Student");

    console.log(person1 instanceof Person); //true
    console.log(person1 instanceof Object); //true

优势:实例person1标识为一个特定的品类Person

    console.log(person1.constructor);   //Person

实例有一个constructor性能,指向构造函数Person

原型格局

成立的各种函数都有一个prototype(原型)属性,这些特性是个指针,指向一个对象(原型对象),这么些目标涵盖所有其实例共享的习性和措施。

    function Person(){};    //空的构造函数
    Person.prototype.name = "Yuki";
    Person.prototype.age = 23;
    Person.prototype.job = "Student"
    Person.prototype.sayName = function(){
        alert(this.name);
    };

    var person1 = new Person();
    person1.sayName();  //"Yuki"

    var person2 = new Person();
    person2.sayName();  //"Yuki"

    alert(person1.sayName == person2.sayName);  //true

下边给出对象属性之间的关联,-->意味着指针指向。

  • Person.prototype–>原型对象(⚠️不是构造函数)
  • Person.prototype.constructor–>Person
  • person1.prototype–>Person.prototype

constructor性能,是原型自带的性质。

当读取某个对象的特性时,首先搜索实例本身是否有给定名字的属性。
只要有,重回该属性,假若没有,搜索指针指向的原型对象,从原型对象中找。

设若在实例中添加了和原型中同名的性质,则程序会读取实例创立的习性(优先搜索到实例的特性,实例原型中的属性也在只是不会被搜寻)

    person1.name = "js";
    person1.sayName();  //"js"

    person1.name = null;
    person1.sayName();  //null

    delete person1.name;
    person1.sayName();  //"Yuki" 来自原型

    person2.sayName();  //"Yuki"

要是实例中添加同名属性,只可以用delete操作符完全除去这多少个特性,才方可另行访问原型中的属性

Object.keys()用来访问所有可枚举的实例属性,再次来到一个Array

    var keys = Object.keys(Person.prototype);
    console.log(keys);  //["name","age","job","sayname"]

    var p1 = new Person();
    console.log(Object.keys(p1));   //[]

更简短的原型语法

目的字面量重写原型对象,缩小不必要的输入

    function Person(){}
    Person.prototype = {
        name : "Yuki",
        age : 23,
        job : "Student",
        sayName : function(){
            alert(this.name);
        }
    };

    var friend = new Person();

    console.log(friend instanceof Object);  //true
    console.log(friend instanceof Person);  //true
    console.log(friend.constructor);    //f Object(){[native code]}
    console.log(Person.prototype);  //{constructor: ƒ, name: "Yuki", age: 23, job: "Student", sayName: ƒ}

这里constructor指向Object()而不再是Person了。在后文提到的构造函数+原型格局中,需要constructor指向构造函数。
假若急需针对Person,用如下方法设置:

    function Person(){}
    Person.prototype = {
        constructor : Person,
        name : "Yuki",
        age : 23,
        job : "Student",
        sayName : function(){
            alert(this.name);
        }
    };

对原型的其它修改会在实例中反响出来:

    var friend = new Person();
    Person.prototype.sayHi = function(){alert("hi")};
    friend.sayHi(); //"hi"

实际上就是事先所说的一个搜寻的过程,先找找实例中是否有sayHi属性,没有则在原型中找。

重写整个原型对象会出错

    function Person(){}
    friend = new Person();

    Person.prototype = {
        constructor : Person,
        name : "Yuki",
        age : 23,
        job : "Student",
        sayName : function(){
            alert(this.name);
        }
    };

    friend.sayName();   //error

重写原型对象从前:
friend.prototype–>Person Prototype
Person.prototype–>Person Prototype原型对象
Person Prototype.constructor–>Person
重写原型对象之后:
friend.prototype–>Person Prototype
Person.prototype–>New Person Prototype新原型对象
Person Prototype.constructor–>Person
New Person Prototype.constructor–>Person
了然如下例子:

    function Person(){}
    Person.prototype = {
        constructor : Person,
        name : "Yuki",
        age : 23,
        job : "Student",
        sayName : function(){
            alert(this.name);
        }
    };
    Person.prototype = {
        name : "JS"
    };
    console.log(Person.prototype);  //{name: "JS"}

一体皆对象,原生引用类型也是用这种措施创立的

    console.log(Array.prototype);   //[constructor: ƒ, concat: ƒ, pop: ƒ, push: ƒ, shift: ƒ, …]
    console.log(String.prototype);  //String {"", length: 0, constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}

构造函数.prototype –> 原型对象
在意:不推荐修改原生对象的原型

原型模式存在的问题,见下例:

    function Person(){}
    Person.prototype = {
        constructor : Person,
        name : "Yuki",
        age : 23,
        job : "Student",
        skills : ["js","vba"],
        sayName : function(){
            alert(this.name);
        }
    };
    var person1 = new Person();
    var person2 = new Person();
    person1.skills.push("van");

    console.log(person1.skills);    //["js", "vba", "van"]
    console.log(person2.skills);    //["js", "vba", "van"]

上述代码,只有person.skill会生出问题,因为skill的值是数组(引用类型)。

构造函数+原型情势

创造自定义类型最常见的章程
构造函数情势:定义实例属性
原型格局:定义方法和共享的习性

    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.skills = ["js","vba"]
    }

    Person.prototype = {
        constructor : Person,
        sayName : function(){
            alert(this.name);
        }
    };
    var person1 = new Person("Yuki",23,"Student");
    var person2 = new Person("Kirito",23,"Student");

    person1.skills.push("c");
    console.log(person1.skills);    //["js", "vba", "c"]
    console.log(person2.skills);    //["js", "vba"]

此外部分创立对象的形式:

  • 动态原型形式
  • 寄生构造函数模式
  • 稳妥构造函数模式

继承

ECMAScript只匡助促成持续,紧要倚重原型链来实现。

原型链

前边讲过,原型、构造函数、实例的涉及如下:
实例.prototype–>原型对象
构造函数.prototype–>原型对象
原型对象.constructor–>构造函数
如果让原型对象等于另一个实例,那么此原型对象将含有一个针对性另一个原型的指针。

    function SuperType(){
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function(){
        return this.property;
    };
    function SubType(){
        this.subproperty = false;
    }
    SubType.prototype = new SuperType();

    SubType.prototype.getSubValue = function(){
        return this.subproperty;
    };

    var instance = new SubType();

    console.log(instance.getSuperValue());  //true
    console.log(instance instanceof Object);    //true
    console.log(instance instanceof SubType);   //true
    console.log(instance instanceof SuperType); //true

落实持续的本色是重写原型对象,这里用了new Supertype()新类型的实例取代原型对象。

后续关系图

从不采用subType默认的原型,而是以superType的实例替换。
成立了原型链:instance–>SubType Prototype–>SuperType Prototype
注意:instance.constructor指向SuperType
走访实例属性时,沿原型链向上搜索:

  1. 实例搜索
  2. 搜索SubType.prototype
  3. 搜索SuperType.prototype

骨子里,所有函数的默认原型都是Object的实例,默认原型都会包含一个里面指针,指向Object.prototype

总体的原型链

同原型格局一样存在问题,包含引用类型值的原型属性会被有着实例共享。而且还设有无法向SuperType的构造函数中传递参数的题目。

    function SuperType(){
        this.colors = ["red","blue","green"];
    }
    function SubType(){}
    SubType.prototype = new SuperType();

    var instance1 = new SubType();
    instance1.colors.push("black");
    console.log(instance1.colors);

    var instance2 = new SubType();  //["red", "blue", "green", "black"]
    console.log(instance2.colors);  //["red", "blue", "green", "black"]

借用构造函数

    function SuperType(){
        this.colors = ["red","blue","green"];
    }
    function SubType(){
        SuperType.call(this);
    }
    var instance1 = new SubType();
    console.log(instance1); //{colors : ["red", "blue", "green"]}
    instance1.colors.push("black");
    console.log(instance1.colors);  //["red", "blue", "green", "black"]

    var instance2 = new SubType();
    console.log(instance2.colors);  //["red", "blue", "green"]

借用构造函数不仅可以修改实例属性,还可以改在子类型构造函数中向父类型构造函数传递参数。

    function SuperType(name){
        this.name = name;
    }
    function SubType(){
        SuperType.call(this,"Yuki");
        this.age = 23;
    }
    var instance = new SubType();
    console.log(instance.name); //"Yuki"
    console.log(instance.age);  //23

只是此措施不可能复用,所以引入组合继承。

组成继承

用构造函数继承属性,用原型链继承方法

    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();
    console.log(SubType.prototype.constructor); //SuperType
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };

    var instance1 = new SubType("Yuki",23);
    instance1.colors.push("black");
    console.log(instance1); //SubType {name: "Yuki", colors: Array(4), age: 23}
    instance1.sayName();
    instance1.sayAge();

    var instance2 = new SubType("JS",23);
    console.log(instance2); //SubType {name: "JS", colors: Array(3), age: 23}

诸如此类各种新实例可以具有和谐的习性,又有何不可行使新的方法。

另外部分后续格局:

  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

参考 MDN web
docs

《JavaScript高级程序设计》