失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 前端与移动开发----JS高级----面向对象编程 类与实例对象 继承 严格模式 模板字符

前端与移动开发----JS高级----面向对象编程 类与实例对象 继承 严格模式 模板字符

时间:2021-12-19 06:00:37

相关推荐

前端与移动开发----JS高级----面向对象编程 类与实例对象 继承 严格模式 模板字符

JS高级01

回顾上阶段

Javascript组成

ECMAScript: 基础语法 (变量, 表达式, 循环, 判断, 函数, 对象等)DOM: document 操作标签(获取, 增加, 插入, 删除, 移动), 事件(鼠标, 键盘, 表单), 标签属性等, 和标签打交道BOM: window对象, 定时器, 计时器, (location, navigator, history)等, 和浏览器打交道

与现阶段区别

之前学的JS基础语法, 这个阶段学习一些高级用法和思想

现在学习:

面向过程开发 / 面向对象开发 区别面向对象开发 类 - 实例对象继承 构造函数 - 了解JS对象底层运作的机制 显示原型 prototype隐式原型__proto__原型链 各种函数 闭包高阶函数递归函数 ES6新增语法使用 (现在学的是ES5版本, ES6只是新增了一些关键字和语法)

1. 面向对象编程

面向过程开发

​ 分析项目所需要的步骤

​ 用基础代码, 把这些步骤一步一步实现, 依次执行

面向对象开发

​ 分析项目所需要的对象

​ 用对象调用他们身上的方法

面向过程和对象区别

例1: 把大象装进冰箱

面向过程思想: 面向对象思想: 大象对象 走路方法 冰箱对象 开门方法关门方法

例2: 造飞机

一个人叠纸飞机, 一步一步来就ok - (面向过程思想编码)

造真飞机, 功能复杂庞大, 代码量多, 需要多个对象参与 - (面向对象思想编码)

* A对象 -> 负责造外壳* B对象 -> 造发动机* C对象 -> 造机仓* D对象 -> 造轮子* ...

总结

面向过程:小项目

面向对象:多人合作大项目

面向对象使用场景

把某个功能封装成插件, 我们可以把相关代码放到类里, 然后实例化对象出来, 调用, 例如前面学过的Swiper等插件

面向对象就是一种思想, 代码还是JavaScript

类与实例对象

什么是类?

类是一个模板, 里面有属性和方法

类能干什么?

定义一套模板, 批量产生实例对象

什么是实例对象?

new 出来的对象

1.0 类模板 - 属性

记住语法格式: 创建类和实例对象

// ES6新增的class关键字定义类/*class 类名 {constructor (形参1, 形参2) { this.属性名 = 形参1this.属性名 = 形参2}}*/// 定义类class Star{constructor (theName, theAge) {// 添加属性this.name = theName; this.age = theAge;}}// 实例化对象var per1 = new Star("刘德华", 18);console.log(per1);var per2 = new Star("张学友", 20);console.log(per2);

1.1new的作用

创建一个空白对象{}执行并替代constructor里this的值在this(空白对象{})身上添加属性和对应的值constructor没有return, 就默认返回这个对象到调用处

class Star{constructor (theName, theAge) {this.name = theName; this.age = theAge;}}// 实例化对象var per1 = new Star("刘德华", 18);console.log(per1);var per2 = new Star("张学友", 20);console.log(per2);// 总结:// 1. 类是模板, 定义属性和方法的地方// 2. new的作用:// (1): 创建一个空白对象{}// (2): 替代constructor里默认this的值// (3): 执行constructor函数, 在this身上添加属性和值// (4): 自动返回这个对象{name:值, age:值}

执行顺序和对应关系如图

1.2 类模板 - 方法

类里除了属性以外, 还可以定义方法, 让所有的实例对象都有这个模板里的方法使用

class Star {constructor(theName, theAge) {this.name = theName;this.age = theAge;}// 方法写在这里sing (songName) {console.log(`${this.name}会唱${songName}`);}}// 实例化对象var per1 = new Star("刘德华", 18);console.log(per1);var per2 = new Star("张学友", 20);console.log(per2);// 使用属性console.log(per1.name);// 使用方法per1.sing("冰雨");// 总结:// 1. 类是模板 - 定义属性和方法// 2. new 类名() - 创建实例对象// 3. 对象.属性 - 原地返回属性的值// 4. 对象.方法() - 调用方法执行(跟普通函数调用一模一样)

1.3this的指向

this是函数内的隐藏变量, 无需声明直接使用

this变量的值, 在代码执行时, 才能知道里面的值 (运行绑定)

class Star {constructor(theName, theAge) {this.name = theName;this.age = theAge;}sing(songName) {console.log(`${this.name}会唱${songName}`);}}var per1 = new Star("刘德华", 18);per1.sing("冰雨");var per2 = new Star("张学友", 20);per2.sing("饿狼传说");// 总结:// new 触发constructor , 函数里 this指向实例对象 (特殊记忆)// 普通函数里, this指向调用者 (口诀通用)

图示如下 - 包括执行流程

1.4 练习

// 练习1: 定义人类Person, 属性有name, sex, 方法有run(方法里打印一句"人类会跑")

class Person {constructor (tName, tSex) {// 形参运行时, 用的传入的具体的值this.name = tName; // 把具体的值, 保存在this对象的属性name上 {name: 值}this.sex = tSex}run () {console.log("人类会跑");}}

// 练习2: 用Person类, 实例化2个对象, 并且传入名字和性别的值, 打印2个实例对象 (本题: 模板, 实例对象之间的关系)

class Person {constructor (tName, tSex) {// 形参运行时, 用的传入的具体的值this.name = tName; // 把具体的值, 保存在this对象的属性name上 {name: 值}this.sex = tSex}}var per1 = new Person("小黄", "男");var per2 = new Person("小花", "女");console.log(per1);console.log(per2);

// 练习3: 定义学生类Student, 属性有name, sex, hobby, 方法有study(方法里代码: console.log(this.name + “在学习”))

class Student {constructor (name, sex, hobby) {this.name = name;this.sex = sex;this.hobby = hobby;}study () {console.log(this.name + "在学习");}}

// 练习4: 用Student类, 实例化1个对象, 传入名字,性别,爱好, 实例对象调用study方法, 观察打印结果 (本题: 思考this.name, 里的this到底是谁?)

class Student {constructor (name, sex, hobby) {this.name = name;this.sex = sex;this.hobby = hobby;}study () {console.log(this.name + "在学习");}}var stu = new Student("小明", "男", "打游戏");stu.study();

// 练习5: 定义工具类Tool, 无属性, 实现2个方法, 一个求3个数的和, 一个求3个数最大值, 方法里都返回最后的结果, 实例化对象, 调用方法, 传参后, 接收返回的结果打印

class Tool {getSum (num1, num2, num3) {return num1 + num2 + num3;}getMax (numA, numB, numC) {return Math.max(numA, numB, numC); // Math.max() 会返回最大值在原地, 但是外面调用getMax()方法的地方还要这个最大值, 所以需要return出去}}var toolObj = new Tool();var result1 = toolObj.getSum(20, 30, 40);console.log(result1);console.log(toolObj.getMax(10, 15, 18));

总结: 多个方法, 使用相同的属性值(this.xxx), 则constructor里this.xxx接收, 如果只有函数自己用, 就用形参传递 (无论写哪里都是对的)

2. class更多用法

2.0继承 - extends

什么是继承

子类的实例对象, 继承父类里的属性和方法

// 1. 父类 - 求和, 求乘积方法class Father {constructor(theX, theY) {this.x = theX;this.y = theY;}getSum() {console.log(this.x + this.y);}getProduct() {console.log(this.x * this.y);}}// 2. 子类也想继承使用// 继承语法: class 子类 extends 父类 {} class Son extends Father {}// 3. 实例化子类对象, 调用方法var son = new Son(10, 29);son.getSum();son.getProduct();console.log(son.x, son.y);// 总结: 子类的实例对象, 能够继承并使用父类的属性和方法(多亏了extends关键字)

2.1 继承 - super 调用 父类constructor

需求: 子类要实现取随机数方法, 但是也想有求和, 求乘积的方法, 所以继承

class Father {constructor(theX, theY) {this.x = theX;this.y = theY;}getSum() {console.log(this.x + this.y);}getProduct() {console.log(this.x * this.y);}}class Son extends Father {// 子类自己也有属性constructor(tx, ty, randNum){super(tx, ty); // 要执行父类的constructor, 把x和y属性和值绑定上this.randN = randNum;}// 子类自己的方法getRandom() {console.log(Math.floor(Math.random() * this.randN));}}var son = new Son(10, 29, 100);son.getSum();son.getProduct();son.getRandom();console.log(son.x, son.y, son.randN);// 总结:// 1. super的作用// (1): 触发父类(extends后面)的类的constructor函数执行// (2): 修改父类constructor里的this为当前new的实例对象// (3): super()执行完父类的constructor后返回到这里, 再执行完子类constructor后返回实例对象到new处// 2. 如果子类constructor使用this, super必须在constructor的第一行

代码的执行顺序和this的值 (根据序号看)

2.2 继承 - super 调用 父类普通方法(了解)

需求: 子类求随机数的地方, 取值范围要参数1和参数2的乘积作为取值范围

class Father {constructor(theX, theY) {this.x = theX;this.y = theY;}getSum() {console.log(this.x + this.y);}getProduct() {console.log(this.x * this.y);return this.x * this.y;}}class Son extends Father {constructor(tx, ty, randNum){super(tx, ty); this.randN = randNum;}getRandom() {// 在这里通过 super.父类普通函数名() 即可跳转到父类相应方法执行后, 返回结果到这里console.log(Math.floor(Math.random() * this.getProduct()));}}var son = new Son(10, 29, 100);son.getRandom();// 总结:// this指向当前函数的调用者// new触发constructor执行, 改变了this指向, 指向为实例对象

代码执行过程

2.3 练习

/*Person类里属性: name和sexPerson类里方法: run - 打印一个字符串即可say - 打印当前调用者名字 Student类继承自Person类Student里属性: hobbyStudent里方法: play - 返回拼接的字符串: 调用者名字和爱好实例化子类对象, 传入3个实际参数的值提示, 子类里的super()应该把收到的实参值传递给父constructor函数, 把值绑定到实例对象的name和sex属性上最后调用run和say观察打印结果调用play方法拿到返回值, 然后打印*/class Person {constructor(name, sex) {this.name = name;this.sex = sex;}run() {console.log('abc');}say() {return this.name;}}class Student extends Person {constructor(name, sex, hobby) {super(name, sex);this.hobby = hobby;}play() {return this.name + this.hobby;}}var zn = new Student('张宁', '男', '玩游戏');zn.run();console.log(zn.say());console.log(zn.play());

3. 严格模式

什么是严格模式

JavaScript 除正常模式,还有严格模式(strict mode)

IE10以上支持, 以下被忽略 (不支持时, 当做普通字符串)

目的:

消除Javascript语法的不合理、不严谨之处【例如变量,不声明就报错】为未来新版本的Javascript做好铺垫 (未来指的是ES6)

3.0 开启严格模式

开启严格模式:“use strict” (只需要在顶部 - 声明一串字符串即可)

<script>"use strict"; // 当前脚本开启严格模式</script><script>function fn(){'use strict'; // 当前函数内, 开启严格模式}</script>

3.1 严格模式规则

严格模式对Javascript的语法和行为,在运行时都做了一些改变

变量必须 - 先声明 -再使用全局函数里的this 不再指向 window函数形参, 不能重名

"use strict"// 1. 变量必须先声明再使用// function myFn(){//var a = 10;//console.log(a);// }// myFn();// 2. 全局函数内, this不再指向windowfunction myFn() {console.log(this); // undefined}myFn();setTimeout(function () {console.log(this); // window}, 1000);// 3. 函数形参不能重名// function fn(a, a){//console.log(a);// }// fn(100, 500);// 总结:// 严格模式下, 变量必须先声明再使用, 全局函数this不再指向window

以后我们都使用ES6的语法, 而非严格模式

更多规则参考:

4. 模板字符串

4.0 模板字符串_基础使用

ES6 - 新定义的字符串

作用: 简化字符串的拼接

使用: 模板字符串必须用 `` 包含 (~那个键), 可以嵌入标签, 变量的部分使用${xxx}

<body><div id="one"></div><div id="two"></div><div id="three"></div><script>// 方式1: dom创建var oneName = "小明";var oneDiv = document.getElementById("one");// 创建p夹着span的var p = document.createElement("p");var span = document.createElement("span");oneDiv.appendChild(p);p.appendChild(span);span.innerHTML = oneName;// 方式2: 用标签字符串拼接var theName = "小花"var twoDiv = document.getElementById("two");twoDiv.innerHTML = "<p><span>" + theName + "</span></p>";// 方式3: ES6新出的字符串var threeName = "小黄";var threeDiv = document.getElementById("three");threeDiv.innerHTML = `<p><span>${threeName}</span></p>`;// 总结:// 1. 模板字符串``, 跟单引'', 双引"", 一样都是字符串// 2. 模板字符串方便拼接标签/还可以回车整理格式// 3. 模板字符串里使用变量, 必须用${}括起来, 运行时, 会把变量的值放到这个字符串里使用</script></body>

5. class封装tab栏

了解插件如何封装和调用以及复用 - 比如之前, 使用new Swiper()

先演示如何使用定义好的插件 tab.js (new Tab())

5.0 tab栏_标签和样式

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>案例_封装tab栏_标签和样式准备</title><style>.box {border: 1px solid #000;box-sizing: border-box;}.box * {box-sizing: border-box;}.box .header {width: 100%;height: 50px;display: flex;border-bottom: 1px solid #222;}.box .header span {transition: all 0.3s;background-color: #fff;line-height: 50px;text-align: center;cursor: pointer;font-size: 16px;border-right: 1px solid #000;flex: 1;}.box .header span.ac,.box .header span:hover {background-color: #ccc;}.box .main {width: 100%;}.box .main div {width: 100%;text-align: center;font-size: 60px;font-weight: 800;display: none;}.box .main div.active {display: block;}</style></head><body><!-- 标签栏容器 --><div class="box"><!-- 导航 --><div class="header"><span class="ac">tab1</span><span>tab2</span><span>tab3</span></div><!-- 内容 --><div class="main"><div class="active">内容1</div><div>内容2</div><div>内容3</div></div></div></body></html>

5.1 tab栏_js创建

// 1. 标题数组元素 - 用span装起来 - 生成标签字符串 - 放到header容器内var tabTitleArr = ["tab1", "tab2", "tab3"];var spanStr = '';for (var i = 0; i < tabTitleArr.length; i++) {// 第一个span要高亮spanStr += `<span class=${i == 0 ? 'ac' : ''}>${tabTitleArr[i]}</span>`;}var headerDiv = document.querySelector(".header");headerDiv.innerHTML = spanStr;// 2. 内容数组元素 - 用div装起来 - 生成标签字符串 - 放到main容器内var contentArr = ["内容1", "内容2", "内容3"];var divStr = ``;for (var j = 0; j < contentArr.length; j++) {divStr += `<div class=${j == 0 ? 'active' : ''}>${contentArr[j]}</div>`;}var mainDiv = document.querySelector(".main");mainDiv.innerHTML = divStr;

5.2 tab栏_class类定义

我们想要多个tab栏, 所以需要tab栏模板, 封装一个类

// 2. 定义类class Tab {constructor(query, configObj) {// 3. 以前保存在var的变量上, 现在保存在对象的属性上// 接收容器和配置this.box = document.querySelector(query);this.headerDiv = this.box.querySelector(".header");this.mainDiv = this.box.querySelector(".main");// 导航数据和正文数据this.tabTitleArr = configObj.tabTitleArr;this.contentArr = configObj.contentArr;// 4. 调用初始化标签方法this.init();}init() {var spanStr = '';for (var i = 0; i < this.tabTitleArr.length; i++) {spanStr += `<span class=${i == 0 ? 'ac' : ''}>${this.tabTitleArr[i]}</span>`;}this.headerDiv.innerHTML = spanStr;var divStr = ``;for (var j = 0; j < this.contentArr.length; j++) {divStr += `<div class=${j == 0 ? 'active' : ''}>${this.contentArr[j]}</div>`;}this.mainDiv.innerHTML = divStr;}}// 1. 模拟swiper使用, new 类, 传入标签容器选择器, 传入配置对象new Tab("#box", {tabTitleArr: ["tab1", "tab2", "tab3"],contentArr: ["内容1", "内容2", "<span style='color: red;'>内容3</span>"]})

5.3 tab栏_点击切换

功能 - 点击切换

var that; // 5. 保存实例对象class Tab {constructor(query, configObj) {that = this; // 保存实例对象// 接收容器和配置this.box = document.querySelector(query);this.headerDiv = this.box.querySelector(".header");this.mainDiv = this.box.querySelector(".main");// 导航数据和正文数据this.tabTitleArr = configObj.tabTitleArr;this.contentArr = configObj.contentArr;// 调用初始化标签方法this.init();}init() {var spanStr = '';for (var i = 0; i < this.tabTitleArr.length; i++) {// 2. 自定义属性 - 索引spanStr += `<span ind="${i}" class=${i == 0 ? 'ac' : ''}>${this.tabTitleArr[i]}</span>`;}this.headerDiv.innerHTML = spanStr;var divStr = ``;for (var j = 0; j < this.contentArr.length; j++) {divStr += `<div class=${j == 0 ? 'active' : ''}>${this.contentArr[j]}</div>`;}this.mainDiv.innerHTML = divStr;// 1. 事件委托 - header标签 - 点击事件this.headerDiv.onclick = this.toggleDiv;}toggleDiv(ev){// 3. 获取点击标签的索引值var ind = ev.target.getAttribute("ind");// 当前span高亮var spanList = that.headerDiv.querySelectorAll("span");for (var i = 0; i < spanList.length; i++) {spanList[i].className = "";}ev.target.className = "ac";// 4. 索引对应div出现var divList = that.mainDiv.querySelectorAll("div");for (var j = 0; j < divList.length; j++) {divList[j].className = "";}divList[ind].className = "active";}}new Tab("#box", {tabTitleArr: ["tab1", "tab2", "tab3"],contentArr: ["内容1", "内容2", "<span style='color: red;'>内容3</span>"]})

5.4 tab栏_编写使用文档

好了, 功能实现后, 可以自己写一个文档, 告诉别人怎么去使用了

index.css - 样式文件index.js - 主要功能使用插件者, 引入index.css和index.js, 然后准备标签结构, 只需要写一个new Tab()传入相关参数就有了tab栏切换的案例

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>tab栏_封装插件</title><!-- 把案例的css代码放到css文件夹/index.css中 --></head><body><!-- 引入固定结构+固定类名的标签 (id自己定义一个) --><div class="box" id="box"><div class="header"></div><div class="main"></div></div><!-- 把封装的class类, 放入到一个js文件夹/index.js文件中 --><script src="./js/index.js"></script><script>// 使用插件的步骤: // (1): 引入插件封装的样式 (index.css)// (2): 引入插件的功能代码 (index.js)// (3): 准备标签结构(固定类名和固定结构)// (4): 实例化使用, 传入配置// 直接new 类名() 然后传入相应的参数就可以得到一个tab栏new Tab("#box", {tabTitleArr: ["体育", "军事", "娱乐"],contentArr: ["我是体育页面", "军事内容好啊", "这里可以放图片啊或者完整的标签结构啊, 但是注意用模板字符串哦, 假设我是娱乐新闻"]})</script></body></html>

经验值

问题:ES6类继承,出现语法报错

描述:使用ES6对其它类进行继承时,语法报错如下: 分析:

Must call super constructor in derived class before accessing 'this' or returning from derived constructor在访问“ this”或从派生构造函数返回之前,必须在派生类中调用super 构造函数总结: 在继承其它构造函数之前,需要使用super关键字对父级的形参进行确认;再使用this

解决:

class Father {say() {return '我是爸爸';}}class Son extends Father {constructor(x, y) {super(); // 要在使用this之前使用super()this.x = x;}}new Son();

总结:无论父亲上面有没有属性,只要在子类中构造器函数内部操作this,就需要super 把父级相关的 属性进行确认;

面试题

事件委托以及优缺点

优点:节省内存, 给动态创建的标签绑定事件

缺点:

不冒泡,不支持。层级过多,不建议用(建议就近委托)

class、extends是什么,super有什么作用

class - 创建类 - 语法糖

extends - 子类继承父类里的方法, 如果子类没有constructor会使用默认的

super - 子类constructor里, super()确认父类里的属性, 绑定到子类的实例对象上(还有值)

如有不足,请多指教,

未完待续,持续更新!

大家一起进步!

前端与移动开发----JS高级----面向对象编程 类与实例对象 继承 严格模式 模板字符串 class封装tab栏

如果觉得《前端与移动开发----JS高级----面向对象编程 类与实例对象 继承 严格模式 模板字符》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。