ES6中的类和对象
事务分为 具体(特指)事务 和 抽象(泛指)事务。
面向对象的思维特点:
- 抽象对象共用的属性和行为组织(封装)成一个类(模板)。
- 对类进行实例化,获取类的对象。
面向对象编程,我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事。
对象
JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事务都是对象,例如:字符串、数值、数组、函数 等。
对象是由属性和方法组成:
属性:事物的特征,在对象中用属性来表示(常用名词)
方法:事物的行为,在对象中用方法来表示(常用动词)。
类 class
ES6 新增了类的概念,可以使用 class 关键字声明一个类,之后以这个类实例化对象。
类抽象了对象的公共部分,它泛指某一大类(class)
对象是特指某一个,通过类实例化一个具体的对象。
创建类
通过class 关键字创建类,类名首字母大写
类必须使用 new 实例化对象
ES6 中没有变量提升,所以必须要先定义类,才能通过类实例化对象。
/ 语法格式:
class Name {
// calss body
}
/ 创建实例:
var xx = new Name();
类 constraint 构造函数
constructor() 方法 是类的构造函数(默认方法),用于传递参数,返回实例对象
- 通过 new 命令生成对象实例时,自动调用该方法。
- 如果没有显示定义,类内部会自动给我们创建一个 constructor()
/ 语法格式:
class Star {
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
}
// 利用类创建对象 new
new bz = new Star('播仔', 18);
// 打印类
console.log(bz); // Star {uname: "播仔", age: 18}
// 打印 uname、age
console.log(bz.uname);
console.log(bz.age);
类添加方法
语法格式:
- 创建类 类名后面不加小括号;生成实例。
- 类名后加小括号
- 构造函数不需要加 function
- 类中的方法都是挂载到 Star.prototype 上的
/ 语法格式:
class Star {
// 类的共有属性放在 constructor 里面
constructor (uname) {
this.uname = uname;
};
// 共有方法
sing (song) {
console.log(`${this.uname} :${song}`);
}
}
// 利用类创建对象 new
new bz = new Star('播仔');
new hz = new Star('猴子');
// 打印
bz.sing('追光者'); // 播仔:追光者
hz.sing('晚婚'); // 猴子:晚婚
类的注意事项
类里的共有属性和方法一定要加 this 使用。
constructor 中的 this 指向的是创建的实例对象,方法中的 this 指向的是这个方法的调用者
class Star {
constructor(option) {
Object.assign(this, option);
// 公有方法要加 this
this.sing();
// 获取按钮,点击触发 sing 方法
this.btn = document.querySelector('button');
// 方法不加 括号,不然会直接调用
this.btn.onclick = this.sing;
}
sing() {
console.log(this.uname);
}
}
var star = new Star ({
uname: '刘德华',
age: '18',
});
类和继承
继承 extends
子类可以继承父类的属性和方法。
/ 语法格式:
calss Father {
constructor() {
}
money() {
console.log(100);
}
}
// 子类:继承父类的属性和方法
class Son extends Father {
constructor() {
}
}
// 利用类创建对象 new
var son = new son();
// 打印子类,继承父类的属性和方法
son.money(); // 100
super 关键字
super 关键字 用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
/ 语法格式:
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(x, y);
}
}
}
// 利用类创建对象 new
let son = new Son(1,2);
// 打印
son.sum(); // 3
super 查找原则
继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的方法,如果没有,则找父类中有没有这个方法,如果有就执行父类的这个方法。
简单理解:super 取就近原则
class Father {
//
say () {
return '我是爸爸';
}
}
class Son extends Father {
say () {
// 调用父类方法
console.log(super.say() + '的儿子');
}
}
// 创建实例化对象
let son = new Son();
// 打印子类方法
son.say(); // 我是爸爸的儿子
super 调用规则
子类在构造函数中使用 super,必须放到 this 前(必须先调用父类的构造方法,再使用子类的构造方法)
// 父类
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 必须放在 this 前
super (x, y);
this.x = x;
this.y = y;
}
// 减法运算方法
sub () {
console.log(this.x - this.y);
}
}
// 创建实例对象
let son = new Son(3,1);
// 打印方法
son.sum(); // 4
son.sub(); // 2
静态属性、方法
通过 实例对象.属性名 来定义静态属性
通过 实例对象.方法 来定义静态方法
面向对象添加静态属性、方法
/ 语法格式:
实例对象.属性名
实例对象.方法名
function Star(uname) {
// 添加实例属性
this.uname = uname;
}
// 添加实例方法
star.prototype.sing = function () {
console.log('唱歌');
}
// 添加静态属性
Star.age = 10;
// 添加静态方法
Star.play = function () {
console.log('玩耍');
}
var star = new Star('刘德华');
类添加静态属性、方法
class Star {
constructor (uname) {
// 添加实例属性
this.uname = uname;
}
// 添加实例方法
sing () {
console.log('唱歌');
}
// 静态属性
static.age = 20;
// 静态方法
static play () {
console.log('玩耍');
}
}
// 创建实例对象
var star = new Star('刘德华');
11-面向对象tab栏-思路分析以及布局介绍.mp4
正则表达式
正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。
正则表达式的特点
- 灵活性、逻辑性和功能性非常的强。
- 可以迅速地用极简单的方式达到字符串的复杂控制。
- 实际开发,一般都是直接复制写好的正则表达式。但是要求会使用正则表达式并且根据实际情况修改正则表达式。比如用户名: /1{3,16}$/
正则表达式的创两种创建方式
/ / 中的数据,不论数据类型,都不要加引号
/ 语法格式:
new RegExp(/正则表达式规则/);
// 利用 RegExp 对象来创建正则表达式
var reg = new RegExp(/abc/);
// 利用字面量的方式 创建正则表达式
var reg1 = /abc/;
校验数据
校验数据时,/ / 中的数据要加引号
test() 方法的返回值是 true || false
/ 语法格式:
正则变量名.test('要检索的字符串');
console.log( reg.test('bca') );
正则表达式的特殊符号
边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
边界符 | 说明 |
---|---|
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
//边界符
var reg = /^abc/;
console.log( reg.test('abcssasdasda') ); // true
console.log( reg.test('asbcsasdasda') ); // false
var reg1 = /abc$/;
console.log( reg1.test('ssasabc') ); // true
console.log( reg1.test('ssabcsa') ); // false
// 以abc开始,以abc结束
var reg2 = /^abc$/;
console.log( reg1.test('abc') ); // true
console.log( reg1.test('abcabc') ); // false
字符类
只要包含 [] 中的内容就为 true
如果括号里有 ^ 表示取反的意思,和边界符不一样
字符类 | 说明 |
---|---|
[] | 匹配包含 [] 中的内容就为 true |
[a-z] | 匹配 a-z 之间的字母 |
2$ | 匹配 a-z 之间的其中一个字母 |
[a-zA-Z] | 匹配 a-z A-Z 之间的其中一个字母 |
[^a-z] | 不允许出现 a-z 其中的字母 |
// 匹配 只要包含 abc 中的内容就为 true
var reg = /[abc]/;
console.log( reg.test('a') ); // true
// 匹配 a || b || c,其都为 false
var reg = /^[abc]$/;
console.log( reg.test('ab') ); // false
console.log( reg.test('abc') ); // false
量词符
用来设定某个模式出现的次数
量词符 | 说明 |
---|---|
* | 重复0次 或 更多次 |
+ | 重复一次 或 更多次 |
? | 多个字符 重复0次 或 1次(单个字符无效) |
{n} | 重复 n 次 |
{n,} | 重复 n次 或 更多次 |
{n, m} | 重复 n 到 m次 |
预定义类
预定义类指的是某些常见模式的简写方式
预定类 | 说明 |
---|---|
\d | 匹配0~9之间任意数字,相当于 [0, 9] |
\D | 匹配0~9以外的字符,相当于 [^0, 9] |
\w | 匹配任意字母、数字、下划线以外的字符,相当于 [A-Za-z0-9_] |
\W | 除所有字母、数字和下划线以外的字符,相当于 [^A-Za-z0-9_] |
\s | 匹配空格(包括换行符、制表符、空格符等),相当于 [\t\r\n\v\f] |
\S | 匹配非空格的字符,相当于 [^\t\r\n\v\f] |
括号总结
- 大括号 量词符. 里面表示重复次数
- 中括号 字符集合。匹配方括号中的任意字符
- 小括号表示优先级
//边界符
var reg = /^abc/;
console.log( reg.test('abcssasdasda') ); // true
console.log( reg.test('asbcsasdasda') ); // false
var reg1 = /abc$/;
console.log( reg1.test('ssasabc') ); // true
console.log( reg1.test('ssabcsa') ); // false
// 以abc开始,以abc结束
var reg2 = /^abc$/;
console.log( reg1.test('abc') ); // true
console.log( reg1.test('abcabc') ); // false
正则替换 replace
replace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。
面向对象案例
常量 const
使用 const 声明引用类型时,不允许进行赋值,但可以通过指针进行改值。
// 使用 const 声明引用类型
const arr = [];
arr[0] = 10; // [10]
arr = [20]; // error:不允许赋值,但可以通过指针进行改值
解构赋值
ES6 新增一种方式——解构赋值
数组解构
例如:把数组 [1, 2, 3] 中的元素分别赋值给 a、b 和 c,传统的做法是单独声明变量和赋值
// 传统方式
var arr = [1, 2, 3];
var a = arr[0];
var b = arr[1];
var c = arr[2];
// 解构赋值
[a, b, c] = [1, 2, 3];
使用解构赋值时,会将 “=” 右侧 “[]” 中的元素依次赋值给左侧 “[]” 中的变量
-
当左侧变量的数量小于右侧元素个数时,则忽略多余的元素
[a, b] = [1, 2, 3]; // [1, 2]
-
当左侧变量的数量大于右侧元素个数时,则多余的变量会被初始化为 undefined
[a, b, c, d] = [1, 2, 3]; // [1, 2s, 3, undefined]
解构赋值 右侧的内容还可以是一个变量名,或是通过解构赋值完成两个变量数值的交换
var arr = [1, 2, 3];
[a, b] = arr; // [1, 2]: 左边值少,舍弃 3
console.log(a + '-' + b); // 1-2
var n1 = 4, n2 = 8;
[n1, n2] = [n2, n1]; //可以是变量
console.log(n1 + '-' + n2); // 8-4
对象解构
var obj = {
name: '张三',
age: 17,
love: '干饭',
}
var {name, age, love} = obj;
console.log(name); // 张三
console.log(age); // 17
console.log(love); // 干饭
箭头函数
/ 语法格式:
var fn = (属性1, 属性2) => {
return 属性1 + 属性2;
}
// 属性写在括号里
var add = (num1, num2) => {
return num1 + num2;
}
console.log( add(7) ); // 70
// 简写方式:如果函数体内只有一行代码,可以省略花括号
sum = (a, b) => a + b;
sum(1, 2); // 3
注意:
箭头函数中没有 arguments,但可以使用 展开运算符 来访问所有的实参
- 展开运算符的变量名 一般使用 arguments 的简写(args) 来声明
arguments 是伪数组,可以遍历,可以通过下标访问数据
但是通过箭头函数给伪数组设置变量名,可以让他变成正常的数组
var fn = (...args) => {
console.log(args); // [10, 20, 30, 40, '张三'];
}
fn(10, 20, 30, 40, '张三');
箭头函数的this指向问题
-
箭头函数中没有自己的 this,所以指向 window
var fn = () => { console.log(this); // window } fn.call(this);
-
箭头函数中没有自己的 this,不能创建实例
var Star = (name) => { this.name = name; console.log(this); // Star is not a constructor } var star = new Star('张三');
-
想要创建自己的实例,只能通过 new 的形式创建对象
var Star = (option) => { Object.assign(this, option); } var star = new Star({ uname: '张三', age: 17, }); console.log(star.age); // 17
-
展开运算符
扩展运算符可以将数组或者对象转为用逗号分隔的参数序列
/ 语法格式: ... 变量名
扩展运算符可以应用于合并数组
let arr1 = [1, 2, 3];
let arr2 = [2, 3, 4];
// 方法一:
let arr3 = [...arr1, ...arr2];
// 方法二:
let arr4 = arr1.push(...arr2);
将类数组或可遍历对象转换为真正的数组
let lis = document.querySelectorAll('ul li');
lis = [...lis];
封装DOM
更方便使用,不需要一直写 document.querySelector
<ul class="box">
<li>列表1</li>
<li>列表2</li>
</ul>
<script>
let box = get('.box');
let lis = gets('li');
/**
* 封装一个DOM
* @param {String} selector 选择器
* @param {Object} element DOM对象
*/
function get(selector, element = document) {
return element.querySelector(selector);
}
function gets(selector, element = document) {
return element.querySelectorAll(selector);
}
</script>
严格判断数据类型
全等无法判断 NaN
NaN === NaN => false
Object.is(NaN, NaN); // true
内置对象方法 扩展
Array.from()
实例方法:find()
根据条件筛选数据
/ 语法格式:
数组.find(回调函数);
var arr = [
{name:'张三', age: 17, score: 90},
{name:'李四', age: 17, score: 95},
{name:'王五', age: 17, score: 100},
];
实例方法:findIndex()
查找数组中第一个满足条件项的下标
/ 语法格式:
数组.find(回调函数);
var arr = [
{name:'张三', age: 17, score: 90},
{name:'李四', age: 17, score: 95},
{name:'王五', age: 17, score: 100},
];
var r = arr.findIndex(item => item.score == 95); // 1
实例方法:includes()
查找数组是否存在某一项
var arr = [10, 20, 30, 40];
console.log(arr.includes(40)); // true
indexOf() 和 includes() 的区别
相同点:查找数组中是否存在某一项
不同点:
indexOf():
返回值是 当前项的下标,找不到返回 -1
console.log(arr.indexOf(40)); // 3
如果数组中有 NaN,indexOf() 无法查找,会返回 -1
var arr = [10, 20, NaN]; console.log(arr.indexOf(NaN)); // -1
includes()
返回值是 布尔值,true || false
console.log(arr.includes(40)); // true
如果数组中有 NaN,includes() 会进行查找
var arr = [10, 20, NaN]; console.log(arr.includes(NaN)); // true
startsWith 方法 和 endWith 方法
作用:查找字符串中是否以 … 开始,或者以 … 结束
返回值:布尔类型(true 包含 || false 不包含)
startsWith ()
以 … 开始
var str = 'hello wold';
console.log(str.startsWith('h')); // true
endWith ()
以 … 结束
var str = 'hello wold';
console.log(str.startsWith('ld')); // true
repeat() 方法
作用:将字符串重复指定次数,返回一个新字符串
/ 语法格式:
字符串.repeat(次数);
var str = '张三';
console.log(str.repeat(3)); // 张三张三张三
Set() 方法
Set() 去重
作用:实现数组去重
方法中的 set 属性:新数组中的长度
var arr = [10, 10, 20, 20, 30];
var newArr = new Set(arr);
console.log(newArr); // {10, 20, 30}
console.log(newArr.size); // 3
Set() 添加数据
可以采用链式编程
var set = new Set();
Set.add(100)
.add('李四');
Set() 查找是否包含
var set = new Set();
set.has(100);
Set() 删除数据
var set = new Set();
set.delect('李四');
Set() 清除所有数据
var set = new Set();
Set.clear();
**注意:**set() 方法,得到的数组是一个伪数组
不可以for循环遍历。不可以使用 数组的方法,需要使用 展开运算符 或 Array.from() 进行转数组
虽然 set()得到的是一个伪数组,但是可以使用 **for in **进行遍历
浅拷贝
将引用类型的指针拷贝给新对象
注意:
由于拷贝的是同一个指针,指针指向堆内存中同一块空间,所以修改数据时会相互影响
var o = {
name: '张三',
}
var obj = o;
obj.name = '李四';
console.log(o); // 李四
深拷贝
将引用类型各项属性的值拷贝给新对象
方式一:手动拷贝
手动拷贝要一个属性一个属性进行拷贝
var o = {
name: '张三',
info: {
age: 17,
}
}
var r = {
name: o.name,
info: o.info() {
age: o.info.age,
}
}
方式二:Object.assign();
只能实现一层的深拷贝
重复的属性,后面会覆盖前面的属性
/ 语法格式:
Object.assign(目标对象, 待拷贝的对象1, 待拷贝的对象2, 待拷贝的对象3, );
方式三:JSON做字符串转换
只有可以转成JSON格式的对象才可以这样用,像function没办法转成 JSON
var o = {
name: '张三',
info: {
age: 17,
}
}
var r = JSON.parse(JSON.stringify(o));
方式四:slice()
slice() 截取也不会影响,所以可以使用 slice(0) 从第零项截取到最后一项,实现深拷贝。
var arr = [10, 20, 30];
var r = arr.slice(0);
r[0] = 100;
console.log(arr); // [10, 20, 30]
方式五:递归拷贝
var o = {
name: '张三',
info: {
age: 17,
},
arr: [10, 20],
fn () {
console.log(100);
}
}
function deepCopy (obj) {
// 如果当前类型是不是基本类型,是就 return,不是就继续
if (typeof obj !== 'object' || typeof obj == null) {
return;
}
// 判断当前是对象还是数组,用于创建存储器
let result = obj.__proto__.constructor.name == 'object' ? {} : [];
for (const key in obj) {
// 如果当前项是object,就重新调用拷贝函数,遍历挡墙项,如果不是 object,则递归到新对象。
result[key] = typeof obj[key] == 'object' ? deepCopy(obj[key]) : obj[key];
}
// 返回新对象
return result;
}
deepCopy(o);
console.log(deepCopy(o));