1 面向过程与面向对象
1 面向过程
面向过程就是分析出问题所需要的步骤 然后用函数吧这些步骤一步一步实现使用的时候在一个一个的依次调用就可以了
2 面向对象
面向对象时吧事物分解成一个个对象 然后由对象之间分功工与合作
3 面向过程与面向对象对比
面向过程 优点 性能比面向对象高 适合跟硬件联系很紧密的东西 列如单片机就采用的面向过程的编程
缺点 不易维护 不易复用 不易扩展
面向对象
易维护 易复用 易拓展 由于面向对象有封装 继承 多态性的特性 可以设计出低耦合的系统 使系统更灵活 更加易于维护
缺点 性能比面向过程低
二 对象与类
对象
对象时由属性和方法组成的 是一个无需键值对的集合 指的是一个具体的事物
属性 事物的特性 在对象中属性来表示(常用名词)
方法 事物的行为 在对象中用方法来表示(常用动词)
创建对象
//以下代码是对对象的复习
//字面量创建对象
var ldh = {
name: '刘德华',
age: 18
}
console.log(ldh);
//构造函数创建对象
function Star(name, age) {
this.name = name;
this.age = age;
}
var ldh = new Star('刘德华', 18)//实例化对象
console.log(ldh);
如上两行代码运行结果为
{name:"刘德华",age:18} //字面量创建的对象
Star {name:"刘德华",age:18} //构造函数创建的对象
类
在ES6中新增添加了类的概念 可以使用class 关键词声明一个类 之后以这个类来实例化对象 类抽象了对象的公共部分 它泛指某一大类(class) 对象特指某一个 通过类实例化一个具体的对象
创建类
1 语法
//步骤1 使用class关键字
class name {
// class body
}
//步骤2使用定义的类创建实例 注意new关键字
var xx = new name()
1. 示例
// 1. 创建类 class 创建一个 明星类
class Star {
// 类的共有属性放到 constructor 里面
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华', 18);
console.log(ldh);
Star{name:"刘德华",age:18} // 通过class 类创建的对象
.2.2类创建添加属性和方法
// 1. 创建类 class 创建一个类
class Star {
// 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数
constructor(uname, age) {
this.uname = uname;
this.age = age;
}//‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐>注意,方法与方法之间不需
要添加逗号
sing(song) {
console.log(this.uname + '唱' + song);
}
}
Star{uname: "刘德华, age: 18}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华', 18);
console.log(ldh); // Star {uname: "刘德华", age: 18}
ldh.sing('冰雨'); // 刘德华唱冰雨
注意
1 通过calss 关键字创建类 类名我们还是习惯性定义首字母大写
2 类里面有个constructor函数 可以接受传递过来的参数 同时返回实例对象
3 constructor函数 只要 new 生成实例时 就会自动调用这个函数 如果我们不写这个函数,类也会自动生成这个函数
4 多个函数方法之间不需要添加逗号分隔
5 生成实例new 不能省略
6 语法规范 创建类 类名后面不要加小括号 生成实例 类名后面加小括号 构造函数不需要加function
2.3 类的继承
语法
// 父类
class Father {
}
// 子类继承父级
class Son extends Father{
}
1 试例
class Father {
constructor(surname) {
this.surname= surname;
}
say() {
console.log('你的姓是' + this.surname);
}
}
class Son extends Father{ // 这样子类就继承了父类的属性和方法
}
var damao= new Son('刘');
damao.say(); //结果为 你的姓是刘
// 以上代码的运行结果
你的性是 刘 子类继承了父类的属性和方法
类使用super关键字访问父类的方法
// 定义了父类
class Father {
constructor(x,y) {
this.x = x
this.y = y
}
sum(){
console.log(this.x + this.y)
}
}
// 子元素继承父类
class Son extends Father {
constructor(x,u) {
super(x,y) // 使用super 调用了父类的构造函数
}
}
var son = new Son(1,2)
Son.sum() // 结果为3
注意:
1 继承中 如果实例化子类输出一个方法 先看子类有没有这个方法 如果有就先执行子类的
2 继承中 如果子类里面没有 就去查找父类有没有这个方法 如果有 就执行父类的这个方法(就近原则)
3 如果子类想要继父类的方法 同时在自己内部阔债自己的方法 利用 super 调用 父类的构造函数 super 必须在子类this 之前调用
// 父类有加法方法
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
// 子类继承父类加法方法 同时 扩展减法方法
class Son extends Father {
constructor(x, y) {
// 利用super 调用父类的构造函数 super 必须在子类this之前调用,放到
this之后会报错
super(x, y);
this.x = x;
this.y = y;
}
subtract() {
console.log(this.x ‐ this.y);
}
}
var son = new Son(5, 3);
son.subtract(); //2
son.sum();//8
4 时刻注意 this的指向问题 类里面的共有属性和方法 一定要加this 使用
1 constructor 中的this 指向的是new 出来的实例对象
2 自定义方法 一般也只想new 出来的实例对象
3 绑定事件之后this 指向的就是触发事件的事件源
5 在ES6中类没有变量提升 所以必须先定义类 才能通过类实例化对象
三 面向对象版tab栏切换
1 功能需求
1 点击tab 栏可以切换效果
2 点击+好 可以添加tba项和内容项
3 点击X好 可以删除当前的tba项和内容向
4 双加tab 项文字或者内容项文字可以修改里面的文字内容
3.2案例准备 1. 获取到标题元素 2. 获取到内容元素 3. 获取到删除的小按钮 x号 4. 新建js文件,定义类,添加需要的属性方法(切换,删除,增加,修改) 5. 时刻注意this的指向问题
3.3切换 为获取到的标题绑定点击事件,展示对应的内容区域,存储对应的索引
this.lis[i].index = i;
this.lis[i].onclick = this.toggleTab;
使用排他,实现只有一个元素的显示
toggleTab() {
//将所有的标题与内容类样式全部移除
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].className = '';
this.sections[i].className = '';
}
//为当前的标题添加激活样式
this.className = 'liactive';
//为当前的内容添加激活样式
that.sections[this.index].className = 'conactive';
}
3.4 添加
为添加按钮 + 绑定点击事件
this.add.onclick = this.addTab
实现标题与内容的添加 做好排他处理
addTab() {
that.clearClass();
// (1) 创建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>';
// (2) 把这两个元素追加到对应的父元素里面
that.ul.insertAdjacentHTML('beforeend', li);
that.fsection.insertAdjacentHTML('beforeend', section);
that.init();
}
3.5删除
// 为元素的删除按钮 x 绑定点击事件
this.remove[i].onclick = this.removeTab
获取到点击的删除按钮的所在的父元素的所有,删除对应的标题与内容
removeTab(e) {
e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件
var index = this.parentNode.index;
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();
}
编辑
为元素(标配与内容)绑定双击事件
this.spans[i].ondblclick = this.editTab
this.spctions[i].ondblclick = this.editTab
在双击事件处理文本选中状态,修改内部DOM节点,实现新旧value值的传递
editTab() {
var str = this.innerHTML;
// 双击禁止选定文字
window.getSelection ? window.getSelection().removeAllRanges() :
document.selection.empty();
// alert(11);
this.innerHTML = '<input type="text" />';
var input = this.children[0];
input.value = str;
input.select(); // 文本框里面的文字处于选定状态
// 当我们离开文本框就把文本框里面的值给span
input.onblur = function() {
this.parentNode.innerHTML = this.value;
};
// 按下回车也可以把文本框里面的值给span
input.onkeyup = function(e) {
if (e.keyCode === 13) {
// 手动调用表单失去焦点事件 不需要鼠标离开操作
this.blur();
}
}
}
// 全代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面向对象 Tab</title>
<style>
@font-face {
font-family: "iconfont";
src: url('./iconfont/iconfont.eot?t=1553960438096');
/* IE9 */
src: url('./iconfont/iconfont.eot?t=1553960438096#iefix') format('embedded-opentype'),
/* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAK4AAsAAAAABmwAAAJrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCcAp4fwE2AiQDCAsGAAQgBYRtBzAbpQXIrrApw71oi3CCOyzEy8RvE4yIN8TD036/zp03qCYRjaJZNBFFS/gREoRGipQKofjuNrb+9XbTqrmXcqWzfTRDqFqWkhAJzYToaE6LQ7Q30CirRqSKMnj58DdIdrNAdhoTQJa5VGfLrtiAy+lPoAcZdUC57UljTR4TMAo4oL0xiqwYG8YueIHPCdTqYajty/t+bUpmrwvEnUK42lQhLMssVy1UNhzN4kmF6vSQVvMY/T5+HEU1SUXBbti7uBBrx++cgqJULp0GhAgBna5AgSkgE0eN6R1NwTitNt0yAI5VG7wr/8AljmoX7K+zq+tBF1Q8k9JTPWp1AjnJDgCzmM3bU0V31dsvV3M2eC6fHjaGfX/qS7U5Gr58vj6uD0bgxudyrV/OtHHyP+NZnpO1txbktjdY+3FB61+7nxeOzq8niGYnRwT3v3aZxeXf6rrNxl5//49WlEtZUUL1Pj3Bv1EO7MuG2namrCkbvcnApLUJtWpRhv2tzlRLx43kQ7WO2/FW6c5QqDZEZnYKFeosoVK1NdSa5E/XaVM1Ra7BhAEQmk0kjV5QaLbIzG5U6HRRqTkK1DqJtivrjMT1zJaNnIsihAiyQE3JdbszcW0Xiadzdl4d8UO0HSUGNDNXzl2hifYSO5pPjrorgdjUAAavoa5TKDZVUXD3kuuOOzh70fShvUiN2owtNsRxIREIIiATUCYpGO2aqXy/CxEeHcfuaKrLDiGbQ5kcEMsNIK8M5qCmR3mn8RFHOpcECBtlAAwWIZ2OAqV5kQoJXHvShORYBzrDZKhhb3uT8QPlrA3bmsKZV6i89DiTV2o1AAAA') format('woff2'),
url('./iconfont/iconfont.woff?t=1553960438096') format('woff'),
url('./iconfont/iconfont.ttf?t=1553960438096') format('truetype'),
/* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('./iconfont/iconfont.svg?t=1553960438096#iconfont') format('svg');
/* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-guanbi:before {
content: "\e676";
}
* {
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 #fff;
z-index: 9;
}
#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;
}
</style>
</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>
window.addEventListener('load', function () {
var that;
class Tab {
constructor(selector) {
// 获取元素
that = this
this.main = document.querySelector(selector)
this.add = this.main.querySelector('.tabadd')
// li 的父元素
this.ul = this.main.querySelector('.fisrstnav ul:first-child')
this.tabscon = this.main.querySelector('.tabscon')
this.init()
}
init() {
// init 初始化操作 让相关的元素绑定事件
// 绑定tab标签页点击事件
this.updateNode() // 在绑定事件之前重新获取页面的lis 和section
this.add.onclick = this.addTab
for (var i = 0; i < this.lis.length; i++) {
// 给每个li添加索引属性
this.lis[i].index = i;
// 绑定切换事件
this.lis[i].onclick = that.toggleTab;
// this.lis[i].onclick = function () {
// console.log(this.index);
// }
this.remove[i].onclick = this.removeTab
this.spans[i].ondblclick = this.editTab
this.sections[i].ondblclick = this.editTab
this.spans[i].addEventListener('selectstart', function (e) {
e.preventDefault() // 双击禁止选中文字
})
this.sections[i].addEventListener('selectstart', function (e) {
e.preventDefault() // 双击禁止选中文字
})
}
}
// 因为动态添加元素 需要重新获取对应的元素
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('.fisrstnav li span:first-child')
}
//1 切换功能
toggleTab() {
// console.log(this.index);
that.clearClass() // 谁调用指向谁 that是constructor 里面的this 当前this 是当前对应的小li
this.className = 'liactive'; // 当前this指向lis[当前点击的li] 给当前点击的li设置类
that.sections[this.index].className = 'conactive'; // 给对应大盒子里面的sections 设置类 that指向 constructor this this index = 等于当前点击小li的索引
}
// 清除所有li和section类
clearClass() {
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].className = ''
this.sections[i].className = '';
}
}
// 2 添加功能
addTab() {
that.clearClass()
// 1 创建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>`
// 2 吧这两个元素追加到对应的父元素里面
that.ul.insertAdjacentHTML('beforeend', li)// 因为是add 这个按钮引用了这个函数此时this指向指为当前按钮
that.tabscon.insertAdjacentHTML('beforeend', section)// 因为是add 这个按钮引用了这个函数此时this指向指为当前按钮
that.init() //重新绑定事件
}
// 3 删除功能
removeTab(e) {
e.stopPropagation() // 阻止冒泡 防止触发li 的点击事件
var index = this.parentNode.index
console.log(index);
this.parentNode.remove()
that.sections[index].remove()
that.init()
// 当我们删除了选中状态的这个li的时候让他的前一个li 处于选定转台
// that.sections[index-1].className = 'conactive'
// 如果当前已经激活的tab 就不用激活删除的前一个 直接return
if (document.querySelector('.liactive')) return
// 如果能找到这个小li就会执行后面的话 否则不执行
index--
that.lis[index] && that.lis[index].click()
}
// 4 修改功能
editTab() {
if (document.querySelector('input')) return
//1 先拿到span的内容
var str = this.innerHTML
// 2 创建一个输入框
// 3 将span的内容修改成输入框
this.innerHTML = `<input type="text" />`
var inp = this.children[0]
// 4 吧第一步获取的值赋值到输入框中
inp.value = str
// 5 选中输入框内的文字
inp.select()
// 6 输入框失去焦点时 将输入框的内容赋值给span
inp.onblur = function () {
this.parentNode.innerHTML = this.value
}
// 按下回车也可以吧文本框里面的值给span
// 敲下回车进行失去和焦点事件
inp.onkeyup = function (e) {
if (e.keyCode === 13) {
// 按下回车手动调用回车键
this.blur()
}
}
}
}
var tab = new Tab('#tab')
})
</script>
</body>
</html>