HTML5
跨域
跨域的原因:浏览器的同源策略导致
同源策略要求:网页中利用ajax发送请求时,目标服务器的域名 端口号 协议 都与当前网页相同
同源: 协议 + 域名 + 端口号 三者都相同
协议://域名:端口号
比如 http://localhost:3000
协议:http; 域名:localhost; 端口号:3000;
网页的开发方案分两类:
- 前后端不分离: 由服务器完成所有任务, 服务器存在极大的压力
- 前后端分离: 服务器负责查数据库, 浏览器服务器拼接成html; 服务器把拼接HTML的任务分离出去, 自身压力减轻, 更适合当前场景.
跨域的解决方案主流存在3种
- CORS:
最推荐
- 在服务器上增加白名单 允许其他来源的访问 - JSONP: 利用 脚本的 src 发请求不存在跨域限定 来解决问题
- 缺点: 仅适用于 GET 请求
- PROXY: 代理
解决方案(三种)
cors
在服务器中为响应头添加Access-Control-Allow-Origin,允许其他来源访问
JSONP
代理方案: 不需要在服务器上进行修改, 前端人员自己就能解决跨域
- 1.JSON Padding 利用 脚本的src属性请求数据 这种方式不存在跨域限定
- 2.需要在请求地址中,利用参数传递回调函数的名字
<script src=“xxxx?callback=函数名”>
- 3.服务器中,需要把数据拼接到 回调函数中
res.send(callback + ( + ' JSON数据 ' )')
- 特点: 仅支持GET请求 + 仅支持JSON格式数据的传递
proxy
代理模式. 仅需要前端解决. 无需修改服务器代码
- 过程: html --请求1–> express服务器 —请求2–> 斗鱼服务器
- 请求1: 存在跨域问题; 请求2: 无跨域问题, 服务器之间的交互.
- 代理方案: 不需要在服务器上进行修改, 前端人员自己就能解决跨域
XSS: 跨站脚本攻击
英文名:
Cross Site Script Attack
理论上简写是: CSS, 但是这个名称和 层叠样式表的缩写重名, 所以改为
XSS
我们的网站中, 如果使用了来自其他网站提供的脚本代码 – 是不安全的
做法
如果脚本代码中存在恶意代码, 会导致网站被攻击!!
总结: 不要使用不受信任的网站提供的脚本, 应该从官方下载
最好把脚本下载到本地使用, 而不是使用远程脚本. 有效防止远程脚本变更导致的安全性问题!!
文件上传
利用multer模块方案
需要利用 uuid 模块生成唯一的文件名保存到服务器中
前端部分
Form
form表单方式能够实现文件上传 但是限制很多
- 上传文件大小受限
- 上传速度慢
- 不支持断点续传
- 不支持上传进度显示
<!-- 文件上传的最基本方案: Form表单方案 -->
<!-- action: 当提交操作触发时, 内容传递的地址 -->
<!-- entype: 编码类型. 如果文件上传则必须是 multipart/form-data -->
<form action="http://localhost:3000/avatar" method="post" enctype="multipart/form-data">
<!-- 这里的name属性, 就是服务器接口的 upload.single(参数) 的参数 -->
<input type="file" name="avatar" id="">
<button>开始上传</button>
</form>
app.js 服务器代码
<!-- 使用时, 不允许用 live server !! 自带刷新效果 会出现问题 -->
<!-- 要使用 localhost:3000 的方案来访问 -->
<script src="https://api.xin88.top/js/jquery-3.6.1.min.js"></script>
const express = require('express');
const cors = require('cors'); // 跨域模块
// multer: 负责处理上传文件
const multer = require('multer');
// uuid : 用于生成不重复的 由英文组成的字符串, 适合做文件的唯一名称
const uuid = require('uuid');
const app = express()
app.use(cors())
app.use(express.static('public'))
// 允许 上传文件夹 中的内容, 被外部访问
app.use(express.static('upload'))
// multer 创建负责处理上传文件的对象
const upload = multer({
// ctrl + i : 查看提示
// dest -> destination 目标,目的地
// 代表上传文件存储的位置 - 目录/文件夹
// 此目录如果不存在,会自动创建
// dest: 'upload',
// 默认的上传设定: 会把文件重用名, 但是没有后缀名, 导致文件无法查看
// storage: 可以个性化对上传的文件 的保存方式进行定制
storage: multer.diskStorage({
destination: 'upload', //配置保存的目录
// 用于接收上传的内容, 然后给出文件存储时的名称
filename(req, file, cb) {
console.log('文件信息:', file);
// 读取上传的文件名
const { originalname } = file
// 从原名称中截取后缀名的部分
const i = originalname.lastIndexOf('.')
// 截取 . 开始的内容
const ext = originalname.substring(i) // .jpg .png ...这种
// 生成唯一名称
const unique_name = uuid.v4() + ext // v4 是最常用的方案
console.log('唯一名称:', unique_name);
// cb : callback缩写, 回调函数. 通过此函数设置文件的存储名称
// cb(错误信息, 文件存储时的名称)
cb(null, unique_name)
// 参数1: null 代表没有错误
// 直接采用原文件名存在两个BUG
// 1. 中文文件名 会乱码
// 2. 同名文件 会覆盖
}
})
})
app.listen(3000, () => {
console.log('服务器已开启, 端口3000');
})
// 制作接口, 用与接收用户上传的头像图片
// 上传操作要求 必须为 POST 方案
// single: 单个,单独的; 代表要处理单文件上传
// 'avatar' : 代表通过post传递的数据中, 名字是avatar的是文件类型,需要处理
app.post('/avatar', upload.single('avatar'), (req, res) => {
console.log(req.file)
res.send(req.file)
})
DOM方案
- 利用DOM方式可以实现更加自定义的上传方式
- 需要把文件的信息存储在FormDate类型的对象里
- 使用jQuery的ajax方法实现
<!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>首页</title>
<style>
#progress {
height: 20px;
background-color: #aaa;
width: 200px;
}
#progress>#thumb {
height: 100%;
background-color: orange;
width: 0%;
transition: 0.1s;
}
</style>
</head>
<body>
<h1>Welcome To Express!</h1>
<!-- 用DOM的方式, 可以实现更加灵活的上传 -->
<input type="file">
<br>
<button id="upload">上传文件</button>
<div id="progress">
<div id="thumb"></div>
</div>
<img src="" alt="">
<!-- 运行时 必须用 localhost:3000 -->
<!-- 因为live server 自带刷新, 与上传操作有冲突! -->
<script src="https://api.xin88.top/js/jquery-3.6.1.min.js"></script>
<script>
// 思路: 点击上传按钮后, 获取 上传文件 的信息, 通过结果传递给服务器
$('button#upload').click(function () {
// 查看文件上传元素中的值
// console.log($(':file'))
// files属性: 存储文件上传的信息
const files = $(':file').prop('files')
console.log('files:', files)
// 1. 先检查上传文件的个数, 如果是0个 则可以弹出提醒
if (files.length == 0) {
alert("请先选择文件, 再进行上传操作")
return
}
// 2. 限制只能传递 图片类型的文件
// type 的值中 含有 image 字样, 就是图片类型. 反之 不含有image 就不是图片
// indexOf() : 值 -1 代表找不到
if (files[0].type.indexOf('image') == -1) {
alert("只允许图片类型文件的上传!!")
return
}
// 大小检测: 只允许上传 2M 以内文件
// 2MB = 2*1024KB = 2 * 1024 * 1024B
if (files[0].size > 2 * 1024 * 1024) {
alert("只允许上传2M以内的文件!")
return
}
// 需要把文件数据封装到 FormData 类型的对象中, 才能被服务器接收
const fd = new FormData()
// 利用 append 方法 把文件数据添加到 fd 中
fd.append('avatar', files[0])
// 参数1: 数据的名字, 相当于 <input type='file' name="avatar"> 中的name属性. 服务器接口中会点名接收此属性的值
// 利用POST方式进行上传
// 需要使用更加自定义的 ajax 方法实现
$.ajax({
// ctrl+i: 可以查看可选的配置项
url: 'http://localhost:3000/avatar', //接口地址
type: 'post', //请求类型
data: fd, // 传输的数据
// 内容类型: false代表不允许更改,采用原始数据类型;
// 默认会自动更改为 字符串类型
contentType: false,
// 数据编码方式: false代表不对数据进行编码, 保持原有状态.
processData: false,
// 监听上传进度
xhr() {
// 提供自定义的xhr 对象实现网络操作, 可以添加进度的监听
var xhr = new XMLHttpRequest()
// progress事件: 当上传进度发生变化时触发
xhr.upload.addEventListener('progress', e => {
// 事件参数 e 中, 包含本次事件的相关信息
// console.log('事件参数:', e);
// 总大小, 已加载大小
const { total, loaded } = e
const num = loaded / total;
const percentage = (num * 100).toFixed(0) + '%';
console.log(percentage);
console.log('加载进度:', percentage);
$('#thumb').width(percentage)
})
return xhr
},
// 上传完毕后的回调函数
success(data) {
console.log('上传结果:', data)
// 读取存储在服务器上的文件名
const { filename } = data
// 把存储在服务器上的文件, 用 img 标签进行展示
$('img').prop('src', `http://localhost:3000/${filename}`)
.height(100)
}
})
})
</script>
</body>
</html>
拖拽操作
- 由HTML5 提供的新的交互方式, 其中 超链接 和 图标 默认支持拖拽, 其他元素不支持, 需要添加
draggable="true"
来开启 - 7个事件
- 3个与被拖拽元素相关
- dragstart:开始拖拽
- 事件参数中的
数据传输
者: dataTransfer 可以存储一些数据, 可以在7个事件中获取- setData(‘text’, 内容) : 用于存储文本信息
- getData(‘text’) : 用于获取文本信息
- 事件参数中的
- drag: 拖拽中
- dragend: 拖拽结束
- dragstart:开始拖拽
- 4个与拖拽元素 路过的元素相关
- dragenter: 进入
- dragover: 在上方
- 阻止默认事件: 例如 超链接 和 图片 默认在放下时, 会在新标签中打开. 如果希望自定义效果, 则需要阻止默认操作:
e.preventDefault()
- 阻止默认事件: 例如 超链接 和 图片 默认在放下时, 会在新标签中打开. 如果希望自定义效果, 则需要阻止默认操作:
- dragleave: 离开
- drop: 放下
- 3个与被拖拽元素相关
拖拽方式实现多文件上传
- 在 dragover 和 drop 方法中, 要调用
preventDefault
阻止默认行为, 否则会在新标签中开启 - 文件信息存储在
dataTransfer.files
属性里 - 多文件接口的制作:
- 使用
array
方法:upload.array('avatars')
, 保存后的信息存储在req.files
属性里
- 使用
<!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>首页</title>
<style>
#box1 {
height: 200px;
background-color: #aaa;
}
#box2 {
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<h1>Welcome To Express!</h1>
<!-- HTML5 提供的新的交互方式:拖拽 -->
<!-- 超链接 和 图片 标签 默认支持拖拽操作 默认行为:在新的标签中开启 -->
<a href="http://baidu.com">百度一下</a>
<br>
<img height="200" src="https://web.codeboy.com/image/index/banner.png" alt="">
<!-- 默认不支持拖拽的元素 可以添加 draggable="true" 让元素允许被拖拽,但是没有默认行为 -->
<p draggable="true">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Suscipit dicta blanditiis nam magni
eaque obcaecati
repudiandae odit voluptas culpa ut tempore possimus, excepturi facere eos vero ullam esse! Soluta, perferendis?
</p>
<hr>
<div id="box1">
<a href="">百度一下</a>
<a href="">京东</a>
<a href="">Tmooc</a>
</div>
<div id="box2">
</div>
<script src="https://api.xin88.top/js/jquery-3.6.1.min.js"></script>
<script>
// 拖拽相关事件:
$('#box1>a')
.on('dragstart', function (e) {
console.log('dragstart: 开始拖动', e);
// dataTransfer : 数据传输者 - 用于存储一些数据传递给其他事件
// 把当前拖动的 标签的序号 保存在数据传输者中
const i = $(this).index()
// setData(数据类型, 数据本身):用于存储值
// 'text': 固定写法 代表数据是文本类型
e.originalEvent.dataTransfer.setData('text', i)
})
// .on('drag', function(e) {
// e.preventDefault() // 阻止默认行为
// // 存在两种触发形式:1.拖动时 2.不动 每隔0.3s自动触发一次
// // console.log('drag: 拖动中...');
// })
// .on('dragend', function() {
// console.log('dragend: 结束拖动');
// })
// 当有被拖动的元素进入自身时
$('#box2')
// .on('dragenter', function() {
// console.log('dragenter:有拖拽元素进入');
// })
.on('dragover', function(e) {
// 在box2上经过时,阻止拖拽元素的默认行为 在新标签中打开
// prevent:阻止 default:默认
e.preventDefault() // 阻止默认行为
console.log('dragover:拖拽元素在自身内部');
})
// .on('dragleave', function() {
// console.log('dragleave:拖拽元素离开自身范围');
// })
.on('drop', function(e) {
console.log('drop:有元素被放下');
// 检测被放下的元素是哪个 把这个元素添加到 #box2 中
// 读取事件传输者中保存的 文本类型的数据内容
const i = e.originalEvent.dataTransfer.getData('text')
console.log('被拖拽元素的序号:', i);
// 通过序号找到被拖拽的元素
const $a = $('#box1>a').eq(i);
// 添加到 #box2 里
$('#box2').append($a)
})
// 扩展作业:能够从box2中拖拽到 box1
$('#box1')
.on('dragover', function(e) {
e.preventDefault() // 阻止默认,防止在新标签打开
})
.on('drop', function(e) {
// 读取数据传输者中携带的文本信息:当前元素的序号
const i = e.originalEvent.dataTransfer.getData('text');
console.log({ i });
$('#box1').append(
$('#box2>a').eq(i)
)
})
</script>
</body>
</html>
Canvas: 画布
- HTML5提供的标签, 带有一套绘制图形的工具库, 可以利用JS把数据实时转换成图片
- 使用场景: 地图 / 图表 / 3D图形 / 网页游戏
- 这个API 是给 高级程序员使用的, 可以基于此API封装库
ECharts
- 生产力工具: 实际开发中 可以用于解决问题的工具
- ECharts 是基于 Canvas 制作的一个 JavaScript图表库, 可以更简单的绘制各种优秀的图表
SVG
HTML5提供的另一种图形绘制方案, 采用 DOM 的方式绘制图形. 提供更佳的交互体验
SVG属于矢量图, 在放大缩小时不会失真
- 矢量图: 放大缩小都不失真, 因为是实时生成的.
- 可交互: 图片的每个部分都是DOM元素, 可以进行灵活的交互
- 例如 影院的选座, 飞机的选座
Promise
-
解决回调地狱问题: 传统的回调函数方案解决异步操作问题时, 如果要确保多个异步操作按照指定的顺序执行,就需要在回调函数中触发下一次异步操作, 会导致层层嵌套.
-
ES6提供的 解决回调地狱问题的 构造函数
-
三种状态:
- pending: 就绪. new Promise() 之后
- rejected: 拒绝/失败. 触发 reject 之后
- fulfilled: 满足/成功. 触发 resolve 之后
设定: 状态只能从 pending 转为其他两种之一. 不可逆
-
4个静态方法
- all: 多个Promise完成后. 必须都是 fulfilled 状态
- allsettled: 多个Promise状态敲定后. 对状态无要求
- race: 多个Promise中,哪个先完成就执行那个
- any: 多个Promise中, 任何一个状态敲定即可
async 和 await
await和async 是 ES7 中提供的新特性, 解决异步操作的语法问题
ES7: 提供语法糖 await 和 async, 是简化 Generator 语法的
在ES6的范围内有三种方案解决异步操作问题:
1. 回调函数: 问题-回调地狱
2. Promise: 问题- then的链式语法导致代码过长
3. Generator: ES6提供的新语法,但是由于格式复杂,使用较少
<script>
function get(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.send()
xhr.onload = function () {
var data = JSON.parse(xhr.response)
resolve({ data }) // 触发then
}
xhr.onerror = function () {
reject(xhr.response)
}
})
}
var url1 = 'https://serverms.xin88.top/mall?page=1'
var url2 = 'https://serverms.xin88.top/mall?page=2'
var url3 = 'https://serverms.xin88.top/mall?page=3'
var url4 = 'https://serverms.xin88.top/mall?page=4'
var url5 = 'https://serverms.xin88.top/mall?page=5'
// 固定规范: 把异步操作放到一个函数中, 函数必须用 async 来修饰
// async:异步 await:等待
async function getData() {
// await只能书写在 用 async 修饰的函数中
// 代表 等待 后方Promise的 then 结果, 触发resolve
const res1 = await get(url1)
console.log({ res1 })
const res2 = await get(url2)
console.log({ res2 })
const res3 = await get(url3)
console.log({ res3 })
const res4 = await get(url4)
console.log({ res4 })
const res5 = await get(url5)
console.log({ res5 })
}
getData()
// 利用 try...catch 抓取报错信息
async function fetchData() {
try {
const res1 = await get(url1)
const res2 = await get(url2)
console.log({ res1, res2 })
} catch (error) {
console.log({ error });
}
}
fetchData()
</script>
fetch
<!--
关于网络操作浏览器提供了两种方案:
1. XHR: XMLHttpRequest. 属于早期方案
-- 利用 回调函数 方式实现异步操作
2. fetch: ES6提供的新的请求方式
-- 利用 Promise 方式实现的异步操作
-->
<script>
var url1 = 'https://serverms.xin88.top/mall?page=1'
fetch(url1).then(res => {
console.log(res)
// 利用 json() 获取其中的数据, 这也是异步操作, 需要用then获取解析的结果
res.json().then(data => {
console.log({ data })
})
})
// 链式语法:
fetch(url1).then(res => {
return res.json()
}).then(data => {
console.log({ data });
})
// 利用箭头函数语法糖进一步简化
fetch(url1).then(res => res.json()).then(data => {
console.log({ data })
})
// 总结: 关于网络接口中数据的获取
// 官方提供两种方案:
// 1. xhr: 利用回调
// 2. fetch: 利用Promise
// 非官方的方案:
// 1. 自己封装
// 2. 第三方封装的, 例如 axios , jQuery 等...
WebWorker
<!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>WebWorker 10:45</title>
</head>
<body>
<h1 id="time"></h1>
<!-- src方式引入外部js文件, 属于同步操作, 需要把js中的代码执行一遍 -->
<!-- <script src="06.js"></script> -->
<script>
// worker: 创建一个新的`工人` 帮你去操作外部js文件中的代码 ,属于微任务
// 不会影响到 宏任务代码的执行, 不会卡顿
new Worker('./06.js')
// 用户体验优化的一种方案: 耗时操作用 worker 执行即可
// 定时器 属于 异步操作 -> 微任务队列: 支线 优先级低
setInterval(() => {
time.innerHTML = new Date().toLocaleTimeString()
}, 1000);
// WebWorker: 用于把某些JS代码 主动放到异步中执行, 即放到微任务中, 不反感主线任务的操作
// 1个操作复杂的函数,会消耗很多时间
// function wasteTime(n) {
// return n < 3 ? 1 : wasteTime(n - 1) + wasteTime(n - 2)
// }
// console.log(
// wasteTime(46) // 属于主线任务 - 宏任务
// // 必须执行完毕 才能执行其他的 微任务
// )
// JS代码的运行机制: 事件循环
// 所有代码会排队执行
</script>
</body>
</html>
// 06.js
function wasteTime(n) {
return n < 3 ? 1 : wasteTime(n - 1) + wasteTime(n - 2)
}
var res = wasteTime(46)
console.log({ res })
// 发送消息: 此操作会触发 worker 的 onmessage 事件
postMessage({ res })
监听结果
<!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>webworder 11:21</title>
</head>
<body>
<h1>运行结果: <span id="result"></span></h1>
<script>
// 当前: 不需要反馈信息
// 现在: 需要 worker 执行完毕后, 告知执行结果
const worker = new Worker('./06.js')
// 监听woker发送的信息
worker.onmessage = function (e) {
console.log('来自worker的消息:', e);
result.innerHTML = e.data.res
}
</script>
</body>
</html>
事件循环: EventLoop
宏任务都做完 才能执行微任务
微
任务是宏
任务执行时产生的- 必须 宏任务 都做完, 再执行微任务
- 微任务 都是 异步操作
- 回调函数 : 定时器的
- promise的 then
- await 下方的那个代码, 需要等 await 执行完毕
defineProperty: 属于比较高级的知识点
<!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>defineProperties 14:22</title>
</head>
<body>
<script>
'use strict'
var stu = {
sname: "楠楠",
age: 18,
sid: '200703012',
phone: '10084'
}
// 要求: 把每个属性 都改成不可重新赋值
// Object.defineProperty(stu, 'sname', { writable: false })
// Object.defineProperty(stu, 'age', { writable: false })
// Object.defineProperty(stu, 'sid', { writable: false })
// Object.defineProperty(stu, 'phone', { writable: false })
// defineProperties: 一次性为多个属性进行配置
Object.defineProperties(stu, {
// 属性名: { 配置项 }
sname: { writable: false },
age: { writable: false },
sid: { writable: false },
phone: { writable: false },
})
stu.sname = 11
stu.age = 11
stu.sid = 11
stu.phone = 11
console.log(stu)
</script>
</body>
</html>
为对象属性提供自定义设定
- 属于
高级程序员
在封装框架时使用的知识点, 面试时经常考 - 6个配置项
writable : 可写
configurable: 可配置
<script>
'use strict'
const boy = {
name: "凯凯",
girl: "倩倩"
}
Object.defineProperty(boy, 'girl', {
writable: false,
// 可配置的: 可以重新配置权限, 可以删除元素
configurable: false, // false 代表不可以
// 通常: 不可写 和 不可重新配置 搭配使用, 才能最安全
})
// 两种方案可以突破不可写的限制
// 1. 改成可写
// 2. 删除属性, 再新增
// Object.defineProperty(boy, 'girl', { writable: true })
delete boy.girl // 删除属性
boy.girl = '翠花' // 这里是新增操作
console.log(boy)
</script>
enumerable: 可遍历
<script>
var emp = {
ename: "凯凯",
age: 28,
phone: "18345422334",
eid: '000454',
salary: 30000, // 薪资保密: 不可以被for..in遍历到
}
Object.defineProperty(emp, 'salary', {
enumerable: false, // 是否可以被遍历
})
// 在 Chrome 中, 不可遍历的属性以浅色显示
console.log(emp)
// 遍历对象: for...in
for (const key in emp) {
console.log(key, emp[key])
}
</script>
value: 默认值
<script>
'use strict'
var emp = { ename: '凯凯' }
// 通过 define 方式新增属性
// 如果emp中存在age属性, 则为修改操作
// 如果emp中不存在age属性, 则为新增操作
Object.defineProperty(emp, 'age', {
value: 33,
writable: true, //必须手动开启权限,才能赋值
})
// 此方式新增的属性, 默认的权限配置都是最低的: 不可写,不可配置,不可遍历
console.log(emp)
emp.age = 20
</script>
get: 计算属性
<script>
var r1 = {
width: 100,
height: 20,
// 语法糖: get
// 在 无参数的函数 前方添加get关键词, 这个函数称为计算属性
// 在使用时不需要() 就能自动触发
get area() {
return this.width * this.height
}
}
console.log(r1)
// console.log(r1.area())
console.log(r1.area) // 使用时 宛如在 读取属性的值, 更加直观
///////////////////////////////////////////
// 练习: 立方体
var c1 = {
length: 20,
width: 40,
height: 50,
get volume() {
return this.width * this.height * this.length
}
}
// 使用 define 的方式增加计算属性:
Object.defineProperty(c1, 'perimeter', {
get() {
return (this.length + this.width + this.height) * 4
}
})
Object.defineProperty(c1, 'area', {
get() {
return 2 * (this.length * this.width + this.length * this.height + this.width * this.height)
}
})
// 要求: 添加计算属性, 用于计算出 体积 面积 和 周长
console.log(
c1.volume, // 长x宽x高
c1.area, // (长x宽 + 长x高 + 宽x高)x2
c1.perimeter, // (长+宽+高)*4
);
</script>