导图社区 JavaScript高级
这是一篇关于JavaScript高级的思维导图,讲述了面向过程与面向对象、对象与类、函数的定义与协调、构造函数和原型等。
编辑于2022-03-23 15:51:02庞静day15-JavaScript高级
面向过程与面向对象
面向过程
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了
面向对象
面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
面向过程与面向对象对比
对象与类
对象是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物
- 属性:事物的特征,在对象中用属性来表示(常用名词)
- 方法:事物的行为,在对象中用方法来表示(常用动词)
创建对象
字面量创建对象
构造函数创建对象
类
在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象
创建类
//步骤1 使用class关键字 class name { // class body } //步骤2使用定义的类创建实例 注意new关键字 var xx = new name();
类创建添加属性和方法
// 1. 创建类 class 创建一个类class Star { // 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数 constructor(uname, age) { this.uname = uname; this.age = age; }//------------------------------------------->注意,方法与方法之间不需要添加逗号 sing(song) { console.log(this.uname + '唱' + song); }}// 2. 利用类创建对象 newvar ldh = new Star('刘德华', 18);console.log(ldh); // Star {uname: "刘德华", age: 18}ldh.sing('冰雨'); // 刘德华唱冰雨
1. 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写 2. 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象 3. constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数 4. 多个函数方法之间不需要添加逗号分隔 5. 生成实例 new 不能省略 6. 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function
类的继承
// 父类 class Father{ } // 子类继承父类 class Son extends Father { }
1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的 2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则) 3. 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用
时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用.
1. constructor中的this指向的是new出来的实例对象 2. 自定义的方法,一般也指向的new出来的实例对象 3. 绑定事件之后this指向的就是触发事件的事件源
在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
面向对象版tab 栏切换
功能需求
1. 点击 tab栏,可以切换效果. 2. 点击 + 号, 可以添加 tab 项和内容项. 3. 点击 x 号, 可以删除当前的tab项和内容项. 4. 双击tab项文字或者内容项文字可以修改里面的文字内容
案例准备
1. 获取到标题元素 2. 获取到内容元素 3. 获取到删除的小按钮 x号 4. 新建js文件,定义类,添加需要的属性方法(切换,删除,增加,修改) 5. 时刻注意this的指向问题
切换
为获取到的标题绑定点击事件,展示对应的内容区域,存储对应的索引
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'; }
添加
为添加按钮+ 绑定点击事件: 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(); }
删除
为元素的删除按钮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.sections[i].ondblclick = this.editTab;
在双击事件处理文本选中状态,修改内部DOM节点,实现新旧value值的传递
构造函数和原型
静态成员和实例成员
实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问
静态成员 在构造函数本身上添加的成员 如下列代码中 sex 就是静态成员,静态成员只能通过构造函数来访问
构造函数的问题
构造函数原型prototype:
对象原型
constructor构造函数
function Star(uname, age) { this.uname = uname; this.age = age; } // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数 Star.prototype = { // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数 constructor: Star, // 手动设置指回原来的构造函数 sing: function() { console.log('我会唱歌'); }, movie: function() { console.log('我会演电影'); } } var zxy = new Star('张学友', 19); console.log(zxy)
原型链
构造函数实例和原型对象三角关系
原型链和成员的查找机制
当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。 如果还没有就查找原型对象的原型(Object的原型对象)。 依此类推一直找到 Object 为止(null)。 __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
原型对象中this指向
function Star(uname, age) { this.uname = uname; this.age = age; } var that; Star.prototype.sing = function() { console.log('我会唱歌'); that = this; } var ldh = new Star('刘德华', 18); // 1. 在构造函数中,里面this指向的是对象实例 ldh console.log(that === ldh);//true // 2.原型对象函数里面的this 指向的是 实例对象 ldh
通过原型为数组扩展内置方法
Array.prototype.sum = function() { var sum = 0; for (var i = 0; i < this.length; i++) { sum += this[i]; } return sum; }; //此时数组对象中已经存在sum()方法了 可以始终 数组.sum()进行数据的求
继承
call()
- call()可以调用函数 - call()可以修改this的指向,使用call()的时候 参数一是修改后的this指向,参数2,参数3..使用逗号隔开连接
子构造函数继承父构造函数中的属性
1. 先定义一个父构造函数 2. 再定义一个子构造函数 3. 子构造函数继承父构造函数的属性(使用call方法)
### 借用原型对象继承方法
1. 先定义一个父构造函数 2. 再定义一个子构造函数 3. 子构造函数继承父构造函数的属性(使用call方法)
ES5新增方法
数组方法forEach遍历数组
arr.forEach(function(value, index, array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 }) //相当于数组遍历的 for循环 没有返回值
数组方法filter过滤数组
var arr = [12, 66, 4, 88, 3, 7]; var newArr = arr.filter(function(value, index,array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 return value >= 20; }); console.log(newArr);//[66,88] //返回值是一个新数组
数组方法some
some 查找数组中是否有满足条件的元素 var arr = [10, 30, 4]; var flag = arr.some(function(value,index,array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 return value < 3; }); console.log(flag);//false返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环
some和forEach区别
- 如果查询数组中唯一的元素, 用some方法更合适,在some 里面 遇到 return true 就是终止遍历 迭代效率更高 - 在forEach 里面 return 不会终止迭代
trim方法去除字符串两端的空格
var str = ' hello ' console.log(str.trim()) //hello 去除两端空格 var str1 = ' he l l o ' console.log(str.trim()) //he l l o 去除两端空格
获取对象的属性名
var obj = { id: 1, pname: '小米', price: 1999, num: 2000 }; var result = Object.keys(obj) console.log(result)//[id,pname,price,num]
Object.defineProperty
Object.defineProperty(对象,修改或新增的属性名,{ value:修改或新增的属性的值, writable:true/false,//如果值为false 不允许修改这个属性值 enumerable: false,//enumerable 如果值为false 则不允许遍历 configurable: false //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性 })
函数的定义和调用
函数的定义方式
1. 方式1 函数声明方式 function 关键字 (命名函数) function fn(){} 2. 方式2 函数表达式(匿名函数) var fn = function(){} 3. 方式3 new Function() var f = new Function('a', 'b', 'console.log(a + b)'); f(1, 2); var fn = new Function('参数1','参数2'..., '函数体') 注意 /*Function 里面参数都必须是字符串格式 第三种方式执行效率低,也不方便书写,因此较少使用 所有函数都是 Function 的实例(对象) 函数也属于对象 */
函数的调用
/* 1. 普通函数 */ function fn() { console.log('人生的巅峰'); } fn(); /* 2. 对象的方法 */ var o = { sayHi: function() { console.log('人生的巅峰'); } } o.sayHi(); /* 3. 构造函数*/ function Star() {}; new Star(); /* 4. 绑定事件函数*/ btn.onclick = function() {}; // 点击了按钮就可以调用这个函数 /* 5. 定时器函数*/ setInterval(function() {}, 1000); 这个函数是定时器自动1秒钟调用一次 /* 6. 立即执行函数(自调用函数)*/ (function() { console.log('人生的巅峰'); })();
this
改变函数内部 this 指向
call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向 应用场景: 经常做继承.
apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。 应用场景: 经常跟数组有关系
bind() 方法不会调用函数,但是能改变函数内部this 指向,返回的是原函数改变this之后产生的新函数 如果只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind 应用场景:不调用函数,但是还想改变this指向
call、apply、bind三者的异同
- 共同点 : 都可以改变this指向 - 不同点: - call 和 apply 会调用函数, 并且改变函数内部this指向. - call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递 - bind 不会调用函数, 可以改变函数内部this指向. - 应用场景 1. call 经常做继承. 2. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值 3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向. ## 3.严格模式
正则
正则表达式概述
什么是正则表达式
正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。
正则表达式的特点
1. 灵活性、逻辑性和功能性非常的强。 2. 可以迅速地用极简单的方式达到字符串的复杂控制。 3. 对于刚接触的人来说,比较晦涩难懂。比如:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 4. 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. 比如用户名: /^[a-z0-9_-]{3,16}$/
正则表达式在js中的使用
正则表达式的创建
方式一:通过调用RegExp对象的构造函数创建
方式二:利用字面量创建 正则表达式
测试正则表达式
test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。
var rg = /123/; console.log(rg.test(123));//匹配字符中是否出现123 出现结果为true console.log(rg.test('abc'));//匹配字符中是否出现123 未出现结果为false
正则表达式中的特殊字符
正则表达式的组成
一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。 特殊字符非常多,可以参考: [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions) jQuery 手册:正则表达式部分 [正则测试工具]( <http://tool.oschina.net/regex)
边界符
| ^ | 表示匹配行首的文本(以谁开始) | | ---- | ------------------------------ | | $ | 表示匹配行尾的文本(以谁结束) | 如果 ^和 $ 在一起,表示必须是精确匹配。
var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符串型 // /abc/ 只要包含有abc这个字符串返回的都是true console.log(rg.test('abc')); console.log(rg.test('abcd')); console.log(rg.test('aabcd')); console.log('---------------------------'); var reg = /^abc/; console.log(reg.test('abc')); // true console.log(reg.test('abcd')); // true console.log(reg.test('aabcd')); // false console.log('---------------------------'); var reg1 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范 console.log(reg1.test('abc')); // true console.log(reg1.test('abcd')); // false console.log(reg1.test('aabcd')); // false console.log(reg1.test('abcabc')); // false
字符类
[] 方括号
var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true console.log(rg.test('andy'));//true console.log(rg.test('baby'));//true console.log(rg.test('color'));//true console.log(rg.test('red'));//false var rg1 = /^[abc]$/; // 三选一 只有是a 或者是 b 或者是c 这三个字母才返回 true console.log(rg1.test('aa'));//false console.log(rg1.test('a'));//true console.log(rg1.test('b'));//true console.log(rg1.test('c'));//true console.log(rg1.test('abc'));//true ---------------------------------------------------------------------------------- var reg = /^[a-z]$/ //26个英文字母任何一个字母返回 true - 表示的是a 到z 的范围 console.log(reg.test('a'));//true console.log(reg.test('z'));//true console.log(reg.test('A'));//false ----------------------------------------------------------------------------------- //字符组合 var reg1 = /^[a-zA-Z0-9]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true ------------------------------------------------------------------------------------ //取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。 var reg2 = /^[^a-zA-Z0-9]$/; console.log(reg2.test('a'));//false console.log(reg2.test('B'));//false console.log(reg2.test(8));//false console.log(reg2.test('!'));//true
量词符
| * | 重复0次或更多次 | | ----- | --------------- | | + | 重复1次或更多次 | | ? | 重复0次或1次 | | {n} | 重复n次 | | {n,} | 重复n次或更多次 | | {n,m} | 重复n到m次 |
用户名表单验证
功能需求: 1. 如果用户名输入合法, 则后面提示信息为: 用户名合法,并且颜色为绿色 2. 如果用户名输入不合法, 则后面提示信息为: 用户名不符合规范, 并且颜色为红色
分析: 1. 用户名只能为英文字母,数字,下划线或者短横线组成, 并且用户名长度为6~16位. 2. 首先准备好这种正则表达式模式/$[a-zA-Z0-9-_]{6,16}^/ 3. 当表单失去焦点就开始验证. 4. 如果符合正则规范, 则让后面的span标签添加 right类. 5. 如果不符合正则规范, 则让后面的span标签添加 wrong类.
括号总结
1.大括号 量词符. 里面表示重复次数 2.中括号 字符集合。匹配方括号中的任意字符. 3.小括号表示优先级
预定义类
正则替换replace
var str = 'andy和red'; var newStr = str.replace('andy', 'baby'); console.log(newStr)//baby和red //等同于 此处的andy可以写在正则表达式内 var newStr2 = str.replace(/andy/, 'baby'); console.log(newStr2)//baby和red //全部替换 var str = 'abcabc' var nStr = str.replace(/a/,'哈哈') console.log(nStr) //哈哈bcabc //全部替换g var nStr = str.replace(/a/a,'哈哈') console.log(nStr) //哈哈bc哈哈bc //忽略大小写i var str = 'aAbcAba'; var newStr = str.replace(/a/gi,'哈哈')//"哈哈哈哈bc哈哈b哈哈"
ES6语法
ES6相关概念(★★)
ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。
为什么使用 ES6 ?
每一次标准的诞生都意味着语言的完善,功能的加强。JavaScript语言本身也有一些令人不满意的地方。 - 变量提升特性增加了程序运行时的不可预测性 - 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码
ES6新增语法
let(★★★)
let声明的变量只在所处于的块级有效
不存在变量提升
暂时性死区
利用let声明的变量会绑定在这个块级作用域,不会受外界的影响
小结
- let关键字就是用来声明变量的 - 使用let关键字声明的变量具有块级作用域 - 在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的 - 防止循环变量变成全局变量 - 使用let关键字声明的变量没有变量提升 - 使用let关键字声明的变量具有暂时性死区特性
const(★★★)
具有块级作用域
声明常量时必须赋值
常量赋值后,值不能修改
小结
- const声明的变量是一个常量 - 既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值 - 声明 const时候必须要给定值
解构赋值(★★★)
数组解构
对象解构
箭头函数(★★★)
xxxxxxxxxx () => {} //():代表是函数; =>:必须要的符号,指向哪一个代码块;{}:函数体const fn = () => {}//代表把一个函数赋值给fn
子主题
子主题
子主题
let、const、var 的区别
- 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象 - 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升 - 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题
子主题