设计模式入门——面向对象

前言

本文将通过一个选项卡案例带读者了解面向对象是个什么概念,同时对于面向对象的各个知识点不会面面俱到,只是简单地从ES5和ES6去看看面向对象是怎么实现的、同时比较这个和面向过程有什么区别。


面向对象概念

面向对象有三要素:继承封装多态 (可以不用马上理解)

面向对象和面向过程有什么不同呢?
可以这么来理解,面向过程大概就是 你规定了做事情的各种方法;
而面向对象,则是 你规定属于一类的各对象的属性和做事情的方法,通过实例化对象使其与面向过程一样达到相同的目的;

那面向对象相对于面向过程有什么优势呢?
我认为面向对象最大的优势就是“大一统”,同时可以针对不同的特殊情况衍生出相应的变种;

就拿造汽车举个例子
面向过程:你指定一辆汽车装几个轮子、装几个座位、用什么材料的方向盘、用什么材料的灯、底盘有多高等具体参数;

面向对象:你指定一辆汽车有没有轮子、有没有座位、有没有挡风玻璃、有没有底盘,然后你还可以在实例化的时候传参告诉工人这些轮子有几个和用的什么材料、这个座位有几个和用的什么材料、挡风玻璃有几块还有用的什么材料等等;

所以现在你应该能看出来面向对象相对面向过程有什么好处了吧?如果用面向对象可以通过实例化造不同类型的车,但是如果用面向过程,写了一大堆代码最后只能造一种车;所以当考虑到代码的复用性和可拓展性时,就要用到面向对象这个技术了。

当然在js里面,并没有严格意义的面向对象,其本质还是原型链、对象的属性委托。


案例

本文讲的案例非常简单,可以说这是每个学js的都会做过的练习,就是如图的一个选项卡
在这里插入图片描述
在这里插入图片描述
相信大多数人遇到这个需求肯定想都不想就开始获取节点、绑定函数了。。

今天我们的目的是知道面向对象在js中怎么实现的,所以接下来就是要看看这个例子分别用ES5和ES6怎么实现


HTML

<div id="tabs" class="tabs">
   <ul class="tabs-titles">
        <li class="active">itemA</li>
        <li>itemB</li>
        <li>itemC</li>
    </ul>
    <div class="content-container">
        <div class="content">
            the content of itemA
        </div>
        <div class="content" style="display: none">
            the content of itemB
        </div>
        <div class="content" style="display: none">
            the content of itemC
        </div>
    </div>
</div>

ES5

window.onload = function () {
  var tabs = new TabsBar("tabs");
  tabs.init();
};

//去掉Element对象内的所有空白节点
function cleanWhitespace(node){
	var loopIndex;
	for (loopIndex = 0; loopIndex < node.childNodes.length; loopIndex++){
		var currentNode = node.childNodes[loopIndex];
		//如果是元素节点,则遍历该子元素的所有的子节点,用递归检查是否包含白节点
		if (currentNode.nodeType == 1){
				cleanWhitespace(currentNode);
		}
		//如果是文本节点,则检查是否是纯粹的空白节点,如果是,就将它从对象中删除
		if (((/^\s+$/.test(currentNode.nodeValue))) && (currentNode.nodeType == 3)){
			node.removeChild(node.childNodes[loopIndex--]);
		}
	}
} 

function TabsBar(boxId) {
  //公有的属性
  this.boxObj = document.getElementById(boxId);
  cleanWhitespace(this.boxObj);

  //私有的属性
  var _titles= this.boxObj.childNodes[0];
  var _contents= this.boxObj.childNodes[1];
  //公有的方法
  this.init = function(){
      bindTitlesEvent();
  }

  // 私有方法 事件绑定
  function bindTitlesEvent() {
    for (var j = 0; j < _titles.childNodes.length; j++) {
      // console.log(1);
      //第二步 绑定事件  A
      (function (k) {
        _titles.childNodes[k].onclick = function () {
          changeContent(k);
        };
      })(j);
    }
  }
  // 私有方法 显示不同类别的内容
  function changeContent(n) {
    for (var i = 0; i < _titles.childNodes.length; i++) {
      _titles.childNodes[i].className = "";
      _contents.childNodes[i].style.display = "none";
    }
    _titles.childNodes[n].className = "active";
    _contents.childNodes[n].style.display = "block";
  }
}

总结:在ES5中

  • 一个“类”就是一个(构造)函数,通过 new 调用实例化
  • 在构造函数中,通过this定义的属性是公共属性(构造函数本身和实例对象都能访问)、通过声明关键字var来声明的属性是私有属性(只能在构造函数内部访问,实例对象不能访问)
  • 与属性类似,通过this.函数名 = 函数方法得到的函数是公共函数,而通过函数声明function得到的函数是私有函数
  • 有一个不成文的约定,私有属性以下划线_开头
  • 另外一个不成文的约定,构造函数以大写字母开头

补充

  • 如果要声明“类”的静态方法,则通过构造函数的点操作进行声明,如 TabsBar.foo = function() {}
  • 同样,声明“类”的静态属性也是通过构造函数的点操作,
    TabsBar.num = 1
  • 静态方法不能访问构造函数内部 this.变量

了解了什么是静态方法就知道第三点补充的原因了,至于静态属性和静态方法的内容这里不做过多描述,因为与本文主旨没有太大关系,本文重点是了解面向对象、而不是深入探讨一些细节。

ES6

window.onload = function () {
   const tabsObj = new TabsBar('tabs');
    tabsObj.init();
}

//去掉Element对象内的所有空白节点
function cleanWhitespace(node){
	var loopIndex;
	for (loopIndex = 0; loopIndex < node.childNodes.length; loopIndex++){
		var currentNode = node.childNodes[loopIndex];
		//如果是元素节点,则遍历该子元素的所有的子节点,用递归检查是否包含白节点
		if (currentNode.nodeType == 1){
				cleanWhitespace(currentNode);
		}
		//如果是文本节点,则检查是否是纯粹的空白节点,如果是,就将它从对象中删除
		if (((/^\s+$/.test(currentNode.nodeValue))) && (currentNode.nodeType == 3)){
			node.removeChild(node.childNodes[loopIndex--]);
		}
	}
} 

// 私有属性的 Symbol
// const _titles = Symbol('titles');
// const _contents = Symbol('contents');

// 私有方法的 Symbol
const bindTitlesEvent = Symbol('bindTitlesEvent');
const changeContent = Symbol('changeContent');
class TabsBar{
    #titles;
    #contents;
    constructor(id) {
        this.tabsObj = document.getElementById(id);
        cleanWhitespace(this.tabsObj);
        // this[_titles] = this.tabsObj.childNodes[0];
        // this[_contents] = this.tabsObj.childNodes[1];
        this.#titles = this.tabsObj.childNodes[0];
        this.#contents = this.tabsObj.childNodes[1];
    }
    init(){
        this[bindTitlesEvent]();
    }

    // 给头部导航绑定事件
    [bindTitlesEvent](){
        this.#titles.childNodes.forEach((childNode, index)=>{
            childNode.addEventListener('click', ()=>{
                this[changeContent](index);
            })
        })
    }
    // 底部内容变更事件
    [changeContent](index){
        const titleNodes = this.#titles.childNodes;
        const contentNodes = this.#contents.childNodes;
        for (let i = 0; i < contentNodes.length; i++) {
            if( i === index ){
                contentNodes[i].style.display = 'block';
                titleNodes[i].classList.add('active');
            } else{
                contentNodes[i].style.display = 'none';
                titleNodes[i].classList.remove('active');
            }
        }
    }
}

总结:在ES6中

  • 使用class来声明一个"类“
  • 在这里介绍两种声明私有属性的方法
  1. Symbol
    class的外部定义相关的Symbol,然后在class内部通过this[]的方式来定义私有属性
  2. #
    在构造器constructor之前通过#声明一个私有属性,访问时通过this.#属性名来访问
  • ES6声明私有函数的方法和声明私有属性的方法是一样的,不过在本文写作时我做了一下测试,通过 #声明的私有方法在运行时会报错,我猜可能是浏览器问题?不管怎样,大家知道能用Symbol#来声明私有函数就可以了

最后

本来想用最短的篇幅介绍一下面向对象的,写作的过程中已经尽量避免了很多额外的拓展了,但感觉篇幅还是比较长。希望不太明白面向对象的小可爱在读完本文后能对面向对象这个技术有一个比较清晰的认识吧,你们能有所收获就是我写作最大的意义!

喜欢本文的记得点赞、收藏,最好能点下关注喔!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面向对象是一种程序设计的思想,它将程序中的数据和对数据的操作封装在一起,形成对象。对象是的一个实例,定义了对象的属性和行为。在Java中,面向对象的概念包括与对象的关系、封装、构造函数、this关键字、static关键字以及设计模式等方面。 设计模式是在软件设计中常用的解决问题的经验总结,它提供了一套可重用的解决方案。在Java中,单例设计模式是一种常见的设计模式之一,它保证一个只有一个实例,并提供一个全局访问点。通过使用单例设计模式,可以确保在程序中只有一个对象实例被创建,从而节省了系统资源并提高了性能。 通过使用单例设计模式,可以实现以下效果: - 限制一个只能有一个实例。 - 提供一个全局访问点,使其他对象可以方便地访问该实例。 - 保证对象的唯一性,避免多个对象的状态不一致。 在Java中,实现单例设计模式有多种方式,包括饿汉式、懒汉式、双重检测锁等。每种方式都有各自的特点和适用场景,开发者可以根据具体的需求选择合适的实现方式。设计模式是一种通用的解决问题的方法,它可以在面向对象的程序设计中提供灵活、可复用的解决方案。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [计算机后端-Java-Java核心基础-第15章 面向对象07 14. 设计模式与单例设计模式.avi](https://download.csdn.net/download/programxh/85435560)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [完整版Java全套入门培训课件 Java基础 03-面向对象(共18页).pptx](https://download.csdn.net/download/qq_27595745/21440470)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值