ECMAScript2015
🔱let变量声明及其特性
- 变量不能重复声明
错误示例:
let star = '罗志祥'
let star = '小猪'
- 块级作用域(全局、函数、eval)
错误示例:
if else while for
{
let girl = '周扬青'
}
console.log(girl)
- 不存在变量提升
错误示例:
console.log(song)
let song = '恋爱达人'
- 不影响作用域链
{
let school = '尚硅谷'
function fn() {
console.log(school)
}
fn()
}
- let实践案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>点击 DIV 换色</title>
<link crossorigin="anonymous" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet">
<style>
.item {
width: 100px;
height: 50px;
border: solid 1px rgb(42, 156, 156);
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="container">
<h2 class="page-header">点击切换颜色</h2>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<script>
//获取div元素对象
let items = document.getElementsByClassName('item');
//遍历并绑定事件
for (let i = 0; i < items.length; i++) {
items[i].onclick = function () {
//修改当前元素的背景颜色
//this.style.background = 'pink';
items[i].style.background = 'pink';
}
}
</script>
</body>
</html>
🔱const常量声明及其特性
- 声明时一定要赋初值
- 一般常量使用大写(潜规则)
- 常量的值不能修改
- 块级作用域
- 对于 数组和对象 的元素的修改,不算作对常量的修改,不会报错
🔱变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
数组的解构:
const F4 = ['小沈阳', '刘能', '赵四', '宋小宝'];
let [xiao, liu, zhao, song] = F4;
console.log(xiao);
console.log(liu);
console.log(zhao);
console.log(song);
对象的解构:
const zhao = {
name: '赵本山',
age: '不详',
xiaopin: function () {
console.log("我可以演小品");
}
};
let { name, age, xiaopin } = zhao;
console.log(name);
console.log(age);
console.log(xiaopin);
xiaopin();
// let { xiaopin } = zhao;
// xiaopin();
🔱模板字符串
ES6引入新的声明字符串的方式『``』。
- 声明
let str = `我也是一个字符串哦!`;
console.log(str, typeof str);
- 内容中可以直接出现换行符
let str = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`;
- 变量拼接
let lovest = '魏翔';
let out = `${lovest}是我心目中最搞笑的演员!!`;
console.log(out);
🔱对象属性的简写形式
ES6允许在大括号里面直接写入变量和函数,作为对象的属性和方法。
let name = '尚硅谷';
let change = function () {
console.log('我们可以改变你!!');
};
const school = {
name,
change,
improve() {
console.log("我们可以提高你的技能");
}
};
console.log(school);
🔱箭头函数声明及其特性
ES6允许使用『箭头』
=>
定义函数。
- 声明一个函数
let fn = (a, b) => {
return a + b;
};
- 调用函数
let result = fn(1, 2);
console.log(result);
特性:
this
是静态的,this
始终指向函数声明时所在作用域下的this
的值
function getName() {
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
};
//设置 window 对象的 name 属性
window.name = '尚硅谷';
const school = {
name: "ATGUIGU"
};
//直接调用
getName(); // 尚硅谷
getName2(); // 尚硅谷
//call 方法调用
getName.call(school); // ATGUIGU
getName2.call(school); // 尚硅谷
- 不能作为构造函数去实例化对象
let Person = (name, age) => {
this.name = name;
this.age = age;
};
let me = new Person('xiao', 30); // 报错❌❌❌
- 不能使用
arguments
变量
let fn = () => {
console.log(arguments); // 报错❌❌❌
};
fn(1, 2, 3);
- 箭头函数的简写:当形参有且只有一个的时候可以省略小括号
let add = n => {
return n + n;
};
console.log(add(9));
- 箭头函数的简写:当代码块只有一条语句时可以省略花括号(此时
return
也必须省略),这一条语句的执行结果就是函数的返回值
let pow = n => n * n;
console.log(pow(8));
案例一:点击 div 2秒后颜色变成粉色
// 获取元素
let ad = document.getElementById('ad');
// 绑定事件
ad.addEventListener("click", function () {
// 保存 this 的值
// let _this = this;
// 定时器
setTimeout(() => {
console.log(this);
// 修改背景颜色 this
// _this.style.background = 'pink';
this.style.background = 'pink';
}, 2000);
});
案例二:从数组中返回偶数的元素
const arr = [1, 6, 9, 10, 100, 25];
// 方法1
const result = arr.filter(function (item) {
if (item % 2 === 0) {
return true;
} else {
return false;
}
});
// 方法2
const result = arr.filter(item => item % 2 === 0);
console.log(result);
小总结:
箭头函数适合与this
无关的回调,如:定时器、数组的方法回调。
箭头函数不适合与this
有关的回调,如:事件的回调、对象的方法。
🔱函数形参的默认值设置
ES6允许给函数参数赋初始值。
- 形参具有默认值的参数,一般位置要靠后(潜规则)
错误示例:
function add(a, c = 10, b) {
return a + b + c;
}
let result = add(1, 2);
console.log(result); // NaN
- 与解构赋值结合使用
function connect({ host = "127.0.0.1", username, password, port }) {
console.log(host)
console.log(username)
console.log(password)
console.log(port)
}
connect({
host: 'atguigu.com',
username: 'root',
password: 'root',
port: 3306
})
🔱函数形参的rest参数
ES6引入
rest
参数,用于获取函数的实参,用来代替arguments
。
- 回顾ES5获取实参的方式
function date() {
console.log(arguments); // arguments是对象
}
date('白芷', '阿娇', '思慧');
- ES6中函数的rest参数
function date(...args) {
console.log(args); // args是数组
}
date('阿娇', '柏芝', '思慧');
- 注:形参有多个且有rest参数时,rest参数必须放在最后
function fn(a, b, ...args) {
console.log(a);
console.log(b);
console.log(args);
}
fn(1, 2, 3, 4, 5, 6);
🔱扩展运算符〔…〕
扩展运算符能将『数组』转换为逗号分隔的『参数序列』。
const tfboys = ['易烊千玺', '王源', '王俊凯'];
function chunwan() {
console.log(arguments);
}
chunwan(...tfboys);
// 相当于
chunwan('易烊千玺', '王源', '王俊凯');
扩展运算符的应用:
- 数组的合并:
const kuaizi = ['王太利', '肖央'];
const fenghuang = ['曾毅', '玲花'];
// const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
console.log(zuixuanxiaopingguo);
- 数组的克隆:
const sanzhihua = ['E', 'G', 'M'];
const sanyecao = [...sanzhihua]; // ['E', 'G', 'M']
console.log(sanyecao);
- 将伪数组转为真正的数组:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div></div>
<div></div>
<div></div>
<script>
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divs);
console.log(divArr);
</script>
</body>
</html>
结果:
🔱一个新的数据类型〔Symbol〕
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol的特点:
①Symbol的值是唯一的,用来解决命名冲突的问题。
②Symbol值不能与其他数据进行运算。
③Symbol定义的对象属性不能使用for...in
循环遍历,但是可以使用Reflect.ownKeys
来获取对象的所有键名。
注:遇到唯一性的场景时要想到Symbol
- 创建Symbol
// 创建Symbol
let s = Symbol();
console.log(s, typeof s); // Symbol() 'symbol'
let s2 = Symbol('尚硅谷');
let s3 = Symbol('尚硅谷');
console.log(s2 === s3); // false
// Symbol.for 创建
/*
Symbol.for() 类似单例模式,
首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,
如果有即返回该 Symbol 值,
若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。
*/
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
console.log(s4 === s5); // true
// 不能与其他数据进行运算,以下全都报错❌❌❌
// let result = s + 100;
// let result = s > 100;
// let result = s + s;
- 对象添加Symbol类型的属性
由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。
// 向对象中添加方法 up down
let game = {
name: '俄罗斯方块',
up: function () { },
down: function () { }
}
// 声明一个对象
let methods = {
up: Symbol(),
down: Symbol()
}
// 不确定game对象有没有up或down方法,但是我们想要给game对象添加up和down的功能
game[methods.up] = function () {
console.log("我可以改变形状")
}
game[methods.down] = function () {
console.log("我可以快速下降")
}
game[methods.up]() // 我可以改变形状
game[methods.down]() // 我可以快速下降
console.log(game)
let youxi = {
name: "狼人杀",
[Symbol('say')]: function () {
console.log("我可以发言")
},
[Symbol('zibao')]: function () {
console.log('我可以自爆')
}
}
// 这里不能调用youxi的方法,因为那两个方法没有事先用变量名保存
console.log(youxi)
- Symbol内置属性
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法,可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
class Person {
static [Symbol.hasInstance](param) {
console.log(param);
console.log("我被用来检测类型了");
return false;
}
}
let o = { a: 1, b: 2 };
console.log(o instanceof Person); // false
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2));
结果:
🔱迭代器〔for…of〕
------迭代器(iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作。
------ES6创造了一种新的遍历命令for...of
循环,iterator接口主要供for...of
消费。
------原生具备iterator接口的数据(可用for...of
遍历):Array
、Arguments
、Set
、Map
、String
、TypedArray
、NodeList
。
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
// 使用 for...in 遍历数组
for (let v in xiyou) {
console.log(v);
}
// 使用 for...of 遍历数组
for (let v of xiyou) {
console.log(v);
}
// 调用xiyou的Symbol.iterator方法,获取方法返回的对象,这个对象有next方法
let iterator = xiyou[Symbol.iterator]();
// 调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
结果:
工作原理:
- 创建一个指针对象,指向当前数据结构的起始位置。
- 第一次调用对象的
next
方法,指针自动指向数据结构的第一个成员。 - 接下来不断调用
next
方法,指针一直往后移动,直到指向最后一个成员。 - 每次调用
next
方法返回一个包含value
和done
属性的对象。
注:需要自定义遍历数据的时候,要想到迭代器。
迭代器自定义遍历对象:
//声明一个对象
const banji = {
name: "终极一班",
stus: [
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false };
//下标自增
index++;
//返回结果
return result;
} else
return { value: undefined, done: true };
}
};
}
};
//遍历这个对象
for (let v of banji) {
console.log(v);
}
结果:
🔱生成器函数
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
function* gen() {
console.log(111);
yield '一只没有耳朵';
console.log(222);
yield '一只没有尾巴';
console.log(333);
yield '真奇怪';
console.log(444);
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log('--------------------------------------------------');
for (let v of gen()) {
console.log(v);
}
结果:
function* gen(arg) {
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}
//执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());
//next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));
结果:
代码说明:
- 生成器函数返回的结果是迭代器对象,调用迭代器对象的
next
方法可以得到yield
语句后的值。 yield
相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次next
方法就会执行一段代码。next
方法可以传递实参,作为函数中当前执行代码段开始处的yield
语句的返回值。
案例一:1s后控制台输出111, 2s后输出222, 3s后输出333
- 以前的做法(回调地狱):
setTimeout(() => {
console.log(111)
setTimeout(() => {
console.log(222)
setTimeout(() => {
console.log(333)
}, 3000)
}, 2000)
}, 1000)
- 现在的做法:
function one() {
setTimeout(() => {
console.log(111)
iterator.next()
}, 1000)
}
function two() {
setTimeout(() => {
console.log(222)
iterator.next()
}, 2000)
}
function three() {
setTimeout(() => {
console.log(333)
iterator.next()
}, 3000)
}
function* gen() {
yield one()
yield two()
yield three()
}
//调用生成器函数
let iterator = gen()
iterator.next()
案例二:模拟获取:用户数据,订单数据,商品数据
function getUsers() {
setTimeout(() => {
let data = '用户数据'
//调用 next 方法, 并且将数据传入
iterator.next(data)
}, 1000)
}
function getOrders() {
setTimeout(() => {
let data = '订单数据'
iterator.next(data)
}, 1000)
}
function getGoods() {
setTimeout(() => {
let data = '商品数据'
iterator.next(data)
}, 1000)
}
function* gen() {
let users = yield getUsers()
console.log(users)
let orders = yield getOrders()
console.log(orders)
let goods = yield getGoods()
console.log(goods)
}
//调用生成器函数
let iterator = gen()
iterator.next()
结果:
🔱Promise对象
Promise是ES6引入的异步编程的新的解决方案。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
Promise 状态的特点:
------Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。
------Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
- Promise基本语法
// 实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
setTimeout(function () {
// let data = '数据库中的用户数据'
// resolve(data)
let err = '数据读取失败'
reject(err)
}, 1000)
})
// 调用 Promise 对象的 then 方法
p.then(function (value) {
console.log(value)
}, function (reason) {
console.error(reason)
})
- Promise封装读取文件(node运行环境)
//1. 引入 fs 模块
const fs = require('fs')
//2. 调用方法读取文件
fs.readFile('./resources/为学.md', (err, data) => {
//如果失败, 则抛出错误
if (err) throw err
//如果没有出错, 则输出内容
console.log(data.toString())
})
//3. 使用 Promise 封装
const p = new Promise(function (resolve, reject) {
fs.readFile("./resources/为学.mda", (err, data) => {
//判断如果失败
if (err) reject(err)
//如果成功
resolve(data)
})
})
p.then(function (value) {
console.log(value.toString())
}, function (reason) {
console.log("读取失败!")
})
- Promise封装Ajax
//接口地址: https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest()
//2. 初始化
xhr.open("GET", "https://api.apiopen.top/getJ")
//3. 发送
xhr.send()
//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
//判断
if (xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
resolve(xhr.response)
} else {
//如果失败
reject(xhr.status)
}
}
}
})
//指定回调
p.then(function (value) {
console.log(value)
}, function (reason) {
console.error(reason)
})
- then 方法
then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。
//创建 Promise 对象
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('用户数据')
reject('出错啦')
}, 1000)
})
//调用 then 方法, then方法的返回结果是 Promise 对象, 对象状态由回调函数的执行结果决定
//如果回调函数中返回的结果是 非Promise 类型的属性, 状态为成功, 返回值为对象的成功的值
const result = p.then(value => {
console.log(value)
//1. 非 Promise 类型的属性
return 'iloveyou'
//2. 是 Promise 对象
return new Promise((resolve, reject) => {
resolve('ok')
reject('error')
})
//3. 抛出错误
throw '出错啦!'
//4. 不返回任何值则是undefined, 状态也是成功
}, reason => {
console.warn(reason)
})
console.log(result) // result也是Promise对象
- catch 方法
简单的语法糖,可以用 then 方法替代
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// 设置 p 对象的状态为失败, 并设置失败的值
reject("出错啦!")
}, 1000)
})
// 方式一:
p.then(function (value) { }, function (reason) {
console.error(reason) // 出错啦!
})
// 方式二:
p.catch(function (reason) {
console.warn(reason) // 出错啦!
})
链式调用:
//创建 Promise 对象
const p = new Promise((resolve, reject) => {
resolve('用户数据')
})
//链式调用
const result =
p
.then(value => {
console.log('value1:', value)
return new Promise(function (resolve, reject) {
reject('出错啦')
})
}, reason => {
console.log('reason1:', reason)
})
.then(value => {
console.log('value2:', value)
}, reason => {
console.log('reason2:', reason)
})
console.log('打印result:', result)
结果:
- Promise实践------读取多个文件(node运行环境)
// 引入 fs 模块
const fs = require("fs")
// 回调地狱实现
fs.readFile('./resources/为学.md', (err, data1) => {
fs.readFile('./resources/插秧诗.md', (err, data2) => {
fs.readFile('./resources/观书有感.md', (err, data3) => {
let result = data1 + '\r\n' + data2 + '\r\n' + data3
console.log(result)
})
})
})
// 使用 Promise 实现
new Promise((resolve, reject) => {
fs.readFile("./resources/为学.md", (err, data) => {
resolve(data)
})
})
.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/插秧诗.md", (err, data) => {
resolve([value, data])
})
})
})
.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
//压入
value.push(data)
resolve(value)
})
})
})
.then(value => {
console.log(value.join('\r\n'))
})
🔱Set(集合)对象
ES6提供了新的数据结构
Set
(集合),它类似于数组,但成员的值都是唯一的。集合实现了iterator
接口,所以可以使用〔扩展运算符...
〕和〔for...of
〕进行遍历。
// 声明一个 set
let s = new Set()
console.log(typeof s)
let s2 = new Set(['大事儿', '小事儿', '好事儿', '坏事儿', '小事儿'])
console.log(s2)
for (let v of s2) {
console.log(v)
}
// 元素个数
console.log(s2.size)
// 添加新的元素
s2.add('喜事儿')
console.log(s2)
// 删除元素
s2.delete('坏事儿')
console.log(s2)
// 检测
console.log(s2.has('糟心事'))
// 清空
s2.clear()
console.log(s2)
结果:
Set 实践:
- 数组去重:
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let result = [...new Set(arr)]
console.log(result)
// (5) [1, 2, 3, 4, 5]
- 交集:
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let arr2 = [4, 5, 6, 5, 6]
// 方法一
/* let result = [...new Set(arr)].filter(item => {
let s2 = new Set(arr2)
if (s2.has(item))
return true
else
return false
}) */
// 方法二
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item))
console.log(result)
// (2) [4, 5]
- 并集:
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let arr2 = [4, 5, 6, 5, 6]
let union = [...new Set([...arr, ...arr2])]
console.log(union)
// (6) [1, 2, 3, 4, 5, 6]
- 差集:
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let arr2 = [4, 5, 6, 5, 6]
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)))
console.log(diff)
// (3) [1, 2, 3]
🔱Map对象
ES6提供了
Map
数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作“键”。Map
也实现了iterator
接口,所以可以使用〔扩展运算符...
〕和〔for...of
〕进行遍历。
// 声明 Map
let m = new Map()
// 添加元素
m.set('name', '尚硅谷')
m.set('change', function () {
console.log("我们可以改变你!")
})
let key = {
school: 'ATGUIGU'
}
m.set(key, ['北京', '上海', '深圳'])
console.log(m)
// 遍历
for (let v of m) {
console.log(v)
}
// size
console.log(m.size)
// 删除
m.delete('name')
console.log(m)
// 获取
console.log(m.get('change'))
console.log(m.get(key))
// 清空
m.clear()
console.log(m)
结果:
🔱class类
ES6提供了更接近传统语言的写法,引入了class(类)这个概念,作为对象的模板。通过class关键字可以定义类。基本上,ES6的class可以看作只是一个语法糖(class 的本质是 function),它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
class
声明类
constructor
定义构造函数初始化
//ES5
function Phone(brand, price) {
this.brand = brand
this.price = price
}
//添加方法
Phone.prototype.call = function () {
console.log("我可以打电话!!")
}
//实例化对象
let Huawei = new Phone('华为', 5999)
Huawei.call()
console.log(Huawei)
//ES6 class
class Shouji {
//构造方法 名字不能修改
constructor(brand, price) {
this.brand = brand
this.price = price
}
//方法必须使用该语法, 不能使用 ES5 的对象完整形式
call() {
console.log("我可以打电话!!")
}
}
let onePlus = new Shouji("1+", 1999)
console.log(onePlus)
结果:
static
定义静态属性和方法
static
定义的属性和方法是构造函数对象的属性和方法,其实例化对象不能共享
class Phone {
// 静态属性中不能有name属性, 因为name属性的值是'Phone', 已经自带了, 且不可改变
static shouji = '手机'
static change() {
console.log("我可以改变世界")
}
}
console.dir(Phone)
let nokia = new Phone()
console.log(nokia.shouji) // undefined
console.log(nokia.change) // undefined
console.log(Phone.shouji) // 手机
Phone.change() // 我可以改变世界
- 回顾ES5的原型链继承模式之组合继承
//手机
function Phone(brand, price) {
this.brand = brand
this.price = price
}
Phone.prototype.call = function () {
console.log("我可以打电话")
}
//智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price)
this.color = color
this.size = size
}
//设置子级构造函数的原型
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone
//声明子类的方法
SmartPhone.prototype.photo = function () {
console.log("我可以拍照")
}
SmartPhone.prototype.playGame = function () {
console.log("我可以玩游戏")
}
const chuizi = new SmartPhone('锤子', 2499, '黑色', '5.5inch')
console.log(chuizi)
结果:
- ES6实现类继承
class Phone {
// 构造方法
constructor(brand, price) {
this.brand = brand
this.price = price
}
// 父类的成员属性
call() {
console.log('我可以打电话')
}
}
// 通过 extends 实现类的继承
class SmartPhone extends Phone {
constructor(brand, price, color, size) {
// 子类 constructor 方法中必须有 super , 且必须出现在 this 之前
// super 调用父类构造函数
super(brand, price) // 相当于Phone.call(this, brand, price)
this.color = color
this.size = size
}
photo() {
console.log('我可以拍照')
}
game() {
console.log('我可以玩游戏')
}
// 重写父类的call方法
call() {
// super.call() // 弹幕网友说这样可以调父类的call方法, 试了确实可以
// 调用父类方法, super作为对象, 在普通方法中, 指向父类的原型对象, 在静态方法中, 指向父类
// 这里是在普通方法中使用super, 所以super就是父类的原型对象, 也就是调用父类原型对象上的call方法
// 以上三行注释都是讲的'super.call()'这句代码
console.log('我可以进行视频通话')
}
}
let xiaomi = new SmartPhone('小米', 1999, '黑色', 5.5)
console.log(xiaomi)
xiaomi.photo()
xiaomi.game()
xiaomi.call()
let dageda = new Phone('大哥大', 999)
console.log(dageda)
dageda.call()
结果:
- get 和 set 属性
class Example {
constructor(a, b) {
this.a = a // 实例化时调用set方法
this.b = b
}
get a() {
console.log('getter被调用了')
return this.c
}
set a(newValue) { // 括号里的值为修改后的值
console.log('setter被调用了')
this.c = newValue
}
}
let exam = new Example(5, 10)
// 上一行代码执行完后输出'setter被调用了', 此时exam有三个属性{b: 10, c: 5, a: (...)}
console.log(exam)
// 上一行代码执行完后输出exam对象
console.log(exam.a)
// 上一行代码执行完后输出'getter被调用了', 然后又输出'5'(这是c的'5')
console.log(exam.c)
// 上一行代码执行完后输出c, 值为'5'
exam.a = 20
// 上一行代码执行完后输出'setter被调用了', 修改的不是a, 而是c, 修改c值为'20'
console.log(exam.c)
// 上一行代码执行完后输出c, 值为'20'
🔱数值的扩展
// Number.EPSILON 是 JavaScript 表示的最小精度
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON)
return true
else
return false
}
console.log(0.1 + 0.2 === 0.3) // false
console.log(equal(0.1 + 0.2, 0.3)) // true
// 二进制和八进制
let b = 0b1010
let o = 0o777
console.log(b) // 10
console.log(o) // 511
let d = 100
let x = 0xff
console.log(d) // 100
console.log(x) // 255
// Number.isFinite 检测一个数值是否为有限数
console.log(Number.isFinite(100)) // true
console.log(Number.isFinite(100 / 0)) // false
console.log(Number.isFinite(Infinity)) // false
// Number.isNaN 检测一个数值是否为 NaN
console.log(Number.isNaN(123)) // false
// Number.parseInt Number.parseFloat 字符串转整数
console.log(Number.parseInt('5211314love')) // 5211314
console.log(Number.parseFloat('3.1415926神奇')) // 3.1415926
// Number.isInteger 判断一个数是否为整数
console.log(Number.isInteger(5)) // true
console.log(Number.isInteger(2.5)) // false
// Math.trunc 将数字的小数部分抹掉
console.log(Math.trunc(3.5)) // 3
// Math.sign 判断一个数到底为正数、负数还是零
console.log(Math.sign(100)) // 1
console.log(Math.sign(0)) // 0
console.log(Math.sign(-20000)) // -1
🔱对象的扩展
// Object.is 判断两个值是否完全相等
console.log(Object.is(120, 120)) // true
console.log(Object.is(NaN, NaN)) // true
console.log(NaN === NaN) // false
// Object.assign 对象的合并
const config1 = {
host: 'localhost',
port: 3306,
name: 'root',
pass: 'root',
test: 'test'
}
const config2 = {
host: 'http://atguigu.com',
port: 33060,
name: 'atguigu.com',
pass: 'iloveyou',
test2: 'test2'
}
console.log(Object.assign(config1, config2))
/*
{host: "http://atguigu.com"
name: "atguigu.com"
pass: "iloveyou"
port: 33060
test: "test"
test2: "test2"}
*/
// Object.setPrototypeOf 设置原型对象
// Object.getPrototypeof 获取原型对象
const school = {
name: '尚硅谷'
}
const cities = {
xiaoqu: ['北京', '上海', '深圳']
}
Object.setPrototypeOf(school, cities)
console.log(Object.getPrototypeOf(school)) // {xiaoqu: Array(3)}
console.log(school) // {name: '尚硅谷'}
🔱模块化
m1.js(分别暴露):
export let school = '尚硅谷'
export function teach() {
console.log("我们可以教给你开发技能")
}
m2.js(统一暴露):
let school = '尚硅谷'
function findJob() {
console.log("我们可以帮助你找工作")
}
export { school, findJob }
m3.js(默认暴露):
export default {
school: 'ATGUIGU',
change: function () {
console.log("我们可以改变你")
}
}
- 方式一,直接在主html文件里面导入模块
index.html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script type="module">
/* 通用的导入方式 */
import * as m1 from "./src/js/m1.js"
console.log(m1)
import * as m2 from "./src/js/m2.js"
console.log(m2)
import * as m3 from "./src/js/m3.js"
console.log(m3)
/* 解构赋值形式 */
import { school, teach } from "./src/js/m1.js"
console.log(school)
teach()
// guigu是school的别名
import { school as guigu, findJob } from "./src/js/m2.js"
console.log(guigu)
findJob()
import { default as m33 } from "./src/js/m3.js"
console.log(m33.school)
m33.change()
/* 简便形式, 只针对默认暴露 */
import m333 from "./src/js/m3.js"
console.log(m333.school)
m333.change()
</script>
</html>
结果:
- 方式二,在主html文件中只引入一个js文件叫
app.js
,这个js文件用来管理所有模块
app.js:
/* 入口文件 */
import * as m1 from "./m1.js"
import * as m2 from "./m2.js"
import * as m3 from "./m3.js"
m1.teach()
m2.findJob()
m3.default.change()
/* 结果:
我们可以教给你开发技能
我们可以帮助你找工作
我们可以改变你
*/
index.html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script src="./src/js/app.js" type="module"></script>
</html>