JavaScript面向对象
- 1 本节目标
- 2 面向对象编程介绍
- 2.1 两大编程思想
- 2.2 面向过程编程POP
- 2.3 面向对象编程OOP
- 2.4 面向过程和面向对象的对比
- 3 ES6中的类和对象
- 3.1 对象
- 3.2 类class
- 3.3 创建类
- 3.4 类constructor构造函数
- 3.5 类添加方法
- 3.6 三个注意点
- 4 类的继承
- 4.1 继承
- 4.2 super关键字
- 5 面向对象案例——tab栏切换
1 本节目标
- 说出什么是面向对象
- 说出类和对象的关系
- 使用class创建自定义类
- 说出什么是继承
2 面向对象编程介绍
2.1 两大编程思想
- 面向过程
- 面向对象
2.2 面向过程编程POP
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
- 面向过程就是按照我们分析好了的步骤,按照步骤解决问题。
2.3 面向对象编程OOP
- 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
- 面向对象是以对象功能来划分问题,而不是步骤。
- 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
- 面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。
- 面向对象的特性:封装性、继承性、多态性。
2.4 面向过程和面向对象的对比
3 ES6中的类和对象
- 面向对象更贴近我们的实际生活,可使用面向对象描述现实世界事物,但事物分为具体的事物和抽象的事物。
- 面向对象的思维特点:
1> 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板);
2> 对类进行实例化,获取类的对象。 - 面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情。
3.1 对象
- 现实生活中,对象是一个具体的事物。
- 在JavaScript中,对象是一组无序相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
- 对象是由属性和方法组成的:
1> 属性:事物的特征,在对象中用属性来表示(常用名词)
2> 方法:事物的行为,在对象中用方法来表示(常用动词)
3.2 类class
- 在ES6中新增加了类的概念,可使用class关键字声明一个类,之后以这个类来实例化对象。
- 类抽象了对象的公共部分,它泛指某一大类(class)。
- 对象特指某一个,通过实例化一个具体的对象。
3.3 创建类
- 语法:
class name {
//class body
}
- 创建实例:
var xx = new name();
- 注意:类必须使用new实例化对象。
3.4 类constructor构造函数
constructor()
方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法,如果没有显示定义,类内部会自动给我们创建一个constructor()
。
- 注意:
1> 通过class关键字创建类,类名我们还是习惯性定义首字母大写
2> 类里面有一个constructor()
函数,可以接受传递过来的参数,同时返回实例对象
3>constructor()
函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
4> 生成实例new不能省略
5> 最后注意语法规范,创建类:类名后面不要加小括号,生成实例:类名后面加小括号,构造函数不需要加function
3.5 类添加方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类中添加方法</title>
</head>
<body>
<script>
// 1.创建类class 创建一个明星类
class Star {
// 类的共有属性放到constructor里面
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
sing(song) {
// console.log('我唱歌');
console.log(this.uname + song);
}
}
// 2.利用类创建对象 new
var ldh = new Star('刘德华',18);
var zxy = new Star('张学友',20);
// console.log(ldh);
// console.log(zxy);
ldh.sing('冰雨');
zxy.sing('李香兰');
// (1)类里面所有的函数不需要写function()
// (2)多个函数或方法之间不需要添加逗号分隔
</script>
</body>
</html>
3.6 三个注意点
- 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象。
- 类里面共有的属性和方法一定要加this使用。
- 类里面的this指向问题。
4 类的继承
4.1 继承
- 程序中的继承:子类可以继承父类的一些属性和方法。
- 语法:
class Father {
// 父类
}
class Son extends Father {
// 子类继承父类
}
4.2 super关键字
super
关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数。
- 注意:子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,再使用子类的构造方法)。
5 面向对象案例——tab栏切换
- tab.html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>面向对象的tab栏切换</title>
<link rel="stylesheet" href="./styles/tab.css">
<link rel="stylesheet" href="./fonts/iconfont.css">
</head>
<body>
<main>
<h4>
Js 面向对象 动态添加标签页
</h4>
<div class="tabsbox" id="tab">
<!-- tab 标签 -->
<nav class="fisrstnav">
<!-- 左侧 -->
<ul>
<li class="liactive"><span>测试1</span><span class="iconfont icon-guanbi"></span></li>
<li><span>测试2</span><span class="iconfont icon-guanbi"></span></li>
<li><span>测试3</span><span class="iconfont icon-guanbi"></span></li>
</ul>
<!-- 右侧 -->
<div class="tabadd">
<span>+</span>
</div>
</nav>
<!-- tab 内容 -->
<div class="tabscon">
<section class="conactive">测试1</section>
<section>测试2</section>
<section>测试3</section>
</div>
</div>
</main>
<script src="js/tab.js"></script>
</body>
</html>
- tab.css代码:
- {
margin: 0;
padding: 0;
}
ul li {
list-style: none;
}
main {
width: 960px;
height: 500px;
border-radius: 10px;
margin: 50px auto;
}
main h4 {
height: 100px;
line-height: 100px;
text-align: center;
}
.tabsbox {
width: 900px;
margin: 0 auto;
height: 400px;
border: 1px solid lightsalmon;
position: relative;
}
nav ul {
overflow: hidden;
}
nav ul li {
float: left;
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
border-right: 1px solid #ccc;
position: relative;
}
nav ul li.liactive {
border-bottom: 2px solid #ffffff;
z-index: 999;
}
#tab input {
width: 80%;
height: 60%;
}
nav ul li span:last-child {
position: absolute;
user-select: none; /* 控制用户不能选中文本 */
font-size: 12px;
top: -18px;
right: 0;
display: inline-block;
height: 20px;
}
.tabadd {
position: absolute;
/* width: 100px; */
top: 0;
right: 0;
}
.tabadd span {
display: block;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
border: 1px solid #ccc;
float: right;
margin: 10px;
user-select: none;
}
.tabscon {
width: 100%;
height: 300px;
position: absolute;
padding: 30px;
top: 50px;
left: 0px;
box-sizing: border-box;
border-top: 1px solid #ccc;
}
.tabscon section,
.tabscon section.conactive {
display: none;
width: 100%;
height: 100%;
}
.tabscon section.conactive {
display: block;
}
功能需求:
1.点击tab栏,可以切换效果
2.点击+号,可以添加tab项和内容项
1>第一步:创建新的选项卡li和新的内容section
2>第二步:把创建的两个元素追加到对应的父元素中
3>以前的做法:动态创建元素createElement,但元素里面内容较多,需要innerHTML赋值,在appendChild追加到父元素里面
4>现在高级做法:利用insertAdjacentHTML()可以直接把字符串格式元素添加到父元素中
5>appendChild不支持追加字符串的子元素,insertAdjacentHTML支持追加字符串的元素
3.点击×号,可以删除当前的tab项和内容项
1>×是没有索引号的,但是他的父亲li有索引号,这个索引号正是我们想要的索引号
2>所以核心思路是:点击×号可以删除这个索引号对应的li和section
4.双击tab项文字或者内容项文字,可以修改里面的文字内容
1>双击事件:ondblclick
2>如果双击文字,会默认选定文字,此时需要双击禁止选中文字:window.getSelection?window.getSelection().removeAllRanges():document.selection.empty();
3>核心思路:双击文字的时候,在里面生成一个文本框,当失去焦点或者按下回车然后把文本框输入的值给原先元素即可。
抽象对象:Tab对象
1.该对象有切换功能
2.该对象有添加功能
3.该对象有删除功能
4该对象有修改功能
- tab.js:
// 声明全局变量that
var that;
class Tab {
constructor(id) {
// 获取元素
that = this;
// 获取大盒子
this.main = document.querySelector(id);
// 获取的是一开始准备好的li和section 不包含后来添加的
/* this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section'); */
// 获取+号
this.add = this.main.querySelector('.tabadd');
// 获取×号 应写进updateNode()里面 因为update会更新,只要新增一个关闭按钮,就会重新获取一次
// this.remove = this.main.querySelectorAll('.icon-guanbi');
// 获取li的父元素
this.ul = this.main.querySelector('.firstnav ul:first-child');
// 获取section的父元素
this.fsection = this.main.querySelector('.tabscon');
this.init();
}
// init() 初始化操作,让相关的元素绑定事件
init() {
this.updateNode();
for(var i = 0; i < this.lis.length; i++) {
this.lis[i].index = i; // 给每个小li都添加了一个属性index,里面存着当前li的索引号
this.lis[i].onclick = this.toggleTab;
// 每个小li都有一个删除按钮
this.remove[i].onclick = this.removeTab;
this.spans[i].ondblclick = this.editTab;
this.sections[i].ondblclick = this.editTab;
}
// 点击按钮 只有一个 不需要循环
this.add.onclick = this.addTab;
}
// 获取所有的li和section和remove还有span
updateNode() {
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.remove = this.main.querySelectorAll('.icon-guanbi');
this.spans = this.main.querySelectorAll('.firstnav li span:first-child');
}
// 1.切换功能
toggleTab() {
// 排他思想
that.clearClass();
this.className = 'liactive'; //点击哪个li li具有liactive类样式
that.sections[this.index].className = 'conactive'; //点击哪个li section具有conactive类样式
}
// 清除所有li和section类的样式
clearClass() {
for(var i = 0; i < this.lis.length; i++) {
this.lis[i].className = ''; // 未被点击的li清除liactive类样式
this.sections[i].className = ''; // 未被点击的section清除conactive类样式
}
}
// 2.添加功能
addTab() {
// 排他思想
that.clearClass();
// 创建li元素和section元素
var random = Math.random(); //生成一个随机数
var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>';
var section = '<section class="conactive">测试'+ random +'</section>';
// 把这两个元素追加到对应的父元素里面
that.ul.insertAdjacentHTML('beforeend', li);
that.fsection.insertAdjacentHTML('beforeend',section);
that.init();
}
// 3.删除功能
removeTab(e) {
e.stopPropagation(); // 阻止冒泡 防止触发li的切换点击事件
var index = this.parentNode.index; // 错号的索引号等于他父亲li的索引号
console.log(index);
// 根据索引号删除对应的li和section remove()方法可以直接删除指定的元素
that.lis[index].remove();
that.sections[index].remove();
that.init();
// 当我们删除了不是选中状态的li的时候,原来处于选定状态的li保持不变
if(document.querySelector('.liactive')) return;
// 当我们删除了选中状态的li的时候,让它的前一个li处于选定状态
index--;
// 如果前面为真 才会执行后面的 如果前面为假 则不再继续执行
that.lis[index] && that.lis[index].click(); // click()手动调用点击事件,不需要鼠标触发
}
// 4.修改功能
editTab() {
var str = this.innerHTML;
// 双击禁止选中文字
window.getSelection?window.getSelection().removeAllRanges():document.selection.empty();
// 双击后添加文本框
this.innerHTML = '<input type="text" />';
// 把原先的内容赋值给文本框
var input = this.children[0]; // span的第一个孩子
input.value = str;
input.select(); // 让文本框里面的文字处于选定状态
// 当我们离开文本框就把文本框里面的值给span
input.onblur = function() {
// 此时span是input的父亲
this.parentNode.innerHTML = this.value;
}
// 按下回车也可以把文本框里面的值给span
input.onkeyup = function(e) {
if(e.key === 'Enter') {
// 手动调用表单失去焦点事件 不需要鼠标离开操作
this.blur();
}
}
}
}
new Tab('#tab');
- 效果图: