重识Javascript系列---ECMAScript继承

来源:互联网 发布:yum安装ipk 编辑:程序博客网 时间:2024/05/17 09:41

1 、工厂模式

即用函数来封装以特定接口创建对象的细节

function createPerson (name,age,sex) {    var person = new Object();    person.name = name;    person.age = age;    person.sex = sex;    person.sayName = function() {        console.log(this.name);    };    return person;}var person1 = createPerson("Liz","21","female");var person2 = createPerson("Cyt","21","male");person1.sayName();person2.sayName();

2 、构造函数模式

自定义构造函数,自定义属性和方法

function Person (name,age,sex) {//构造函数应大写第一个字母,其他函数应小写第一个字母    this.name = name;    this.age = age;    this.sex = sex;    this.sayName = function() {        console.log(this.name);    };}var person1 = new Person("Liz","21","female");var person2 = new Person("Cyt","21","male");person1.sayName();person2.sayName();

2.1 构造函数的调用

作为构造函数使用

var person = new Person("Liz","21","female");person.sayName();

作为普通函数使用

Person("Cyt","21","male");window.sayName();

在另一个对象中调用

var o = new Object();Person.cell(o,"Cyt","21","male");//使用cell或apply在某个特殊对象的作用域中调用Person()构造函数,调用完成后,此特殊对象就有了Person的所有属性和方法。o.sayName();

2.2 构造函数的缺点

ECMAScriptS中函数是对象,因此每定义一个函数,也就是实例化了一个对象。
所以每个Person实例都包含了不同的方法显示name属性,然而创建多个方法完成同样任务function实例根本没必要。故可将函数定义转移到构造函数外部:

function Person (name,age,sex) {    this.name = name;    this.age = age;    this.sex = sex;    this.sayName = sayName;}function sayName() {        console.log(this.name);    };var person = new Person("Liz","21","female");person.sayName();

3 、原型模式

使用原型的好处就是可以让所有对象共享它所包含的属性和方法,不必在构造函数中定义对象信息,而是可以将这些信息直接添加到原型对象中。

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();person1.sayName();//Lizperson2.sayName();//Liz

3.1 理解原型

无论什么时候,只要创建你一个函数,就会默认为该函数创建一个prototype属性(Person.prototype)。默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性和_proto _(神秘内部属性━━━(゚∀゚)━━━━!!姐自创的,反正就一个神奇的内部属性呗)。
constructor(构造函数)属性包含一个指向prototype属性所在函数的指针。
_proto _(神秘内部属性)属性则在使用构造函数创建一个实例后,在新实例中的该_proto _(神秘内部属性)属性包含一个指向构造函数的原型属性的指针,没有创建新实例则默认Object。

function Person () {    }

这里写图片描述
默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性和_proto _(神秘内部属性)。

添加其他prototype后也是如此。

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};

这里写图片描述

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();person1.sayName();//Lizperson2.sayName();//Liz

创建新实例后person1.constructor指向person1的prototype属性所在的函数,person1._proto_ 指向的内容就是其构造函数的原型即Person.prototype
这里写图片描述
黑色箭头表示包含关系,红色箭头表示指向关系
这里写图片描述
Person构造函数、Person的原型属性及现有的两个实例之间的关系如下
这里写图片描述
这里写图片描述
在所有实现中可通过isPrototypeOf()来确定对象之间是否存在这种关系。
如:

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();console.log(Person.prototype.isPrototypeOf(person1));//trueconsole.log(Person.prototype.isPrototypeOf(person2));//true

3.2 原型与实例工作关系

由图可知:
这里写图片描述
实例person1和person2都只是包含了一个指向Person.prototype的内部属性_proto_,但依然可以调用person1.sayName()。
那是因为,每当代码读取某个对象的某个属性时,都会执行一次搜索。搜索首先从对象实例本身开始,若没找到,再继续搜索指针指向的原型对象。故person1.sayName()会读取保存在原型对象中的函数。
虽然可以通过对象实例访问原型中的值,但不能通过对象实例重写原型中的值。如果在实例中添加了一个属性,其名称与实例原型中的一个属性同名,那该属性就会屏蔽原型中的那个属性

例:

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();person1.name = "Cyt";person1.sayName();//Cytperson2.sayName();//Liz

创建同名属性,原型中的属性会被屏蔽,但如果是修改·····那就修改了原型属性,所有实例都将可访问该被修改的属性内容!!!

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.friend = ["Jace","Lucy"]Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();person1.friend.push("Cyt");

这里写图片描述
通过 delete 操作符可完全删除实例的该属性,使我们可以再次通过此实例访问原型对象中的该属性
如:

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();person1.name = "Cyt";person1.sayName();//Cytperson2.sayName();//Lizdelete person1.name;person1.sayName();//Lizperson2.sayName();//Liz

可通过hasOwnProperty()方法来检测一个属性是否存在于实例还是原型,返回true才是给定属性存在于对象实例
如:

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();person1.name = "Cyt";console.log(person1.hasOwnProperty("name"));//true——来自实例自己console.log(person2.hasOwnProperty("name"));//false——来自原型

通过hasOwnProperty方法和for in 的结合可以确定该属性存在于原型还是实例
因为,hasOwnProperty()只在属性存在于实例才返回true,而in只要通过对象能访问到该属性即返回true.所以hasOwnPrototypeProperty返回true,则该属性属于原型,反之属于实例或者不存在该属性,所以使用时应当确保该属性存在

function Person () {    }Person.prototype.name = "Liz";Person.prototype.age = 21;Person.prototype.sex = "female";Person.prototype.sayName=function() {    console.log(this.name);};var person1 = new Person();var person2 = new Person();console.log(hasOwnPrototypeProperty (person1,"name"));//true——存在于person1.name = "Cyt";console.log(hasOwnPrototypeProperty (person1,"name"));//falseconsole.log(hasOwnPrototypeProperty (person2,"name"));//trueconsole.log(hasOwnPrototypeProperty (person2,"id"));//falsefunction hasOwnPrototypeProperty (object,name) {    return !object.hasOwnProperty(name)&&(name in object);}

这里写图片描述

3.3 新的原型语法

对象字面量写法

function Person () {    }Person.prototype = {    name : "Liz",    age : 21,    sex : "female",    sayName:function() {    console.log(this.name);    }}

这样的创建方法确实更加简洁,但是Person.prototype也相当于设置为一个以字面量形式创建的新对象,虽然最终结果相同,仅constructor属性不再指向Person了,而是一个Object,如果constructor属性非常重要,可在创建原型时特意重新设置它的值。
如:

function Person () {    }Person.prototype = {    constructor : "Person",    name : "Liz",    age : 21,    sex : "female",    sayName:function() {    console.log(this.name);    }}

3.4 对_proto_ 的重新理解

_proto_是当前实例指向其构造原型的指针,当创建实例后,又将原型修改为其他对象,则切断了构造函数与最初原型之间的联系,但它任引用最初的原型实例中的_proto_指针仅指向原型(Person.prototype)而不是构造函数(function Person)
这里写图片描述

function Person () {    }Person.prototype = {    constructor : "Person",    name : "Liz",    age : 21,    sex : "female",    sayName:function() {    console.log(this.name);    }}var person1 = new Person();person1.sayName();//LizPerson.prototype = {    constructor : "Person",    name : "Cyt",    age : 21,    sex : "female",    sayName:function() {    console.log(this.name);    }}var person2 = new Person();person1.sayName();//Lizperson2.sayName();//Cyt

3.5 原型对象的总结

  1. 原生对象的原型在构造函数上都是采用这样的模式,所有我们可以通过Array.prototype找到sort方法,甚至通过重新定义同名方法修改原生对象的原型,但是并不推荐这样的做法。
  2. 原型模式忽略了为构造函数传参,也因为其共享的本性造成很大的问题。

4 、构造模式与原型模式组合 !!!

实例属性在构造函数中定义,而所有实例共享属性则在原型中定义。

function Person (name,age,sex) {    this.name =name;    this.age = age;    this.sex = sex;    this.friend = [];    }Person.prototype = {    constructor : "Person",    sayName:function() {    console.log(this.name);    }};var person1 = new Person("person1","21","female");var person2 = new Person("person2","22","male");person1.friend.push("Cyt,Liz,Jace");person2.friend.push("Cyt,Liz,Lucy");console.log(person1.friend);//["Cyt,Liz,Jace"]console.log(person2.friend);//["Cyt,Liz,Lucy"]

5 、继承

原型、构造函数、实例之间的关系:
这里写图片描述
让原型对象等于另一个类型的实例。则此时的原型对象将多包含一个指向另一个原型的指针,相应的,另一个原型对象中也会包含一个指向另一个构造函数的指针
继承通过创建Person实例,并将Person实例付给Monitor.prototype实现的。其本质是重写原型对象,从而使Person实例的属性和方法也存在Monitor.prototype中。

function Person (name,age,sex) {    this.name =name;    this.age = age;    this.sex = sex;    this.friend = [];    }Person.prototype = {    constructor : "Person",    sayName:function() {    console.log(this.name);    },    sayFriend:function() {    console.log(this.friend);    }}function Student(className , id){    this.class = className;    this.id = id;}Student.prototype = new Person();Student.prototype.sayId = function() {    console.log(this.id);}Student.prototype.sayClass = function() {    console.log(this.class);}var person = new Person();var student = new Student();

这里写图片描述
!!!自有方法一定要在继承语句后添加,否则会被重写···即白写了,而且自有方法也不能采用字面量形式创建!!!否则···连你的继承也白写,嘿嘿嘿,字面量形式固然简洁,一定要注意···!!!

function Person (name,age,sex) {    this.name =name;    this.age = age;    this.sex = sex;    this.friend = [];    }Person.prototype = {    constructor : "Person",    sayName:function() {    console.log(this.name);    },    sayFriend:function() {    console.log(this.friend);    }}function Student(className , id){    this.class = className;    this.id = id;}Student.prototype = new Person();Student.prototype.sayId = function() {    console.log(this.id);}Student.prototype.sayClass = function() {    console.log(this.class);}function Monitor(grade){    this.grade = grade;}Monitor.prototype = new Student();Monitor.prototype.sayGrade = function() {    console.log(this.grade);}var person = new Person();var student = new Student();var monitor = new Monitor();

这里写图片描述

额额,这个只能说确实是让他们继承了,但是并不实用······························
于是乎,吼吼,总结一句话就是使用空func作为继承纽带,连接父类和子类。

  1. 创建空func
  2. 使func的原型指向父类的原型
  3. 使子类的原型指向新创建的func()
  4. 确保父类构造函数和子类构造函数都是自己
function Person (name,age,sex) {    this.name =name;    this.age = age;    this.sex = sex;    this.friend = [];    }Person.prototype = {    sayName:function() {    console.log(this.name);    },    sayFriend:function() {    console.log(this.friend);    }}function Student(className,id,name,age,sex){    Person.apply(this,[name,age,sex]);    this.class = className;    this.id = id;}function Monitor(grade,className,id,name,age,sex){    Student.apply(this,[className,id,name,age,sex]);    this.grade = grade;}function extend (subClass,superClass) {    var func = function() {};//这其实是个类    func.prototype = superClass.prototype;//因为我们采用的是构造原型组合模式,所有,prototype只含是方法    subClass.prototype = new func();//子类的原型指向新创建的func(),其实就是将func中superClass.Prototype加到自己的方法中。    subClass.prototype.constructor=subClass;//确保父类构造函数和子类构造函数都是自己    superClass.prototype.constructor=superClass;}extend(Student,Person);extend(Monitor,Student);Student.prototype.sayId = function() {    console.log(this.id);}Student.prototype.sayClass = function() {    console.log(this.class);}Monitor.prototype.sayGrade = function() {//自定义的方法要写在继承后,以免被重写    console.log(this.grade);}var Liz = new Monitor("心理委员","计科2013-02","2013443394","Liz","21","female");

这里写图片描述
这里写图片描述

0 0
原创粉丝点击