一、面向对象
1.解释什么是面向过程,什么是面向对象
面向过程(POP):就是分析解决问题所需要的步骤,然后把函数这些步骤一步一步实现,使用的时候再一个一个依次调用
面向对象(OOP)是把事物分解成一个个对象,然后由对象之间分工与合作。
大象(对象):进去
冰箱(对象):打开+关闭
- 面向对象开发思想中,每一个对象都是功能中心,具有明确分工。
- 面向对象编程具有灵活,代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目
面向对象的特点:
- 封装性
- 继承性
- 多态性
2.面向过程和面向对象的区别
用面向过程的方法写出的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭
二、ES6中的类和对象
面向对象:更贴近实际生活,使用面向对象描述显示实际事务,但是事务分为具体的事务和抽象的事务
手机:
- 抽象的(泛指)
- 具体的手机(特指)
面向对象的思维特点:
-
抽取对象公用的属性和行为组织成一个类(模板)
-
对类进行实例化,获取类的对象
对象
显示生活中,万物皆对象,对象就是一个具体的实物,看得见摸得着的实物
js中:对象是一组无序的相关属性和方法的集合,所有的事物都是对象
- 属性:事物的特征
- 方法:事物的行为
类class
ES6中新增了类的概念,是class关键字声名一个类,之后以这个类来实例化对象
- 类抽取了对象的公共部分:泛指某一大类
- 对象:特指某一个,通过实例化一个具体的对象
面向对象的思维特点
- 抽取对象的公用属性和行为组织成一个类
- 对类进行实例化可以获取类的对象
1. 简单创建类
- 首先创建一个类
- 创建好类之后可以进行实例化操作(创建实例对象)
- 类里面的函数不需要写function中
- 多个函数或者方法之间不需要添加逗号
<script>
// 1.创建类class 创建一个类
class Star {
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
sing(song) {
console.log("唱" + song + "歌");
//可以直接在类中使用实例化的对象
console.log(this.uname + song);
}
}
// 2.利用类实例化创建对象
var ldh = new Star("刘德华", 18); //创建实例
var zxy = new Star("张学友", 19); //创建实例
console.log(ldh); //生成实例化对象
console.log(zxy.uname);
ldh.sing("冰雨");
//3. 类里面的所有函数不需要写function(直接语义化操作)
//4.多个函数或者方法之间不需要添加逗号
</script>
2. 解释constructor-构造函数
用于传递参数,返回实例对象
3.类中添加方法
- 可以直接在类中调用实例化后的对象
sing(song) {
console.log("唱" + song + "歌");
console.log(this.uname + song); //可以直接在类中使用实例化的对象
}
//将此方法写入到类中直接使用实例化后的对象
三、类的使用和继承关系的理解
1.继承关键字:extends
- 创建父类
- 子类继承父类(语义化)
- 然后进行实例化子类进行直接操作
class Father {
constructor() {}
money() {
console.log(100);
}
}
class Son extends Father {}
var son = new Son();
注意:实例化后只有父有参数,只有父类存在方法,子类可以使用自己的参数调用父类的方法(相当于完全继承)
son.money();
2. 父子都有参数,子使用父类的方法(不完全继承)
问题:当父子都存在构造函数但是只有父类存在方法:此时使用子类参数实例化对象调用父类方法就会报错
解决方法:使用Super关键字:将不完全继承变成完全继承
将子类参数构造函数调用父类里的构造函数,也可以调用父类的普通函数
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
//此处将下面的构造函数利用super关键字就可以传递上来使用父类的构造函数。然后就可以使用父类的方法
}
sum() {
console.log(this.x + this.y);
}
}
// 继承
class Son extends Father {
constructor(x, y) {
// 解决调用出错:super
// this.x = x;
// this.y = y;
super(x, y); //结局:调用了父类的构造函数
}
}
var son = new Son(1, 2);
var son1 = new Son(11, 22);
son.sum(); //直接使用son实例的构造函数会报错,应该使用关键字将子类的参数继承到父类参数
son1.sum();
3.继承性调用父子的函数:使用super关键字
此处调用子类的函数,然后就会按照就近原则依次查找父子之间存在的相同的函数
继承中的属性或者方法查找原则:就近原则
- 当继承父类之后先查找子类是否存在方法
- 如果子类不存在就直接查找父类
- 这个也叫做就近原则
class Father {
say() {
return "我是爸爸";
}
}
class Son extends Father {
say() {
console.log(super.say() + "的儿子");
}
}
var son = new Son();
son.say(); //就近原则:依次查找
//先查找调用类,然后再查找调用类的父类(注意使用调用类腰添加super关键字)
4. 子类单独创建方法之后既可以调用父类的函数又可以调用子类的函数
注意:
- 首先创建实例化对象,子类构造函数中继承到父类构造函数并且自己也得存在构造函数
- 然后就可以即调用子类的函数也可以调用父类的构造函数
<script>
// 父类有加法方法
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(x, y);
// 以下的this指向的是子类的构造函数实例化
this.x = x;
this.y = y;
}
//单独创建一个方法
subtract() {
console.log(this.x - this.y);
}
}
var son = new Son(3, 5);
son.subtract();//-2
son.sum();//8
</script>
5. 类和对象的使用注意事项
注意事项:
- 在es6中类没有变量提升,必须先定义好类再进行实例化创建
- 类里面的共有属性和方法必须添加this使用
- 想要在实例化对象时候就可以调用方法
- this指向问题
- constructor里的this指向实例化对象
- 方法中:谁调用this指向谁
<body>
<button>点击</button>
<script>
//1. 问题:如果在定义好类之前进行实例化就会出现报错
//2. cosntructor里面的this指向的就是实例化对象
//3. 需要在创建好类之后再进行实例化
//4. 类里面的共有属性和方法必须添加this使用
// var ldh = new Father();
// --------------------------
var that;
var _that;
class Star {
constructor(uname, age) {
that = this;
console.log(this);
this.uanme = uname;
this.age = age;
this.btn = document.querySelector("button");
this.btn.onclick = this.sing;
//不添加方法括号:实现的是点击之后再进行调用函数
}
// 此处的sing调用者是btn,此时里面的this指向的就是btn
sing() {
console.log(this); //this指向的是按钮(因为是按钮触发调用)
//此处的this指向的是btn所以它的name就是undefined
console.log(that.uname);
}
dance() {
// 调用方法中使用this就会指向实例化
_that = this;
console.log(this);
}
}
var ldh = new Star("刘德华");
console.log(that === ldh); //true:this指向的就是new出来的实例化对象
ldh.dance(); //函数的this指向就是函数调用本身
console.log(_that === ldh); //true:this指向的就是调用者
</script>
</body>
6. 面向对象案例
- 首先布局页面
- 进行tab栏切换操作
- 创建类和构造函数(为了获取所有的DOM元素)
- 获取所有DON元素
- 执行事件初始化操作
- 初始化操作:(初始化包括:重新渲染+点击事件处理操作)
- 首先执行动态更新
- 添加按钮点击执行添加效果
- 遍历当前所有li,获取当前索引号
- 当前li点击就是切换方法
- 当前删除点击就是删除方法
- 当前标签内容点击就是修改标签内容方法
- 当前内容点击就是修改内容方法
- 动态更新方法:
- 重新获取所有的li(包含着标签和删除按钮)
- 重新获取所有的内容
- 重新获取所有的删除按钮
- 重新获取所有的标签
- 切换功能(派他思想)
- 调用实例化对象下面的清除方法
- 设置当前的调用的标签为指定样式名
- 设置当前的调用的标签内容为指定样式名
- 单独创建一个清除方法,遍历当前所有lis并将当前类名和内容名字修改为空
- 设置添加功能
- 清除当前所有类名
- 创建一个应该添加的html标签
- 创建一个应该添加的html内容
- 将内容添加到指定的父元素中
- 再次执行初始化操作
- 设置删除功能
- 阻止事件冒泡
- 设置下标为当前事件的父元素下标
- 打印当前父元素下标
- 清除lis指定下标
- 清除内容执行下标
- 设置删除li不是存在指定样式的li时候,删除指定的li
- 点击删除之后设置下标--
- 手动设置是当前li就直接出触发点击事件
- 修改功能
- 获取原先的文字
- 设置双击文字禁止选中(固定语句)
- 清除
- 然后当前div内添加一个input
- input内容就是原先的文字
- 设置文本框文字处选中状态
- 设置鼠标移除就把当前文字内容给当前绑定事件的父级
- 设置鼠标按下:如果鼠标事件是13那么就执行自动调用鼠标移除事件
- 创建类和构造函数(为了获取所有的DOM元素)
// 定义最大的类
var that;
class Tab{
constructor(id) {//此处的id接收传递进来的参数
that = this;
this.main = document.querySelector('main');
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
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();
}
// 1.初始化操作:所有的li添加绑定事件
init() {
this.updateNode();
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;
//调用删除按钮执行删除效果
this.remove[i].onclick = this.removeTab;
this.spans[i].ondblclick = this.editTab;
this.sections[i].ondblclick = this.editTab;
}
}
//动态添加元素,需要重新获取对应的元素
updateNode() {
// 更新获取所有的li和section
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
// 不断更新方式获取删除按钮
this.remove = this.main.querySelectorAll('.icon-guanbi')
//获取所有span
this.spans = this.main.querySelectorAll('.fisrstnav ul span:first-child')
}
// 1.切换功能
toggleTab() {
// 测试console.log(this.index);
// 排他思想:实例对象使用清除方法
that.clearClass();
// 点击当前按钮:添加样式
this.className = 'liactive';
// 当前对应的内容显示(注意this的用法不是指向按钮而是类)
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();
var random = Math.random();
// 1.创建li元素和section 元素
var li = '<li class="liactive"><span>Tab</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.删除功能
removeTab(e) {
// 阻止事件冒泡
e.stopPropagation();
var index = this.parentNode.index;
console.log(index);
//直接使用remove方法删除指定元素
that.lis[index].remove();
that.sections[index].remove();
that.init();
// 当删除的li不是选择的li时候,删除指定的li,原来选择的li还是保持状态
if (document.querySelector('.liactive')) return;
// bug:设置当前的按钮点击删除之后显示当前按钮前一个显示点击状态
index--;
//手动调用不需要鼠标触发 (判断是否存在这个li)
that.lis[index]&&that.lis[index].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];
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();
}
}
};
}
new Tab('#tab');//此处实例化参数是tab栏