javaScript进阶面向对象ES6
JavaScript 面向对象编程
面向对象编程的特性:封装性、继承性、多态性
面向过程编程的优点:性能比面向对象高,适合跟硬件联系的东西;缺点:没有面向对象易维护、易复用、易扩展
类和对象
1 | // 创建类 |
- 通过 class 关键字创建类,类名我们还是习惯性定义首字母大写
- 类里面有一个 constructor 函数,可以接受传递过来的参数,同时返回实例对象
- constructor 函数只要 new 生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
- 生成实例 new 不能省略
- 最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function
this 指向问题
1 | // 谁调用方法,this就指向谁 |
类继承 extends 和 super 关键字
1 | class Father{ |
总结
1 | (1) 调用父类构造函数 super 关键字 |
重点
- 在 ES6 中类没有变量提示,所有必须先定义类,才能通过类实例化对象
- 类里面的共有属性和方法一定要加 this 使用
- 类里面的this指向问题
- constructor 里面的this指向实例对象,方法里面的this指向这个方法的调用者
类编程思想
1 | // (1) 时刻需要注意类的this指向,谁调用了该方法,谁就是this指向的对象 |
添加 标签
1 | var newli = |
insertAdjacentHTML()
方法将指定的文本解析为Element
元素,并将结果节点插入到DOM树中的指定位置。它不会重新解析它正在使用的元素,因此它不会破坏元素内的现有元素。这避免了额外的序列化步骤,使其比直接使用innerHTML操作更快语法:
element.insertAdjacentHTML(position, text);
position:一个 DOMString
,表示插入内容相对于元素的位置,并且必须是以下字符串之一:
'beforebegin'
:元素自身的前面。'afterbegin'
:插入元素内部的第一个子节点之前。'beforeend'
:插入元素内部的最后一个子节点之后。'afterend'
:元素自身的后面。
构造函数和原型
ES6以前通过构造函数生成对象。构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。
创建对象可以通过三种方式:
- 对象字面量
- new Object()
- 利用构造函数创建对象
1 | // (1) 利用 new Object() 创建 |
静态成员和实例成员
1 | function Star(uname,age){ |
构造函数的问题
构造函数很好用,但是存在浪费内存的问题。当实例化很多对象,如果构造函数有函数,每一个实例化对象都需要开辟新的内存空间,去创建函数。
构造函数原型 prototype
原型是一个对象,我们也称 prototype 为原型对象
构造函数通过原型分配的函数是所有对象所共享的
1 | // 不需要将函数写在构造函数里面,而是写在原型上去共享 |
对象原型 __proto__
对象身上系统自动添加 __proto__
对象,指向我构造函数原型对象 prototype
1 | console.log(obj1.__proto__ === Star.prototype); //logs true 完全等价 |
修改原型对象
1 | // 在给原型对象添加函数时,假如添加很多函数,可以如下写 |
这样给原型添加函数方法会直接覆盖以前的原型,因为给原型赋值的是一个对象。如果这样我们必须手动的利用 constructor
指向原来的构造函数
1 | Star.prototype = { |
构造函数、实例、原型对象三者之间的关系
Star构造函数有一个原型对象 prototype 指向了 原型对象 prototype
原型对象 prototype 的属性 constructor 又指向了 Star 构造函数,因此如果赋值prototype为对象,就没有 construtor指向 Start
实例化对象通过
__proto__
指向 prototype 完全等价
原型链
Star 原型对象也有
__proto__
指向的是 Object 原型对象的 prototypeObject 原型对象指向的是 null
PS:原型链对象成员查找规则(就近原则),当对象实例没有向上级 原型对象 prototype 找,直到最顶层
扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法 。 比如给数组增加自定义求偶数和的功能
PS:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} , 只能是 Array.prototype.xxx = function(){} 的方式
1 | Array.prototype.sum = function(){ |
继承
ES6之前并没有给我们提供 extends 继承。我们通过构造函数+原型对象模拟实现继承,被称为组合继承
call()
1 | // 作用 |
借用父构造函数 继承属性
1 | function Father(uname,age){ |
继承方法
(1) 构造函数一般将方法写在原型上
1 | function Father(uname,age){ |
1 | function Father(uname, age) { |
PS:可以明显看到 Son 的原型 和 Father 原型一样,连给 Son 原型添加的方法,也添加到 Father 原型上,其实它们开辟的空间地址是同一个地址,因此修改会互相影响
使用父实例对象赋值
1 | // 实例对象开辟的空间 和 构造函数原型开辟的空间是不一样的地址 |
类的本质
类是本质就是函数function,ES6通过 类 实现面向对象编程。ES6 类 和 ES5 构造函数是一样的,只是另一种写法(语法糖)
类所有的方法都定义在 原型对象 上,和E6操作是一样的,只是 ES6 可以直接写在类里面
ES5 中新增的方法
数组方法
迭代(遍历)方法:forEach()、map()、filter()、some()、every()
1 | // (1) forEach |
1 | // (2) filter |
1 | // (3) some |
1 | // (4) map |
1 | // (5) every() |
forEach 和 som 的区别
1 | // 当查询元素唯一标识选择 some 方法更高效 |
字符串方法
1.trim()
去除字符串两侧的空格。常用于 input 输入框去除空格
对象方法
1.Object.defineProperty(obj,prop,descriptor)
1 | // 定义对象中新属性或修改原有的属性 |
2.Object.keys()
1 | // 用于获取对象自身所有的属性 |
函数进阶
函数的定义和调用
定义函数
1 | // (1) 函数定义方式 |
调用六种函数的方法
1 | // (2) 调用 |
this
改变函数内部 this 指向
js专门提供了一些函数方法来帮我们更优雅处理函数内部 this 的指向问题,常用的 bind()、call()、apply()
- call(thisArg) 方法使用一个指定的
this
值和单独给出的一个或多个参数来调用一个函数。
注意:该方法的语法和作用与
apply()
方法类似,只有一个区别,就是call()
方法接受的是一个参数列表,而apply()
方法接受的是一个包含多个参数的数组。
1 | 语法: function.call(thisArg, arg1, arg2, ...) |
call()
提供新的 this 值给当前调用的函数/方法。你可以使用 call
来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
使用 call
方法调用父构造函数
在一个子构造函数中,你可以通过调用父构造函数的 call
方法来实现继承,类似于 Java
中的写法。下例中,使用 Food
和 Toy
构造函数创建的对象实例都会拥有在 Product
构造函数中添加的 name
属性和 price
属性,但 category
属性是在各自的构造函数中定义的。
1 | function Product(name, price) { |
apply()
方法调用一个具有给定this
值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
1 | 语法:func.apply(thisArg, [argsArray]) |
apply
与 call()
非常相似,不同之处在于提供参数的方式。apply
使用参数数组而不是一组参数列表。apply
可以使用数组字面量(array literal),如 fun.apply(this, ['eat', 'bananas'])
,或数组对象, 如 fun.apply(this, new Array('eat', 'bananas'))
。
bind()
方法创建一个新的函数,在bind()
被调用时,这个新函数的this
被指定为bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
1 | // 语法 |
在默认情况下,使用 window.setTimeout()
时,this
关键字会指向 window
(或 global
)对象。当类的方法中需要 this
指向类的实例时,你可能需要显式地把 this
绑定到回调函数,就不会丢失该实例的引用。
1 | setTimeout(function(){ |
总结
区别点:
- call 和 apply 会调用函数,并且改变函数内部this指向
- call 和 apply 传递的参数不一样,call 传递参数 aru1,aru2……,apply 必须数组形式[arag]
- bind 不会调用函数,可以改变函数内部 this 指向
主要应用场景:
- call 经常做继承,去使用父构造函数的成员
- apply 经常跟数组有关系。比如借助于数学对象实现数组最大值和最小值
- bind 不调用函数,但是还想改变 this 指向。比如改变定时器内部的 this 指向
严格模式
js 除了提供正常模式外,还提供了严格模式(strict mode)。ES5的严格模式是采用最有限制性js变体的一种方式,即在严格的条件下运行js代码
- 消除了js语法的一些不合理、不严谨之处、减少了一些怪异的行为
- 提高编译器效率,增加运行速度
- 禁用了在 ECAMScript 的未来版本中可能定义的语法,为未来新版本的js做好铺垫。比如不能使用 class、enum、export、super、为变量名
严格模式可以应用到整个脚本或个别函数
1 | // (1) 为脚本开启严格模式 |
严格模式的变化
- 变量必须声明,不能是直接赋值
num = 10
正常模式不会报错可以打印 num 值。但严格模式下必须var num = 10
必须声明 - 严禁删除已经声明好了的变量
- 全局作用域中函数中的 this 是 undefined。正常模式是 window
- 构造函数不加new 调用,this会报错。this指向的是 undefined
- 定时器没有变化,还是指向 window 对象
函数变化
不允许有重名参数
严格模式禁止了不在脚本或者函数层面上的函数声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14;
if (true) {
function f() { } // !!! 语法错误
f();
}
for (var i = 0; i < 5; i++) {
function f2() { } // !!! 语法错误
f2();
}
function baz() { // 合法
function eit() { } // 同样合法
}
高阶函数
高阶函数是对其它函数进行操作的函数,它接收函数作为参数(回调函数)或将函数作为返回值输出
1 | // 高阶函数运用案例 |
闭包
什么是闭包(closure):闭包指有权访问另外一个函数作用域中变量的函数
作用:延伸了变量的作用范围
1 | // 闭包就是就是函数,在全局如何使用局部变量 |
思考题
1 | // (1) |
1 | object.getNameFunc()() => 等同于 立即执行函数 |
递归
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
递归里面必须加退出条件,不然死循环就会栈溢出
案例
1 | // 利用递归函数求斐波那契数列(兔子序列)1、1、2、3、5、8、13、21 |
浅拷贝 和 深拷贝
- 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
- 深拷贝拷贝多层,每一级别的数据都会拷贝
1 | // 浅拷贝 |
ES6 新增浅拷贝语法糖
1 | Object.assign(obj,targetObject) |
1 | // 深拷贝 函数封装 |
正则表达式
正则表达式概述
正则表达式是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象
通常用来检索、替换那些符合某个模式的文本,例如表单验证、还用于过滤页面内容中的敏感词
特点:灵活性、逻辑性和功能性非常的强
极为简单的控制复杂的字符串
正则表达式在 JavaScript中的使用
1 | // 创建正则表达式 |
测试正则表达式 test
test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串
正则表达式的特殊字符
一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组成,比如/ab*c/。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^、$、+等
边界符 ^ $
^
表示匹配行首的文本(以谁开始)
$
表示匹配行尾的文本(以谁结束)
如果两者边界符出现,表示必须是精度匹配
1 | var reg = /^simplelife$/; |
字符类
1 | //字符类:[] 表示有一些列字符可供选择,只要匹配其中一个就可以了 |
量词符
量词符同于设定某个模式出现的次数
1 | // * 号 相当于 >= 0 可以出现0次或则很多次 |
总结 中括号 大括号 小括号
1 | var reg = /^abc{3}$/ // 它只是让c重复三次 abccc |
预定义类
预定义类指的是某些常用模式的简写方式
预定义类 | 说明 |
---|---|
\d | 匹配0-9之间的任一数字,相当于[0-9] |
\D | 匹配所有0-9以外的字符,相当于 [^0-9] |
\w | 匹配任意的字母、数字、下划线,相当于 [^A-Za-z0-9_ |
\W | \w 取反 |
\s | 匹配空格(包括换行符、制表符、空格符等) |
\S | 匹配非空格字符 |
常用正则表达式(测试正则匹配):https://c.runoob.com/front-end/854/
正则表达式中的替换
1 | // 替换 replace |
PS:上面这种替换方式之后替换一次
正则表达式参数
/表达式/[switch]
switch 按照什么样的模式来匹配,有三种值
- g:全局匹配
- i: 忽略大小写
- gi:全局匹配 + 忽略大小写
ES6
1.箭头函数
箭头函数不绑定 this 关键字,箭头函数中的 this,指向的是 函数定义位置的上下文 this
1 | var obj = { |
2.伪数组转化成真正的数组
1 | var divs = document.querySelectorAll('div') // 获取所有的div 得到伪数组 |
1 | // 使用数组内置方法转化 |
3.Array的扩展方法
find()
方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
1 | Array.prototype.find((item,index)=>{ |
findIndex()
方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
1 | const array1 = [5, 12, 8, 130, 44]; |
includes()
方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true
,否则返回 false
。
1 | const array1 = [1, 2, 3]; |
4.模板字符串
1 | // 模板字符串可以解析变量 |
5.String 的扩展方法
1 | // (1) startsWith() 表示参数字符串是否在原字符串的开头,返回布尔值 |
1 | // repeat() 方法 |
6.Set 数据结构
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
1 | // Set 本身是一个构造函数,用来生成 Set 数据结构 |
实例方法
- add(value):添加某一个值,返回 Set 结构本身
- delete(value):删除个值,返回boolean值
- has(value):返回一个布尔值,表示该值是否为Set成员
- clear():清除所有成员,没有返回值
1 | // 遍历 |