JavaScript高级语法-面向对象编程模式的特性及实现

@写在前面:对于初学者友好,变量声明没有采取ES6+标准,且学且珍惜🌹

来着是客,客随主便,如果帮到你麻烦动动您的金手指点个赞支持本蜗牛,我将倍儿有面子

以下知识如果帮助到喜欢学习的你我将倍感荣幸,如果有哪里缺漏或错误的地方望指正

本文将介绍有关面向对象的特性及实现,想要快速上手,这篇文章很适合你,但是对于对象,我只是没有大哥而已。🤣除此以外的知识这里一笔带过,我将会通过学习不断补全一笔带过的知识点,希望和你一起共同成长共同进步😘Mu~a~**

关于面向对象编程

       面向对象编程是大家耳熟能详的编程模式,对于初学者而言,对象容易理解,girlFriends嘛,简简单单。但是对于一个项目的开发模式,除了历史课出现过的面向过程编程,随着技术的不断进步,这种模式显然已经不能满足那些秃头大佬的潮流进步,所以面向对象编程就大踏步的走来了。
       将现实抽象,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统更贴近事物的自然运行模式。 当然,这些除了我的理解也只有度娘更详细了

面向对象的特性(优势)

  1. 抽象性:将现实业务抽象为对象,以对象之间的关系分析业务
  2. 封装性:将属性方法封装到对象里,方便维护、统一进行管理,便于二次开发,安全性也有保障
  3. 继承性:将对象的属性方法进行传递,继承公用的方法属性可以大大提高开发效率,减少不必要的内存消耗
  4. 多态性:一个类可以产生多种类型的对象。JavaScript原生实现的多态就只有函数重写,JavaScript无法直接实现,其他的形式可以通过某些技巧来模拟,但不推荐。

抽象性是一个概念,理解就行,实际操作就是将现实抽象出他的某些属性和行为

封装性

就是将对象的手用麻袋套起来🙅

属性分为两大类

  • 公有属性:任何人在任意位置都可以访问并修改的属性
  • 私有属性:有一定的限制条件,达到条件才可以访问或更改,这样的属性安全性较高

下面将通过案例代码实现共有属性与私有属性

//创建一个构造函数及其实例
function User(name,phone){
    this.name=name;
    this.phone=phone;
}

//实例化一个对象
var u1=new User("张三","13284956871");

//张三的phone可以直接访问
console.log(u1.phone);//13284956871

//而且可以直接修改
u1.phone="12347594740";
console.log(u1);//User {name: '张三', phone: 12347594740}

        显然,这样的公有属性是不安全的,所以我们就可以利用面向对象的封装性,进行属性的私有封装,这就类似于闭包的方式了,在构造函数内部创建两个方法(getset),我们可以通过get方法获取对应数据,利用set方法修改对应数据,数据无法直接在外部进行访问,这样就提高了数据的安全性

//在User构造函数内部封装一个局部变量,只能通过其方法获得
function User(name,phone){
    this.name=name;
    var phone=phone;
    //创建方法时,我们可以判断其传入的用户(模拟当前用户)
    //get方法
    this.getPhone=function(user){
        //判断user.name是不是当前对象的this.name
        if(user.name==this.name){//如果用户是当前对象,那么就返回他的phone
            return {uname:this.name,uphone:phone};
        }else{//如果不是,那就说明权限不够
           return "这是"+this.name+"的数据"+user.name+"权限不够无法查看";
        }
    }
    //set方法
    this.setPhone=function(user,uphone){//当然还是要判断传入的user,然后传入修改的phone
        if(user.name==this.name){
            //如果是当前用户,修改phone时还需要验证其输入的修改格式是否正确
            var reg=/\d{11}/;
            if(reg.test(uphone)){//如果符合规范,才可以修改
                phone=uphone;
                return "修改成功";
            }else{//如果不符合
                return "格式错误";
            }
        }else{
           return "这是"+this.name+"的数据"+user.name+"权限不够无法修改";
        }
    }
}
//实例化一个对象
var u1=new User("张三","17480285760");
//先输出看一下
console.log(u1);//User {name: '张三', getPhone: ƒ, setPhone: ƒ}这里只能拿到它的共有属性和方法,私有属性只能通过其内部方法获得

//实例一个李四
var u2=new User("李四","17482047563");

//接着让李四查看或修改张三的手机号
console.log(u1.getPhone(u2));//权限不够
console.log(u1.setPhone(u2));//权限不够

//接着让张三自己获取修改自己的电话
console.log(u1.getPhone(u1));//{uname: '张三', uphone: '17480285760'}
//接着,张三随便输入修改
console.log(u1.setPhone(u1,"12345678"));//格式错误
//再次查看
console.log(u1.getPhone(u1));//没有被修改

//格式正确后
console.log(u1.setPhone(u1,"16352786789"));//
//再次查看
console.log(u1.getPhone(u1));//修改成功--{uname: '张三', uphone: '16352786789'}

请添加图片描述

以上代码只是实现了一个简单的逻辑,说明私有属性安全性较高,这只是一个思想,具体逻辑还需要看实际业务需求


继承性

就是JavaScript中的基因遗传,儿子除了有自己的属性还继承了父亲的属性,接着儿子的儿子也继承了父亲的属性。但在JavaScript中,继承可比遗传学简单多了。

       这里使用原生JavaScript来实现继承,这其实也可以算一道面试题了,为了理解原理,我们将层层递进,而不是一次就学会最终效果的完美继承

例:

//实现原生继承
//举例:动物园管理系统-有多个类型的动物(2只老虎,3只狮子,3只熊猫)
//不利用js的继承性思想,实现该需求

/*创建老虎的构造函数以及2只老虎实例对象*/
function Tiger(name,age){
    this.name=name;
    this.age=age;
}

//老虎的行为都是共有的,所以可以放到原型中以节省内存消耗
Tiger.prototype={
    move:function(){
        console.log(this.name+"正在走");
    },
    eat:function(){
        console.log(this.name+"正在吃饭");
    },
    play:function(){
        console.log(this.name+"正在打滚");
    }
};

//创建两只老虎
var t1=new Tiger("大皮",3);
var t2=new Tiger("小皮",4);

/*创建狮子的构造函数以及3只狮子实例对象*/
function Lion(name,age){
    this.name=name;
    this.age=age;
}

//共有行为
Lion.prototype={
    move:function(){
        console.log(this.name+"正在走");
    },
    eat:function(){
        console.log(this.name+"正在吃饭");
    },
    mating:function(){
        console.log(this.name+"正在造小狮子");
    }
};

//3只狮子
var l1=new Lion("阿俊",3);
var l2=new Lion("大头",2);
var l3=new Lion("采花",2);

/*创建熊猫的构造函数以及3只熊猫*/
function Panda(name,age){
    this.name=name;
    this.age=age;
}

//共有行为
Lion.prototype={
     move:function(){
        console.log(this.name+"正在走");
    },
    eat:function(){
        console.log(this.name+"正在吃饭");
    },
    climbing:function(){
        console.log(this.name+"正在爬树");
    }
};

//3只熊猫
var p1=new Panda("大花",3);
var p2=new Panda("小花",2);
var p3=new Panda("翠花",2);


~这一顿操作下来,费时又费力,艾玛累坏我了~

通过以上案例我们可以看出,不同的构造函数具有相同的属性和行为(方法),而且这样创建对象效率极低,代码量多,内存消耗也大

       所以,我们是否可以将重复的属性及方法单独放到一块,放到一个构造函数中,让其他构造函数可以复用,这也就是继承的思想和概念

//我们可以创建一个构造函数,把共有的属性和方法都给该构造函数
function Animal(name,age){
    this.name=name;
    this.age=age;
}
Animal.prototype={
    move:function(){
        console.log(this.name+"正在走");
    },
    eat:function(){
        console.log(this.name+"正在吃饭");
    }
};

那么如何实现其他构造函数继承Animal中的属性和方法,这里就需要简单解释一下原型的一些概念


一个对象操作时,属性或方法的默认查找方式为:先从该对象的构造函数内部查找有没有某个属性,如果有直接获取使用,如果没有再从其构造函数的原型[[prototype]](也就是该对象原型__proto__指向的prototype)中查找,如果还没有找到,那就去构造函数原型对象的构造函数中去查找,直到找到返回或未找到提示。

依据上面的思想,我们也就可以实现一种继承方式——原型继承

原型继承
//创建一个猫的构造函数
function Cat(name,age){
    //这里可以不必写,因为是继承过来的,但name,age参数还是要有,需要进行传值
}
//属性或方法可以找原型要,那么我们就可以将Animal构造函数属性都挂载到Cat原型上
//也就是说,可以让Cat.prototype(原型对象) 等于Animal对象
Cat.prototype=new Animal("继承",3);//将动物对象赋值给Cat原型对象,所以Animal属性也就一起给了Cat.prototype

//创建一个对象,测试一下
var c1=new Cat("加菲",4);
console.log(c1);

请添加图片描述

可以看到,Cat对象中没有属性,但在他的原型上继承了Animal的属性
       通过这种方式继承的属性被原型定义好的属性取代,无法直接使用,优点是实现了原型方法的继承,eatmove都被Cat继承了过来,但是缺点就是所有的属性值,只和创建animal对象时保持一致

虽然以上的原型继承方式不能直接使用,但是依据对象中属性或方法的查找方式,我们依旧可以利用这样的特性另辟蹊径

       依据以上思想, 我们就能知道,可以将附带共同属性的构造函数挂载到继承它的构造函数的原型上,但是继承之后属性值无法通过对象实例改变,所以能否将这些属性进行重置,这样就可以在对象的新实例中修改属性的值了

       我们知道,构造函数中是可以改变this的指向,也就是说创建对象实例时,构造函数的属性值就是这个对象实例的属性值,所以想到,在构造函数中,将Animal的属性都挂载过来

依据如上想法,我们也就有了第二种继承方式——冒充继承

冒充继承
/*在使用new创建对象实例的过程中,会在其构造函数内部创建一个空对象,然后让this指向该空对象,接着把所有的属性都挂载给空对象并赋值,最后返回出来赋值到创建的实例上
*/
//所以我们就可以想到,在构造函数中,将Animal的属性都挂载过来

function Dog(name,age){
    //根据new的原理,可以在Dog函数内部调用Animal(arg1,arg2...),这样从Dog传进来的值会传入Animal,最后再Animal中,this就指向了这些属性
    Animal(name,age);
}
var dog=new Dog("小黑",2);
console.log(dog);//打印出来后发现dog并没有属性值,说明Animal中的this并没有指向new创建的对象
//如果Animal中的this等同于Dog中的this,那么就意味着Animal中的this指向new创建的对象
//这里我们可以使用call方法强行改变this指向
function Dog2(name,age){
    Animal.call(this,name,age);//意思是将Dog中的this与参数对应的传给Animal
}
var dog2=new Dog2("小黄",3);
console.log(dog2);

       改变了this指向后,让Dog2中的this等于了Animal中的this,实例对象的this也就等于了Animal的this,换句话说,Animal中的this指向new创建的对象

请添加图片描述

这样的继承方式叫冒充继承:通过改变this的指向性实现的继承方法。缺点:这样的方式只继承了属性,没有继承原型,也只是调用了一次属性而已

       通过观察以上两种继承方式可以发现,原型继承不能重置属性但可以继承原型,冒充继承不能继承原型但可以重置属性,所以我们可以结合这两种方法和思路,实现一个完美的继承方式 ——组合继承

组合继承
//创建一个猴子构造函数,实现继承于Animal
function Monkey(name,age){
    //第二步,利用冒充继承改变Monkey内部的this指向,实现属性继承
    Animal.call(this,name,age);
}

//第一步:利用原型继承继承Animal的原型中的内容
Monkey.prototype=new Animal()//不传入参数
//除了继承过来的方法,我们也可以有自己独特的行为
Monkey.prototype.laugh=function(){
    console.log(this.name+"正在嘲笑");
}

//实例化一个猴子
var m1=new Monkey("屁孩",3);
//当然,m1也可以调用继承过来的move
m1.move();//屁孩正在走
m1.laugh()//屁孩正在嘲笑

这样的组合继承法,既继承了属性还继承了原型,这样的最终继承方案算是比较完美了。

注意: 如果要新加入方法或属性,一定要加在继承以后,以防加完之后再继承,会被继承覆盖。


@写在最后:ES6还没有出来之前,以上js原生组合继承的方式算是非常ok的,如果对你有任何启发,那就给本蜗牛来个点赞+关注+收藏,以后的日子一起成长🌹


还有一件事:对于上述this指向问题、原型原型链,还有ES6+的优化,以后的文章中会一一出现,敬请期待

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值