javascript面向对象

来源:互联网 发布:淘宝摄影棚出租 编辑:程序博客网 时间:2024/05/24 06:08

第一种:通过new Object()得到

var person = new Object();            person.name = "小花";            person.age = "两岁";            person.say = function() {                alert(this.name+this.age+"喵喵喵")            }            person.say();

第二种:通过json形式得到

var person = {                name : "小花",                age : 2,                say : function() {                    alert(this.name+this.age);                }            };            person.say();

以上这两种方法都存在了对象不能重用的缺陷由此有了第三种方法工厂模式
第三种:工厂模式

function createPerson(name,age) {                var p = new Object();                p.name = name;                p.age = age;                p.say = function() {                    alert(this.name+this.age)                }                return p;            }            var p1 = createPerson("小花",2);            var p2 = createPerson("小小",6);            p1.say();            p2.say();

使用工厂模式定义对象有效地解决了对象无法重用的问题,但是又存在了另一个问题,就是无法判断得到的对象的类型了,如 typeof 或者 instanceof 来判断类型,仅仅得到一个 Object 类型,所以就推出了基于构造函数的方式
第四种构造函数

//构造函数function Person(name,age) {                this.name = name;                this.age = age;                this.say = say;                this.say=function(){                    alert("我的名字是:"+this.name+",我的年龄是:"+this.age);                }            }            var p1 = new Person("小花",18);            var p2 = new Person("小小",19);            p1.say();            p2.say();            alert(p1 instanceof Person);

基于构造函数的定义的方式最大的好处除了对象重复使用外,就是让我们还可以判断它的类型。但是还存在的一个问题就是:say 方法在每个对象创建后都存在了一个方法拷贝(但是我们
发现代码在只有调用时,say 方法才会在堆中创建,基于闭包的原理),这样就增加了内存的
消耗了,如果在对象中有大量的方法时,内存的消耗就会高。
解决方法:把say这个方法放到全局函数,这样就所有的对象指向了一个方法。

function Person(name,age) {                this.name = name;                this.age = age;                this.say = say;            }            function say() {                alert("我的名字是:"+this.name+",我的年龄是:"+this.age);            }            var p1 = new Person("小花",18);            var p2 = new Person("小小",19);            p1.say();            p2.say();

存在问题:破坏了对象的封装性。
解决方法:基于原型的对象创建方案。
封装–Javascript 的原型(prototype)

function Person(){};            Person.prototype.name="小花";            Person.prototype.age="两岁";            Person.prototype.sex="...";            Person.prototype.say=function(){                alert(this.name+";"+this.age+";"+this.sex);            }            var p1 = new Person();            p1.say();//正常访问            say();//报错

1.把属性放构造函数里
2.把方法放在原型中
这样 window 就无法访问到 say 方法了,此时 say 方法只属于 Person 对象独有的方法。解决了封装破坏的情况。
什么是原型?

            //第一种状态            //定义了一个对象             function Person() {}            //第二种状态,这样赋值就会赋在原型对象中             //使用原型来给对象赋值             //这样就讲一个对象的属性和方法放在了该对象的原型中             //外界是无法访问到这样数据的             Person.prototype.name="小花";            Person.prototype.age="两岁";            Person.prototype.sex="...";            Person.prototype.say=function(){                alert(this.name+";"+this.age+";"+this.sex);            }            //第三种状态             var p1 = new Person();            //此时调用的是原型中的,因为自己中没有这些属性和方法             p1.say();            //正常访问了            say(); //报错了 

原型是 js 中非常特殊一个对象,当一个函数创建之后,会随之就产生一个原型对象,当
通过这个函数的构造函数创建了一个具体的对象之后,在这个具体的对象中就会有一个属性
指向原型。
这里写图片描述
原型重写

function Person() {}            Person.prototype = {                name: "小花",                age: 18,                say: function() {                    alert("我的名字是:" + this.name + ",我今年" + this.age + "岁了");                }            }            var p1 = new Person();            p1.say() var p2 = new Person();            p2.name = "小小";            p2.age = 20;            p2.say();

我们是将该对象的原型覆盖

function Person() {}            var p1 = new Person();            Person.prototype.sayHello = function() {                    alert("我的名字是:" + this.name + ",我今年" + this.age + "岁了");                }                //   p1.sayHello();              Person.prototype = {                    constructor: Person, //手动指向Person  name : "刘帅哥",  age : 18,  say : function() {   alert("我的名字是:"+this.name+",我今年"+this.age+"岁了");  } }                      var p2 = new Person();                    p2.name = "小小";                    p2.age = 20;                    p1.sayHello(); //此时找不到name和age,但是代码正确 p2.say();//正确                      p1.say(); //错误,因为原型重写了 p2.sayHello();//错误

以下是原型重写图这里写图片描述

终极方案—基于组合的对象定义

function Dog(name,age,sex,color,weight){                this.name=name;                this.age=age;                this.sex=sex;                this.color=color;                this.weight=weight;            }            Dog.prototype.speak=function(){                alert(this.name+"汪汪汪");            }            Dog.prototype.eat=function(){                alert(this.name+"好饿");            }            var d1=new Dog("小黑",2,"男","黑",20);            d1.speak();            d1.eat();

目的就是为了这种定义 javascript 对象的方式,所以最终的定义 javascript 对象的方案就是最终的定义 javascript 对象的方案就是基于组合的方式定义,将属性的定义放在构造函数中,将方法的定义放在原型中。
继承–原型创建对象
继承,望名而知意,就是我们现实社会中的子孙后代继承了父辈的财富,我们一直在说,面向对象的语言就是在模拟现实世界,通过模拟现实世界来编程
* 原型链实现继承*

//定义一个父类 function Parent() {  this.pv = "parent"; }  Parent.prototype.showParent = function() {  alert(this.pv); }  //定义一个子类 function Son() {  this.sv = "Son"; }  //使用原型链来实现继承 Son.prototype = new Parent();  Son.prototype.showSon = function() {  alert(this.sv); }  var s1 = new Son(); s1.showParent(); s1.showSon(); 

当子类的原型指向父类的对象后,子类就继承了类,实现了继承,这就是基于原型链的继承。以下是内存模型图分析这种继承。
这里写图片描述
但是使用原型链实现继承会有以下一些问题:
1、 不要在设定了原型链之后,再原型重写
2、 一定要在原型链赋值之后才能添加或者覆盖方法
父类方法的覆盖(重写)
当子类继承父类后,子类如果认为父类的方法不能足自己或者不太满意父类的方法,可以使用与父类同名的方法来覆盖(也叫重写)父类方法。
注意:javascript 中存在重写,但是没有重载。
原型链继承的缺陷
1、 无法从子类中调用父类的构造函数,这样就没有办法把子类属性赋值给父类。
2、 父类中属性是在子类的原型中的,这违背了我们前面所讲的装的理念(属性在对象中,方法在原型中),会出现前面值的混淆问题。 所以我们一般都不要使用单纯的原型链来实现继承。
基于伪装实现继承
call和 apply 方法,基于这两个方法,我们可以实现一个基于伪
装的继承。

function Parent() {                this.pv = "parent";            }            //定义一个子类             function Son() {                this.sv = "Son";                Parent.call(this); //注意:此时的this指的是Son的对象        //那么就是Son对象调用Parent函数 }                var s1 = new Son();                alert(s1.pv);

在子类中的 this 指的就是子类实例化后的对象本身,当我们在子类中使用 call 方法调用父类后,就相当于将父类的构造方法绑定到了子类的对象身上,这样就伪装了子类可以使用父类的构造方法,完成了继承。
子类初始化父类属性
基于原型链的继承我们说了缺陷就是无法实现子类去调用父类的造函数,这样就无法修改父类的属性,基于伪装完全可以解决这个问题。

//定义一个父类            function Parent(name) {                this.name = name;            }            //定义一个子类             function Son(name, age) {                this.age = age;                Parent.call(this, name);            }            var s1 = new Son("小花", 18);            var s2 = new Son("小草", 28);            alert(s1.name + "," + s1.age);            alert(s2.name + "," + s2.age);

这样可以通过子类来设置父类的属性。
伪装的缺陷
由于使用伪造的方式继承,子类的原型不会指向父类,所以父类中写在原型中的方法不会被子类继承,所以子类调用不到父类的法。解决的办法就是将父类的方法放到子类中来,但是这样的又违背了封装的理念。
终极方案—基于组合实现继承

function Parent(name) {                this.name = name;                this.friends = ["小花", "小草"];            }            Parent.prototype.parentSay = function() {                    alert(this.name + "---->" + this.friends);                }                //定义一个子类             function Son(name, age) {                this.age = age;                Parent.apply(this, [name]);            }            //使用原型链来实现继承             Son.prototype = new Parent();            Son.prototype.sonSay = function() {                alert(this.name + "*******===" + this.age);            }            var s1 = new Son("小小", 18);            s1.friends.push("花花");            s1.parentSay();            s1.sonSay();            var s2 = new Son("牛牛", 28);            s2.parentSay();            s2.sonSay();

总结:不管是封装还是继承最终方案都是基于组合,就是汲取了这种方案的长处,舍去了缺点

0 0