主要内容:
面对过程 vs 面向对象 理解
js 对象及类(ES6) *
案例:面向对象版 tab 栏切换
面型过程 vs 面向对象
问题:
- 从宏观上看,编程思想有哪两种?
- 面向过程专注于什么?面向对象呢?
- 面向对象编程有哪些核心特性?
- 思考:面向对象和面向过程之间是完全无关的么?
回答:
- 面向过程及面向对象
- 面向过程专注于解决问题的步骤,面向对象专注于问题中涉及到的对象
- 封装 继承 多态
- 面向对象和面向过程是彼此关联的,面向对象中对象方法实现某些目标仍然需要基于具体的步骤
JS 对象及类(ES6)
问题:
- 对象和类之间是什么关系?
回答:
- 类是多个具备相同属性的对象的模板,对象是类的实例
ES6 类的定义及实例化
问题:
- ES6 如何定义类?如何实例化?
- 回忆 js 对象创建有几种方法?ES5 用什么充当类?
回答:
// ES6 定义类格式 类名首字母要求大小class 类名{constructor(参数1,参数2){this.属性名1 = 参数1}方法1(){...}方法2(){...}}class Stu{constructor(id,name){this.id = id;this.name = name;}study(){alert('学习')}}var stu1 = new Stu('001','jim') // 必须有new 没有new时会报错console.log(stu1.name)stu1.study()
补充:
- ES6 类的本质 就是个函数
- 类的另一种定义方式 var C = class {…}
- 静态方法
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
// 通过static关键字定义静态方法,通过类名直接调用,类似Array.isArray() Date.now()
static test(){alert('test')}
}
Father.test()
ES6 类的继承
问题:
- ES6 如何实现继承?
- 思考:继承有什么好处?
答:
- extends
- 方便代码复用
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
say(){
console.log('father')
}
sum() {
console.log(this.x + this.y);
}
// 通过static关键字定义静态方法,通过类名直接调用,类似Array.isArray() Date.now()
static test(){alert('test')}
}
class Son extends Father{
}
var s1 = new Son(1,2)
s1.sum()//s1自动继承父类的所有方法
super 关键字及使用注意事项
问题:
- 用一句话描述 super 关键字的功能。
- super 关键字有几种用法?
- 在构造函数中使用 super 方法时要注意什么?
答:
- 调用父类方法
- a super()调用父类的构造函数 b,super.方法名() 调用父类的普通方法
- 要放在this的前面,若子类定义了自己的constructor则在该constructor 中必须调用super,若未定义自己的constructor时则不需调用
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
say(){
console.log('father')
}
sum() {
console.log(this.x + this.y);
}
// 通过static关键字定义静态方法,通过类名直接调用,类似Array.isArray() Date.now()
static test(){alert('test')}
}
class Son extends Father{
constructor(x,y,xxx){
super(x,y)
this.xxx = xxx;
}
calc(){
this.sum() //子类中可通过this调用父类方法
}
say(){
super.say() //子类中通过super调用父类同名方法
console.log('son')
}
}
var s1 = new Son(1,2,3)
s1.sum()//s1自动继承父类的所有方法
s1.say()
ES6 类使用时注意事项
问题:
- ES6 使用类时有哪些注意事项?
答:
a 必须先定义类再实例化
b 类中所有的实例属性及方法在使用时都必须通过this.属性名/方法名() 的方式
c 类中this的指向仍然遵循”谁调用指向谁”
d 不能重复定义
案例(面向对象 tab 栏切换)
需求:
1 点击li实现tab栏切换效果
2 点击每个li中的删除按钮,删除tab及对应content
3 点击 加号 动态添加li及对应的content且对应的li及其删除按钮具备功能
4 双击li中文字实现编辑
思路(面向对象):
1 封装类 可以方便地 new出多个选项卡
2 在构造函数中设置相关的各种属性(属性值为各种元素)并为各种元素绑定事件
3 设置切换、删除、新增、编辑等四个方法实现对应功能
注意:this指向的变化
var that;
class Tab {
constructor(selector) {
// 将选项卡的核心部件定义为公共属性
that = this;
this.main = document.querySelector(selector);
// getElementsByTagName获取到的伪数组会自动根据dom的变化而变化
this.tabs = this.main.getElementsByTagName('li');
this.contents = this.main.getElementsByTagName('section');
//获取关闭按钮
this.rmBtns = this.main.getElementsByClassName('icon-guanbi');
// 获取tab文字span
this.spans = this.main.getElementsByClassName('text');
this.addBtn = this.main.querySelector('.tabadd');
// li的父级
this.tabsParent = this.main.querySelector('ul');
// section的父级
this.contentsParent = this.main.querySelector('.tabscon');
// 初始化,绑定事件
this.init();
}
init() {
this.addBtn.onclick = this.add;
for (var i = 0; i < this.tabs.length; i++) {
this.tabs[i].index = i;
this.tabs[i].onclick = this.toggle;
//关闭按钮
this.rmBtns[i].onclick = this.remove;
//tab文字
this.spans[i].ondblclick = this.edit;
//编辑contents
this.contents[i].ondblclick = this.edit;
}
}
// 切换
toggle() {
// 此处this指被click的li
console.log(this.index);
// 清除所有li及section的类
that.clearClass();
// 为当前li及对应的section加上相关 类
this.className = 'liactive';
that.contents[this.index].className = 'conactive';
}
// 添加
add() {
// 新增li及section并使其处于选中状态
// 取消其他li及section的选中状态
that.clearClass();
// 新增
var li = `<li class="liactive">
<span class="text">新增的${Math.random().toFixed(
3
)}</span><span class="iconfont icon-guanbi"></span>
</li>`;
that.tabsParent.insertAdjacentHTML('beforeend', li);
var section = `<section class="conactive">测试1${Math.random()}</section>`;
that.contentsParent.insertAdjacentHTML('beforeend', section);
that.init();
}
// 删除
remove(e) {
e.stopPropagation();
console.log(this.parentNode.index);
var index = this.parentNode.index;
// 删除 index对应的li及section
// that.lis[index].remove()
this.parentNode.remove();
that.contents[index].remove();
// 重新初始化
that.init();
if (that.main.querySelector('.liactive')) return;
//删除的是被选中的li 让上一个 索引为index-1 处于被选中状态
if (index == 0 && that.tabs.length > 0) {
that.tabs[0].click();
} else {
index--;
that.tabs[index] && that.tabs[index].click();
}
}
// 编辑
edit() {
// 阻止默认选中文字
window.getSelection
? window.getSelection().removeAllRanges()
: document.selection.empty();
// 将内容替换为input框
var txt = this.innerHTML;
this.innerHTML = `<input type="text" value="${txt}" />`;
var inp = this.children[0];
// select方法选中文本框中文字
inp.select();
// inp失去焦点时,将其value赋值给span的innerHTML
inp.onblur = function () {
if (this.value !== '') {
this.parentNode.innerHTML = this.value;
} else {
this.parentNode.innerHTML = txt;
}
};
// 若编辑后按下enter键同样有上述blur效果
inp.onkeyup = function (e) {
if (e.keyCode === 13) {
this.blur();
}
};
}
// 清空所有类
clearClass() {
for (var i = 0; i < that.tabs.length; i++) {
that.tabs[i].className = '';
that.contents[i].className = '';
}
}
}
var t = new Tab('#tab');
