Javascript -- OO

来源:互联网 发布:数据质量分析报告公司 编辑:程序博客网 时间:2024/05/18 03:05

因为script没有办法创建类,所以我们只能是通过构造函数来进行类的定义,里面可以有属性,以及方法。


工厂模式:使用简单的函数创建对象,为对象添加属性和方法,然后返回对象,最后被构造函数替代

构造函数:可以创建自定义引用类型,可以想创建内置对象实例一样使用new操作符。不过,构造函数模式也有缺点即每个成员都会在新实例重新创建一遍。由于函数可以不局限于任何对象,因此没有理由不再多个对象键共享函数。你完全可以把它定义成共享,然后成员方法里面用this来进行引用。

原型模式:使用构造函数的prototype属性来指定那些应该共享的属性和方法。组合使用构造函数模式和原型模式是,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。


Javascript主要通过原型链实现继承,原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型的构造函数。这样就可以做到每个实例都具有自己的属性。同时还能保证只使用构造函数模式来定义类型。使用最多的继承模式是组合模式,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。


原型式继承,可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制,而复制得到的副本还可以进一步改造

寄生式继承,与原型式继承类似,也是基于某个对象或某些信息来创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类型构造函数而导致的低效率问题。

寄生组合继承,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。


对象都有一个constructor(构造函数)属性。该属性执行person.它最初是用来标识对象类型的,但是现在最好还是用instanceof操作符,能够标识自定义的类型,这个也是构造函数替代工厂模式的原因。

alert(person1.constructor == Person);        //truealert(person1 instanceof Person);            //true
将构造函数当成函数

构造函数就是你对函数进行了new调用。否则跟普通函数一样

//当构造函数var Person = new Person("ali", 29, "home");person.sayname();                           //ali//在另一个对象的作用域中调用var o = new Object();Person.call(o, "lily", 27, "company");      //通过call对o,调用Person方法,因为都是this.name类型形式,所以给o添加了新的属性o.sayname();                                //lily 

每个函数都有一个prototype属性,它指向一个prototype对象,它包含自定义的特定类型的所有实例共享的属性和方法;最初这个对象只包含constructor属性。指向构造函数本身,所以我们才都能访问到这个方法。

每个对象的实例都会有一个属性_proto_来指向构造函数对象中prototype所指向的对象。注意这是对所有的实例而言的,因为prototype是共享的。

构造函数就是不涉及prototype指向对象的那段进行this引用操作的逻辑。其中prototype的constructor属性会指向当前的函数,所以我们才说它指向的是构造函数。

访问顺序:先实例中查看是否有要找的名字,然后才到prototype所指的原型对象中查找哦。

Person.name = "ali";                        //在实例里面创建这个属性Person.prototype.name = "ali";              //在实例的原型对象中添加这个属性 


通过hasownproperty()方法和in操作符就可以确定该属性到底是存在于实例中还是在原型中。
alert("name" in Person1);                   //都为true的话,就说明在实例中,否则在原型对象中。alert(person1.hasOwnProperty("name"));      //最好在下面,因为如果上面为假,这个肯定为假
原型的动态型:对原型对象的任何修改都能够立即在实例中显示。

易错点:先定义一个var person = new Person() 对象,然后我们对函数Person的prototype对象进行重写的话,那么重写之前的对象仍旧是引用最初的原型对象,因为我们前面已经说了,每个对象的建立都会有一个属性_proto_指向原型对象,我们对函数的prototype重写的话,现在新的函数prototype指向了一个新的位置,对以前的对象不会有影响。


组合构造函数和原型模式例子,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。它是目前用的最广泛的创建自定义类型的方法:

function Person(name, age, job){    //属性    this.name = name;    this.job  = job;    this.age  = age;    this.friends = ["ali", "lily"];             //引用对象,一定要放到构造函数里面    //方法    if (typeof this.sayname != "function"){     //只会在初次调用构造函数时才执行,你只需检查一个就行,不须要写一堆if语句检查  Person.prototype.sayname = function(){    alert(this.name);};    }}

继承

原型链作为实现继承的主要方法,它利用先对原型对象进行重写,赋予另一个类型的实例,造成的结果就是我们现在的原型对象可以访问到另一个对象的原型对象,同时另一个对象通过原型对象中的constructor属性可以访问到当前对象的构造方法。然后假如这个对象的原型对象指向另一个对象,我们就组成了一个链式的结构。

首先我们必须是先重写当前对象的原型对象,然后再对这个原型对象进行功能的增加。

原型链的问题:父实例中的所有属性和方法都会成为子函数中的原型对象中的属性。

function SuperType(name){    this.name = name;    this.color = ["red","blue"];}SuperType.prototype.sayname = function(){    alert(this.name);};function SubType(name, age){    SuperType.call(this, name);            //第二次调用Supertype()是在构造函数中,它只会调用父方法中的构造方法重新创建属性name,color。    this.age = age;}SubType.prototype = new SuperType();       //第一次调用Supertype(),会创建name,color以及编译sayname方法到prototype中SubType.prototype.sayage = function(){    alert(this.age);};

我们应该发现了,它实际上是调用两次Supertype()的构造方法,其实多次的调用也是非常的耗费时间。下面是一个改进的寄生组合模式继承

function inherit(SubType, SuperType){    var prototype = Object(SuperType.prototype);  //创建对象,    prototype.constructor = SubType;              //让原型对象指向子函数    SubType.prototype = prototype;          //现在我们的子函数中的原型模块既有父函数中的原型对象,同时也有一个指向自身构造方法的属性constructor}function SuperType(name){    this.name = name;    this.color = ["red","blue"];}SuperType.prototype.sayname = function(){    alert(this.name);};function SubType(name, age){    SuperType.call(this, name);                this.age = age;}inherit(SubType,SuperType);               //与原型组合继承不同的地方。。。。。    SubType.prototype.sayage = function(){    alert(this.age);};


注意:在Js中无须实现定义好类的属性,只需要为属性赋值,js将自动创建这些属性。

详见:Javascript 高级编程,第六章面向对象的程序设计