面向对象的思维特点:
面向对象编程首先考虑的是有哪些对象,按照面向对象的思维特点,不断创建对象、使用对象、指挥对象做事情。
面向过程与面向对象对比:
面向过程 | 面向对象 | |
---|---|---|
优点 | 性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。 | 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护 |
缺点 | 不易维护、不易复用、不易扩展 | 性能比面向过程低 |
对象:由属性和方法组成:是一个无序键值对的集合,指的是一个具体的事物。
以下代码是对对象的复习:
//字面量创建对象
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);
在 ES6
中新增加了类的概念,可使用 class
关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class
)对象特指某一个,通过类实例化一个具体的对象。
类 class:泛指某一大类;
对象:特指某一事物(通过类实例化产生的)。
步骤1: 使用class
关键字
class name {
// class body
}
步骤2:使用定义的类创建实例 ,new关键字
var xx = new name();
1.)创建类 class:创建一个明星类
class Star {
// 类的共有属性放到 constructor 里面
constructor(name, age) {
this.name = name;
this.age = age;
}
}
2.) 利用类创建对象 new
var ldh = new Star('刘德华', 18);
console.log(ldh);
以上代码运行结果::
我们可以看出和使用构造函数的结果是一样。
1). 创建类 class 创建一个类
class Star {
// 类的共有属性放到constructor里面,constructor是构造器或者构造函数
constructor(uname, age) {
this.uname = uname;
this.age = age;
}//------------------------------------------->注意,方法与方法之间不需要添加逗号
sing(song) {
console.log(this.uname + '唱' + song);
}
}
所有的 class 类里面不需要加 function
2). 利用类创建对象 new
var ldh = new Star('刘德华', 18);
console.log(ldh); // Star {uname: "刘德华", age: 18}
ldh.sing('冰雨'); // 刘德华唱冰雨
class
关键字创建类, 类名我们还是习惯性定义首字母大写constructor
函数,可以接受传递过来的参数,同时返回实例对象constructor
函数 只要 new
生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数new
不能省略语法规范,
{}
;function
;3)在类中添加方法:直接把函数或者方法写到类里面
class Star {
// 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数
constructor(uname, age) {
this.uname = uname;
this.age = age;
}//------------------------------------------->注意,方法与方法之间不需要添加逗号
sing(song) {
console.log(this.uname + '唱' + song);
}
}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华', 18);
console.log(ldh); // Star {uname: "刘德华", age: 18}
ldh.sing('冰雨'); // 刘德华唱冰雨
在程序中,子类可以继承父类的一些属性和方法
// 父类
class Father{
}
// 子类继承父类
class Son extends Father {
}
class Father {
constructor(surname) {
this.surname= surname;
}
say() {
console.log('你的姓是' + this.surname);
}
}
class Son extends Father{ // 这样子类就继承了父类的属性和方法
}
var damao= new Son('刘');
damao.say(); //结果为 你的姓是刘
//定义了父类
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(x, y); //使用super调用了父类中的构造函数
}
}
var son = new Son(1, 2);
son.sum(); //结果为3
继承的关键字
extends
super
:调用父类中的构造函数或普通函数,可以把constructor()
里的数据传递给父类。
注意:
继承中,如果实例化子类输出一个方法,先看子类本身有没有这个方法,如果有就先执行子类的;
继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则);
如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super
调用父类的构造函数,super
必须在子类this
之前调用。
时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用.。
在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象:
功能需求:
该对象具有:
class Tab {
constructor(){
}
// 1、切换功能
toggle(){}
// 2、添加功能
addTab(){}
// 3、删除功能
removeTab(){}
// 4、修改功能
editTab(){}
};
class Tab {
constructor(id){
// 获取元素
this.main=document.querySelector(id);
this.lis=this.main.querySelectorAll('li');
this.sections=this.main.querySelectorAll('section');
}
init(){
// init 初始化,让页面加载时相关的元素绑定事件
for (var i=0; i< this.lis.length;i++){
this.lis[i].index=i;
this.lis[i].onclick=function(){
}
}
}
// 1、切换功能
toggleTab(){}
// 2、添加功能
addTab(){}
// 3、删除功能
removeTab(){}
// 4、修改功能
editTab(){}
};
new Tab('#tab');
var that;
class Tab {
constructor(id) {
that = this;
// 获取元素
this.main = document.querySelector(id);
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.init();
}
init() {
// init 初始化,让页面加载时相关的元素绑定事件
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].index = i;
this.lis[i].onclick = this.toggleTab;
}
}
// 1、切换功能
toggleTab() {
// console.log(this.index);
that.clearClass();
this.className = 'liactive';
that.sections[this.index].className = 'conactive';
}
clearClass() {
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].className = '';
this.sections[i].className = '';
}
}
// 2、添加功能
addTab() {}
// 3、删除功能
removeTab() {}
// 4、修改功能
editTab() {}
};
new Tab('#tab');
声明了一个全局变量
that
,在constructor
对其进行赋值,以解决this
指向(lis
)问题。
书写了一个利用循环清除样式的clearClass方法。
功能分析:
li
和新的内容section
;第一步:创建新的选项卡li
和新的内容section
;
createElement
。但是元素里内容较多,需要innerHTML
赋值,再appendChild
追加到父元素;insertAdjacentHTML()
可以直接把字符串格式元素添加到父元素中。点击前往 :insertAdjacentHTML方法官方使用说明
appendChild
不支持追加字符串的子元素;
insertAdjacentHTML
支持追加字符串的元素。
// 2、添加功能
addTab() {
that.clearClass();
// 1、创建li元素和section元素;
var random = Math.random();
var li = '新标签页 ';
var section = '测试' + random + '';
// 2、把这两个元素追加到对应的父元素里
that.ul.insertAdjacentHTML('beforeend', li);
that.fsection.insertAdjacentHTML('beforeend', section);
}
注:后添加的li
和section
,不能在页面加载时获取 ,因此会出现后添加的li
和section
没有切换效果,此时就将获取li
元素和section
元素的放到函数updateNode
里,在点击+
号时,重新调用此函数获取元素并绑定onclick
事件,代码如下:
var that;
class Tab {
constructor(id) {
that = this;
// 获取元素
this.main = document.querySelector(id);
this.add = this.main.querySelector('.tabadd');
// 获取 li 的父元素
this.ul = this.main.querySelector('.fisrstnav ul:first-child');
// 获取 section的父元素
this.fsection = this.main.querySelector('.tabscon');
this.init();
}
init() {
this.updateNode();
// init 初始化,让页面加载时相关的元素绑定事件
this.add.onclick = this.addTab;
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].index = i;
this.lis[i].onclick = this.toggleTab;
}
}
// 获取所有的小li 和 section
updateNode() {
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
}
// 1、切换功能
toggleTab() {
// console.log(this.index);
that.clearClass();
this.className = 'liactive';
that.sections[this.index].className = 'conactive';
}
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 = '新标签页 ';
var section = '测试' + random + '';
// 2、把这两个元素追加到对应的父元素里
that.ul.insertAdjacentHTML('beforeend', li);
that.fsection.insertAdjacentHTML('beforeend', section);
that.init();
}
// 3、删除功能
removeTab() {}
// 4、修改功能
editTab() {}
};
new Tab('#tab');
注意:
在tab类的删除方法中处理: 点击x获取对应的索引,注意获取的索引号是当前x号的父元素的索引,要读取parentNode.index
。
因为tab标题本身有点击事件,x号的点击事件会向上传递,需要阻止事件冒泡。
获取所有tab标题的x按钮并为每一个tab标题的x号绑定点击事件(一定要在更新的方法中获取,否则新添加的tab栏获取不到,代码会报错)
// 3、删除功能
removeTab(e) {
e.stopPropagation(); // 阴止冒泡 防止触发li的切换点击事件
var index = this.parentNode.index;
// 根据索引号删除对应的li和section remove() 方法可以直接删除指定的元素
that.lis[index].remove();
that.sections[index].remove();
that.init();
// 如果删除的不是选中状态的li,原来的选中状态li保持不变
if (document.querySelector('.liactive')) return;
// 如果删除了选中状态的列,需要设置它的前一个li为选中状态
index--;
// click 调用点击事件,不需要鼠标触发
that.lis[index] && that.lis[index].click();
}
总结:
remove()
方法可以直接删除指定的元素;1)将索引自减代码
that.lis[index] && that.lis[index].click()
中的&&
符号,表示如果左边为真,才执行右边的代码。
2)if (document.querySelector('.liactive'))
:如果有处于选定状态的列,就return
,下面的代码不再执行。
双击选项卡li
或section
里面的文字,可以实现修改操作。
ondblclick
;核心思路:
双击文字时,在里面生成一个文本框;
当失去焦点或是按下回车,就把文本框的值给原先元素即可。
注:如果双击,会默认选定文字,此时需要双击禁止选中文字。
双击禁止选中文字的代码:
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
1)为元素(标题与内容)绑定双击事件
this.spans[i].ondblclick = this.editTab;
this.sections[i].ondblclick = this.editTab;
文本框失去焦点时,将文本框里的值再赋给span
input.onblur = function() {
this.parentNode.innerHTML = this.value;
}
当敲下回车时,也能够把文本框的值给span
input.onkeyup = function(e) {
if (e.keyCode === 13) {
//手动调用失去焦点事件,不需鼠标离开操作
this.onblur();
}
}
利用了键盘事件里的事件对象。
修改模块的代码:
// 4、修改功能
editTab() {
// 获取原先的内容
var str = this.innerHTML;
// 双击禁止选定文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
this.innerHTML = '';
// 获取文本框
var input = this.children[0];
input.value = str; //将原先span里的文字赋值给文本框
// 让文本框里的文字处于选定状态
input.select();
//文本框失去焦点时,将文本框里的值再赋给span
input.onblur = function() {
this.parentNode.innerHTML = this.value;
}
//敲下回车时,也能够把文本框的值给span
input.onkeyup = function(e) {
if (e.keyCode === 13) {
//手动调用失去焦点事件,不需鼠标离开操作
this.onblur();
}
}
}
至此,已完成Tab栏的切换、增、删、改功能