JavaScript
javascript 基础
JS 定义
HTML/CSS/JS 的关系,HTML 负责网页结构,CSS 负责网页样式,JS 负责网页行为
JavaScript 是运行在浏览器端的脚本语言(弱类型),由浏览器解释执行,不需要编译,功能在运行过程中逐行解释并执行的,现在的 JavaScript 也可以在 Node.js 技术进行服务端运行
现在 JS 使用于网页特效,服务端开发(Node.js),桌面程序(Flectron),App(Cordova),控制硬件-物联网(Ruff),游戏开发(cocos2d js)等等
- 浏览器如何执行 JS
浏览器分为渲染部分和 JS 引擎
渲染引擎:用来解析 HTML 和 CSS,俗称内核,如 chrome 浏览器的 blink,老版本的 webkit
JS 引擎:也成为 JS 解释器。用来读取网页中的 JS 代码,对其处理后运行,如 chrome 浏览器的 V8
- JSAPI DOM—文档对象模型
是处理可扩展性标记语言的标准编程接口,提供各种接口,可以对页面上的各种元素进行操作如大小,位置,颜色
- JSAPI BOM—浏览器对象模型
提供独立内容,可以于浏览器窗口进行互动的对象结构,通过 BOM 可以操作浏览器窗口,如弹出窗口,控制浏览器跳转,获取分辨率
JS 引入方式
- 行内式 - 用于事件
<input type="button" name="" onclick="alert('ok!')" />
- 内嵌式 - 学习使用
<script type="text/javascript">
alert("ok!");
</script>
- 外链式 - 常用
<script type="text/javascript" src="js/inderx.js"></script>
JS 代码结构
-
使用
;分割消息,会有自动分号插入,但并不总是插入在预期位置。 -
使用
//或者/* */进行注释 -
现代模式需要使用
use strict开启,且必须在脚本的第一行,或使用包装器函数进行包裹。- 目前最好将其写入脚本当中,但当代码全部写在 class 和 module 中时,会自动开启。
- 开启后无法关闭。
变量和数据类型
- 变量的声明 - 通常使用
let进行声明
let user; // 只声明
let username = "jojo"; // 声明并赋值
let age = 18,
message = "hello"; // 同时声明多个变量
- 变量命名规范 - 变量名称必须仅包含字母、数字、符号
$和_,且首字符必须非数字。
// 区分大小写,通常使用小驼峰命名(除第一个单词以外都以大写字母开头)
// 对象类型+变量名如:对象 age 则命名为 nAge
// 变量名最好有实际含义
- 常量的声明 - 使用
const进行声明,声明的常量不可修改。
const COLOR_RED = "#f00"; // 常量名全部大写
const age = someCode(BIRTHDAY); // 常量值在声明时确定,这样的常量则使用变量命名法则。
- 旧的声明方式 - 使用
var进行声明(不推荐使用)
var username = "jojo"; // 一般情况下不再使用
- 数据类型 - js 当中有七大原始类型
- number 数据类型,bigint 大整数类型
- string 字符类型
- boolean 布尔类型
- undefined undefined 类型,变量声明未初始化
- null null 类型,表示空对象,如果定义的变量将来准备保存对象,可将变量初始化为 null
- object 复合类型,Symbol 创建对象的唯一标识符
let nFigure = 1; // typeof iNumber = number
nFigure = 2.123; // typeof iNumber = number
nFigure = Infinity; // typeof iNumber = number
nFigure = NaN; // typeof iNumber = number
bFigure = 123n; // typeof iNumber = bigint // 整数后加n设置为 bigint 大整数类型
let sName = "jojo"; // 可以使用单引号或双引号,没有特殊含义 typeof sName = string
let sName2 = `${sName} is a boy`; // 使用反引号包裹,可以插入变量;
let boolNameFieldChecked = true; // 名称字段是否被检测,true已经检查,typeof boolNameFieldChecked = boolean
let age = null; // typeof age = object // null 表示空,是一个空对象指针
let age2; // typeof age2 = undefined // 未定义
// 最后一种到对象时演示
- 数据类型转换
let nFigure = 12;
nFigure1 = Infinity;
nFigure2 = NaN;
let sFigure = "abc12";
let sFigure1 = "12";
let boolFigure = true;
let empty = null;
let undefinedFigure;
let conversion;
// 转换为字符串
conversion = String(nFigure); // '12'
conversion = nFigure1.toString(); // 'Infinity'
conversion = nFigure2.toString(); // 'NaN'
conversion = String(boolFigure); // 'true'
conversion = String(empty); // '[object Undefined]'
conversion = undefinedFigure.toString(); // 'undefined'
// 转换为数字
conversion = Number(sFigure); // NaN
conversion = Number(sFigure1); // 12
conversion = Number(boolFigure); // 1
conversion = Number(empty); // 0
conversion = Number(undefinedFigure); // NaN
// 转换为布尔值
let boolFigure = Boolean(nFigure); // true
boolFigure = Boolean(empty); // false 数字0,空字符串"",null,undefined,NaN 变换后为 false
// 转为object到对象时演示
交互方式
- alert - 弹出消息窗口
alert("hello world!");
- prompt - 弹出消息窗口,可以接收输入
let age = prompt("请输入年龄:", 18); // 第一个参数是提示信息,第二个参数是初始值(可以省略,但不建议)
alert(`${age}岁`); // 会弹两次窗口,第一次是输入窗口,第二次是弹出的提示窗口
- confirm - 弹出消息窗口,并可以点击确定或取消
let isConfirm = confirm("确定要删除吗?");
if (isConfirm) {
alert(isConfirm);
alert("删除成功");
} else {
alert("取消删除");
}
运算符
| 优先级 | 运算符类型 | 结合性 | 运算符 | 使用场景 |
|---|---|---|---|---|
| 19 | 分组 | n/a | (…) |
a*(b-c) |
| 18 | 成员访问 | 从左到右 | ….… |
obj.a |
| 18 | 需计算的成员访问 | 从左到右 | …[…] |
obj["name"] |
| 18 | new(带参数列表) | n/a | new …(…)构建实例 |
new add(1,2) |
| 18 | 函数调用 | 从左到右 | …(…)执行函数 |
add(1,2) |
| 18 | 可选链 | 从左到右 | …?.… 对?前校验 |
obj.a?.b |
| 17 | new(无参数列表) | 从右到左 | new … |
new add |
| 16 | 后置递增 | n/a | …++ |
i++ |
| 16 | 后置递减 | n/a | …-- |
i-- |
| 15 | 逻辑非 | 从右到左 | !… |
!true=false |
| 15 | 按位非 | 从右到左 | ~… |
~1011=0100 |
| 15 | 一元加法 | 从右到左 | +… |
+12 |
| 15 | 一元减法 | 从右到左 | -… |
-13 |
| 15 | 前置递增 | 从右到左 | ++… |
++i |
| 15 | 前置递减 | 从右到左 | --… |
--i |
| 15 | typeof | 从右到左 | typeof … |
typeof a |
| 15 | void | 从右到左 | void … |
void(0) |
| 15 | delete | 从右到左 | delete … |
delete a.b |
| 15 | await | 从右到左 | await … |
await add() |
| 14 | 幂 | 从右到左 | …**… |
a**b |
| 13 | 乘法 | 从左到右 | …*… |
a*b |
| 13 | 除法 | 从左到右 | …/… |
a/b |
| 13 | 取余 | 从左到右 | …%… |
a%b |
| 12 | 加法 | 从左到右 | …+… |
a+b |
| 12 | 减法 | 从左到右 | …-… |
a-b |
| 11 | 按位左移 | 从左到右 | …<<… |
16<<2=64 |
| 11 | 按位右移 | 从左到右 | …>>… |
16>>2=4 |
| 11 | 无符号右移 | 从左到右 | …>>>… |
-16>>>31=1 |
| 10 | 小于 | 从左到右 | …<… |
5<6=true |
| 10 | 小于等于 | 从左到右 | …<=… |
5<=6=true |
| 10 | 大于 | 从左到右 | …>… |
5>6=false |
| 10 | 大于等于 | 从左到右 | …>=… |
5>=6=false |
| 10 | in | 从左到右 | …in… |
a in b |
| 10 | instanceof | 从左到右 | …instanceof… |
a instanceof b |
| 9 | 相等 | 从左到右 | …==… |
a == b |
| 9 | 不相等 | 从左到右 | …!=… |
a != b |
| 9 | 严格相等 | 从左到右 | …===… |
a === b |
| 9 | 严格不相等 | 从左到右 | …!==… |
a !== b |
| 8 | 按位与 | 从左到右 | …&… |
12 & 4=4 |
| 7 | 按位异或 | 从左到右 | …^… |
12 ^ 4=8 |
| 6 | 按位或 | 从左到右 | …|… |
12 | 4=12 |
| 5 | 逻辑与 | 从左到右 | …&&… |
12 && 4=4 |
| 4 | 逻辑或 | 从左到右 | …||… |
12 & 4=12 |
| 4 | 空值合并 | 从左到右 | …??… |
替代逻辑或,做默认值 |
| 3 | 条件(三元)运算符 | 从右到左 | …?…:… |
a?1:2 |
| 2 | 赋值 | 从右到左 | …=… |
a=2 |
| 2 | 赋值 | 从右到左 | …+=… |
a+=2 |
| 2 | 赋值 | 从右到左 | …-=… |
a-=2 |
| 2 | 赋值 | 从右到左 | …**=… |
a**=2 |
| 2 | 赋值 | 从右到左 | …*=… |
a*=2 |
| 2 | 赋值 | 从右到左 | …/=… |
a/=2 |
| 2 | 赋值 | 从右到左 | …%=… |
a%=2 |
| 2 | 赋值 | 从右到左 | …<<=… |
a<<=2 |
| 2 | 赋值 | 从右到左 | …>>=… |
a>>=2 |
| 2 | 赋值 | 从右到左 | …>>>=… |
a>>>=2 |
| 2 | 赋值 | 从右到左 | …&=… |
a&=2 |
| 2 | 赋值 | 从右到左 | …^=… |
a^=2 |
| 2 | 赋值 | 从右到左 | …|=… |
a|=2 |
| 2 | 赋值 | 从右到左 | …&&=… |
a&&=2 |
| 2 | 赋值 | 从右到左 | …||=… |
a||=2 |
| 2 | 赋值 | 从右到左 | …??=… |
a??=2 |
| 1 | 逗号/序列 | 从左到右 | …,… |
a=2,b=3 |
()[],优先级运算符- 一元操作符
-减,+加,--自减,++自增- 自减自增运算符,前置和后置的区别,前置先运算再赋值,后置先赋值再运算
- 正/负运算符,会自动执行类型转换,将其他类型转换为数字类型
- 算术运算符
+加,-减,*乘,/除,%取余,**幂;先乘除后加减- 连接运算符
+,'1'+2="12",2+2+'1'="41",1+'2'+'2'="122"
- 连接运算符
- 关系操作符
==等于,!=不等于,===全等于,!==不全等于,>大于,<小于,>=大于等于,<=小于等于- 比较结果均返回布尔类型
- 字符串比较会按照字典的方式一个个字符比较,比较的是字符的 Unicode 编码
- 等于
==会转换类型并对比。全等于===不转换类型直接对比。
- 逻辑运算符
!非,&&与,||或,??空值合并;先非再与最后或!true = false,!false = truetrue||true = true,true||false = true,false||true = true,false||false = false- 或运算执行到
true就停止,返回true true&&true = true,true&&false = false,false&&true = false,false&&false = false- 与运算执行到
false就停止,返回false ??空值合并运算符,当运算符左侧为null或undefined时,返回右侧的值,否则返回左侧的值
- 三目运算符
? :,如果条件为真,则返回冒号前的值,否则返回冒号后的值 - 赋值运算符
=等号,+=加等,-=减等,*=乘等,/=除等,%=取余等 - 逗号运算符
,,从左到右依次执行,返回最后一个值
条件分支语句
- if 语句
let nFigure = 70;
let sFigure = "12";
//第一种
if (nFigure == 70) {
console.log("相等!");
}
//第二种
if (sFigure === 12) {
console.log("相等!");
} else {
console.log("不相等!");
}
//第三种
if (nFigure <= 100 && nFigure > 90) {
console.log("优秀");
} else if (nFigure <= 90 && nFigure > 80) {
console.log("良好");
} else if (nFigure <= 80 && nFigure >= 60) {
console.log("及格");
} else {
console.log("不及格");
}
- ? 语句
let nFigure = 70;
let sFigure = "12";
console.log(nFigure == 70 ? "相等!" : "不相等!");
console.log(
nFigure <= 100 && nFigure > 90
? "优秀"
: nFigure <= 90 && nFigure > 80
? "良好"
: nFigure <= 80 && nFigure >= 60
? "及格"
: "不及格"
);
nFigure <= 100 && nFigure > 90
? console.log("优秀")
: nFigure <= 90 && nFigure > 80
? console.log("良好")
: nFigure <= 80 && nFigure >= 60
? console.log("及格")
: console.log("不及格");
循环语句
- while 循环
let nFigure = 0;
while (nFigure < 100) {
nFigure++;
console.log(nFigure);
}
- do-while 循环,至少循环一次
let nFigure = 0;
do {
nFigure++;
console.log(nFigure);
} while (nFigure < 100);
- for 循环
// let nFigure = 0;
for (let nFigure = 0; nFigure <= 100; nFigure++) {
// 内联变量声明
console.log(nFigure);
}
/* 执行流程:
->let nFigure = 0;
->如果nFigure <= 100 成立 -> 运行console.log(nFigure); ->运行nFigure++
->如果nFigure <= 100 成立 -> 运行console.log(nFigure); ->运行nFigure++
->...
->如果nFigure <= 100 不成立 -> 结束循环
*/
跳出循环
- break 语句
for (let nFigure = 0; nFigure <= 100; nFigure++) {
if (nFigure == 50) {
break;
}
console.log(nFigure);
}
- continue 语句
for (let nFigure = 0; nFigure <= 100; nFigure++) {
if (nFigure == 50) {
continue;
}
console.log(nFigure);
}
- 标签跳出
outer: for (let nFigure = 0; nFigure <= 100; nFigure++) {
for (let jNum = 0; jNum <= 100; jNum++) {
if (nFigure == 50) {
break outer; // 跳出outer循环,直接执行到console.log("跳出循环");
}
console.log(nFigure);
}
}
console.log("跳出循环");
多分枝语句
这里主要的是接收数据的数据类型,需要同时匹配才可以。
switch (x) {
case 1:
//do something
break;
case 2:
//do something
break;
default:
//do something
break;
}
函数
函数声明和调用
函数通常是一个动作/行为,给函数起名时应该是动词,简短且明确的描述函数作用。
通常是动词开头例如:show..展示什么内容,get...得到什么值,calc...计算某些内容,create...创建一些东西,check...检查内容并返回布尔值。
// 1. 函数声明,声明后的函数可以在任意地方调用,因为这是全局性的
function fnName(parameter1, parameter2, ... parameterN) {
...body...
}// 声明一个叫fnName的函数,接收参数parameter1,parameter2,...parameterN,函数体为body
// 2. 函数复制
let func = fnName; // 将函数赋值给变量func,func也可以调用函数
// 3. 函数调用
fnName(value1, value2, ... valueN);// 调用函数,传入参数value1,value2,...valueN
func(value1, value2, ... valueN);// 同等调用
// 4. 函数表达式,创建一个匿名函数并复制给sayHi
// 执行到这一句时才会创建函数,这只能在此之后调用函数
let sayHi = function(value1, value2, ... valueN) {
alert(value1);
};
// 5. 传入的参数可以是任意类型,包括函数
// 如果传入函数,则称其为回调函数,回调函数会在函数体内被调用
示例:
function calcAdd(nFigure1, nFigure2) {
let nRs = nFigure1 + nFigure2;
alert(nRs);
return nRs;
}
let nCount = calcAdd(3, 4);
alert(nCount);
变量作用域
js 变量分为全局变量和局部变量,局部变量优先全局变量
如果全局变量和局部变量重名,在函数内使用的是局部变量(形成遮蔽),在函数外使用的是全局变量
//局部变量
function fnMyalert() {
let b = 20;
alert(b);
}
fnMyalert();
alert(b);
//全局变量
let c = 22;
function fnYouralert() {
c++;
}
fnYouralert();
alert(c);
传入参数和返回值
function calcAdd(nFigure1, nFigure2 = "abc") {
// 设置形参,接收传入的参数
// 设置默认参数,这里可以是另外的函数(当第二个参数没传入时,才会执行,如果传入参数则不会执行)
return nFigure1 + nFigure2; // 返回值,将计算结果返回,函数立即停止并返回内容,若果没有return则返回undefined
}
ES6 箭头函数
let func = () => ...body...;
let func1 = n => ...body...;
let func2 = (n, m) => {
...body...;
return n*m
};
()=>...body...;
// 等同于
let func = function () {
...body...;
}
let func1 = function (n) {
...body...;
}
let func2 = function (n,m) {
...body...;
return n*m
}
function(){
...body...;
}
浏览器调试方法
F12 开启开发者工具
在“控制台”输入代码,可以实时查看代码运行结果。
在“代码来源”处添加 js 文件,可进行断点调试。
在代码文件当中添加debugger;,可进行断点调试。执行到此时会自动暂停。

在“控制台”使用console.log()代码可以输出内容
函数测试
使用 Mocha 进行函数测试
function pow(x, n) {
return x ** n;
}
describe("pow", function () {
it("pow(2,3) should return 8", function () {
// 一个it最好只使用一个断言
assert.equal(pow(2, 3), 8); // 这里代码就叫做断言,如果pow(2,3)不等于8,则测试失败
});
it("pow(3,4) should return 81", function () {
// 通常使用循环创建多个it进行测试通用性
assert.equal(pow(3, 4), 81);
});
});
示例写法
<!DOCTYPE html>
<html>
<head>
<!-- add mocha css, to show results -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css"
/>
<!-- add mocha framework code -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
<script>
mocha.setup("bdd"); // minimal setup
</script>
<!-- add chai -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
<script>
// chai has a lot of stuff, let's make assert global
let assert = chai.assert;
</script>
</head>
<body>
<script>
function pow(x, n) {
return x ** n; // using exponentiation operator
/* function code is to be written, empty now */
}
</script>
<!-- the script with tests (describe, it...) -->
<!-- <script src="../js/test.js"></script> -->
<!-- 文件当中的内容如下所示 -->
<script>
describe("pow", function () {
it("raises to n-th power", function () {
assert.equal(pow(2, 3), 8);
});
});
</script>
<!-- the element with id="mocha" will contain test results -->
<div id="mocha"></div>
<!-- run tests! -->
<script>
mocha.run();
</script>
</body>
</html>
对象
使用对象可以存储数据,对象中的数据叫做属性,属性名和属性值用冒号分隔,多个属性之间用逗号分隔。
对象的写法
- 通用写法
// 字面量写法
let user = {};
// 构造函数写法
let user = new Object();
let user = {
// 一个对象,对象如果是纯数字会自动正向排序,否则按照添加时间排序。纯数字前加`+`将取消自动排序
name: "jojo", // 属性名(所有的属性名都会被转换成str类型):属性值
age: 18,
};
user.city = "shanghai"; // 添加属性
alert(user.name); // 读取属性
delete user.name; // 删除属性
- 使用方括号来加强属性名
let user = {};
user["likes birds"] = true;
alert(user["likes birds"]); // true
let key = prompt("你想了解点什么", "Online");
user["user " + key] = true; // user["user Online"] = true;
alert(user["user " + key]);
- 若在函数中使用同名形参传递,则在对象处可简写
function makeUser(name, age) {
return {
name, // 相等于name:name
age, // 相等于age:age
};
}
- 特殊 key
__proto__这个 key 只能设置是对象的值
let obj = {};
obj.__proto__ = 5; // __proto__ 是一个特殊属性
alert(obj.__proto__); // [object Object]
- 文档注释对象标签
/** 用户对象
* @typedef {object} User
* @property {number} id - 用户ID
* @property {string} name - 用户名
*/
/**获取用户信息
* @param {number} userId - 用户ID
* @returns {User} - 返回用户对象
*/
function getUserInfo(userId) {
return {
id: userId,
name: "jojo",
};
}
getUserInfo(123);
- 在对象当中创建函数
// 第一种在对象当中创建函数的方法
let user = {
name : "jojo",
age : 18,
}
function sayHi(){
console.log("Hello, world!");
}
user.sayHi = sayHi; // 将sayHi函数添加到user对象当中
user.sayHi(); // 调用执行user对象当中添加的sayHi函数
// 第二种在对象当中创建函数的方法
let user = {
name = "jojo",
age = 18,
sayHi: function(){ // 在对象当中创建函数,并使用一个名称来指向这个函数
alert("Hello, world!");
},
sayHi1(){ // 直接创建一个指向sayHi函数的名称,不能重名
alert("Hello, world!");
}
}
对象的in方法
in查询属性是否存在,for... in...循环遍历属性
let obj = { test: NaN, test2: 1 };
alert("test" in obj); // true
for (let key in obj) {
alert(key); // 得到key值 test test2
alert(obj[key]); // 得到value值 NaN 1
}
浅拷贝和深拷贝
let user1 = {
name: "jojo1",
sizes: {
height: 182,
width: 50,
},
};
let user2 = { name: "jojo2" };
// 引用,使用另一个名称引用同一个对象。
let admin = user1; // admin和user指向同一个对象,修改admin会修改user
admin.name = "peter";
alert(user1.name); // 'peter'
// 合并,将两个对象合并成一个,覆盖相同的属性。
Object.assign(user1, user2); // admin和user指向不同的对象,修改admin不会修改user
alert(user1); // user2不改变,user1变为{ name: 'jojo2', sizes: { height: 182, width: 50 } }
// 浅拷贝
// 方案1
let clone = {};
for (let key in user1) {
clone[key] = user1[key];
}
clone.name = "jojo3"; // 不影响user1;
clone.sizes.wight = 60; // 影响 user1;
// 方案2
let clone1 = Object.assign({}, user1); // 完全拷贝只对一层有效,其他层级还是引用
clone1.name = "jojo4"; // 不影响user1和clone
clone1.sizes.wight = 60; // 影响 user1和clone
// 多层深拷贝
// 方案1
let deepclone = JSON.parse(JSON.stringify(user1)); // 使用JSON.stringify将对象转换成字符串,再使用JSON.parse将字符串转换成对象
// 方案2
import _ from "lodash";
let deepclone2 = _.cloneDeep(user1); // 使用lodash库
对象的回收
当对象没有任何引用时,会被垃圾回收机制回收.
对象变为不可达状态后被回收,主要是从根开始的不可达。
对象的this方法
当需要在对象内的函数中引用对象中的一些属性时,可以使用 this 关键字来引用对象
let user = {
name: "jojo",
age: 18,
sayHi: function sayHi() {
alert(this.name); // 使用this来引用对象中的属性,这里的this指向User对象
},
};
user.sayHi(); // jojo
// 简写
let user = {
name: "jojo",
age: 18,
sayHi() {
alert(this.name); // 使用this来引用对象中的属性,这里的this指向User对象
},
};
函数内部都可以使用this关键字来引用一些属性,这个值是计算出来的,不同的对象当中得到的结果不同
function SayHi() {
// 先定义函数
console.log(this.name); // 使用this来引用对象中的属性,这个值是计算出来的,不同的对象当中得到的结果不同
}
let User = { name: "jojo" }; // 创建对象
let Admin = { name: "peter" }; // 创建对象
User.func = sayHi; // 将函数添加到对象当中,函数当中的this指向User对象
Admin.func = sayHi; // 将函数添加到对象当中,函数当中的this指向Admin对象
User.func(); // jojo
Admin.func(); // peter
// 在全局当中使用this,则指向window对象,如果使用严格模式则this指向undefined
console.log(this.name); // 这里的this指向window对象(window对象是全局对象)。如果使用严格模式,则this指向undefined
对象的new方法
使用new方法需要先声明构造函数,然后再使用new方法进行实例化。
构造函数起名通常使用大驼峰命名法(UserInfo),构造函数中的this指向实例化的对象。
- 第一种构造函数,使用
new方法,不推荐使用
function User(name, age) {
// 声明一个构造函数,
this.name = name;
this.age = age;
this.sayHi = function () {
console.log("Hi" + this.age + this.name);
};
}
let user = new User("jj", 18); // 使用new方法进行实例化
let user1 = new User("jj1", 18); // 使用new方法进行实例化
user.sayHi(); // Hi18jj
user1.sayHi(); // Hi18jj1
- 第二种构造函数,使用
new方法,更好的做法
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.sayHi = function () {
console.log("Hi" + this.age + this.name);
};
let user = new User("jj", 18); // 使用new方法进行实例化
user.sayHi(); // Hi18jj
- 通过顶级 Object 类型来实例化一个对象,通过对象字面量创建对象。
// Object类来实例对象,相当于创建一次性的构造函数,并直接实例化
let oCar = new Object();
oCar.brand = "Benz";
oCar.number = "00001";
oCar.speed = 200;
oCar.driver = function () {
console.log(this.brand, this.number, this.speed);
};
console.log(oCar.number);
oCar.driver();
//字面量
let oTom = {
name: "tom",
age: "20",
sing: function () {
console.log(this.name + this.age);
},
};
console.log("name" + oTom.name);
oTom.sing();
- 第三种构造函数,使用
new方法,新语法是第二种的语法糖。
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log("Hi" + this.age + this.name);
}
}
let user = new User("jj", 18); // 使用new方法进行实例化
user.sayHi(); // Hi18jj
对象的?.选择
?.这样的语法,可以选择对象当中可能不存在的属性,但选择的对象是必须要声明的。
?.()这是选择调用一个可能不存在的函数。
?.[]读取一个可能不存在的属性。更为安全的读取。
这些符号也可以用以delete关键字之后,删除一个可能不存在的属性。存在则删除,不存在则不改变。
但是这些符号不能用于写入。
obj?.prop; // 如果obj存在,则返回obj.prop,否则返回undefined
obj?.[prop]; // 如果obj存在,则返回obj[prop],否则返回undefined
obj?.method(); // 如果obj存在,则调用obj.method(),否则返回undefined
delete obj?.prop; // 如果obj存在,则删除obj.prop,否则不改变obj
Symbol类型
Symbol类型和string类型可作为对象属性键。
let user = {
name: "jojo", // 对象字面量方法,使用string类型作为属性键
[id]: 1, // 对象字面量方法,使用Symbol类型作为属性键
};
let user1 = {
name: "jojo",
id: 1, // 这里是string类型的key,和后续创建的Symbol类型的key重复,但不冲突
};
let id = Symbol("id"); // 将id定义为symbol类型,并给予一个描述叫id
user1[id] = 2; // 使用symbol类型作为属性键
console.log(user1[id]); // 2,使用symbol类型作为属性键
console.log(user1.id); // 1,使用string类型作为属性键
Symbol类型是唯一值,即使两个Symbol类型描述相同,也是不同的值。
let id1 = Symbol("id");
let id2 = Symbol("id");
console.log(id1 == id2); // false,即使描述相同,也是不同的值
Symbol类型不能自动转换成string类型,需要使用toString()进行。Symbol类型使用Symbol.description显示描述。
let id = Symbol("id");
console.log(id); // error
console.log(id.toString()); // Symbol(id),将symbol类型转换成string类型
console.log(id.description); // id,显示symbol类型的描述
Symbol类型可以创建隐藏属性,防止被意外读取。Symbol类型不能使用for...in循环遍历。
let user = {
name: "jojo",
}; // 这是在其他代码当中引用的
let id = Symbol("id");
user[id] = 1;
console.log(user[id]); // 1
console.log(user.id); // undefined,因为id是symbol类型,不能自动转换成string类型,所以不能使用点语法来访问
for (let key in user) {
console.log(key); // name,因为id是symbol类型,不能使用for...in循环遍历
}
- 全局
Symbol类型,使用Symbol.for()创建,使用Symbol.keyFor()获取。
// 从全局注册表中读取,按给定的描述返回Symbol。
let id = Symbol.for("idaa"); // 如果该Symbol不存在则创建
let idAgain = Symbol.for("idaa"); // 获取这个全局Symbol
console.log(id == idAgain); // true
consoloe.log(Symbol.keyFor(id)); //idaa,按找给定的Symbol,返回描述
对象的转换
- 对象不会自动转换为布尔类型,若主动转换成布尔类型,都是 true
- 转为数字类型发生在对象相减或者应用数学函数时,例如,Date 对象相减会得到两个日期之间的差值
- 转为字符串通常发生在诸如 alert(obj)或者对象属性键这种输出函数中
使用 hint 进行自定义转换
hint有三种取值String、Numbre、Default
//String取值通常发生在(输出,将对象作为属性键)
alert(obj);
console.log(obj);
Obj[obj] = 123;
//Number取值通常发生在(显示转换,数学运算(除了二元加法),大于/小于比较)
let num = Number(obj);
let x = +obj;
let delta = data1 - data2;
let greater = num1 > num2; // 这里是因为历史原因使用的是Number
//Default取值通常发生在(二元加法,与字符串、数字或Symbol进行==比较)
let total = obj1 + obj2;
if (user == 1){...};
然后根据hint得到的值,就可以自定义转换,调用的函数是[Symbol.toPrimitive](hint).通常调用的顺序是
- 尝试调用
obj[Symbol.toPrimitive](hint),如果定义了则改变,否则下延。- 若
hint是String,尝试调用obj.toString()或obj.valueOf(),那个存在调用那个- 若
hint是Number或Default,尝试调用obj.valueOf()或obj.toString(),那个存在调用那个
// obj[Symbol.toPrimitive](hint)使用方法
obj[Symbol.toPrimitive](hint) = function(hint){
// hint取值有三种,String,Number,Default
// 必须返回一个原始值(string,number,bigin,boolean,symbol,null,undefined)
}
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
console.log(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money; // 根据hint的值,返回不同的值,string返回this.name,number和default返回this.money
},
}
console.log(user); // hint: string -> {name: "John"}
console.log(+user); // hint: number - > 1000
console.log(user + 500); // hint: default - > 1500
// obj.toString()和obj.valueOf()使用方法
// 如果hint是String,则优先调用toString(),然后再用valueOf()
// 如果hint是Number或Default,则优先调用valueOf(),然后再用toString()
let user = {
name: "John",
money: 1000,
// 对于hint="string"
toString() { // 相对来说比较全能
return `{name: "${this.name}"}`;
}
// 对于hint="number"或"default"
valueOf() {
return this.money;
}
};
console.log(user); // hint: string -> toString -> {name: "John"}
console.log(+user); // hint: number -> valueOf -> 1000
console.log(user + 500); // hint: default -> valueOf -> 1500
数据类型的使用
在 JS 中原始类型有七种:string、number、bigint、boolean、undefined、symbol、null。
这些原始类型可以被当作对象来做一些方法,但这并不是真正的对象,我们不能给这些原始类型添加属性。
数字类型的使用
- 分割数字,使用
_进行分割,例如let num = 1_000_000_000;。 - 科学计数法,使用
e进行科学计数法,例如let num = 1e3;// 1e3 = 1000。 - 二进制、八进制和十六进制,使用
0b、0o、0x进行表示,例如let num = 0b1111;0o377;0xff。 - 数字进制转换,使用
toString(base)进行转换base=2~36,例如let num = 255;num.toString(2) // 1111_1111和123456..toString(36); // 2n9c使用两个.调用。 - 舍入,使用
Math.round()进行四舍五入,使用Math.ceil()向上舍入,使用Math.floor()向下舍入,使用Math.trunc()去掉小数部分,使用Math.toFixed(n)将数字舍入到指定的小数位数,返回字符串。 - 是否为正确的数字,使用
Number.isFinite()判断是否为有限数字,使用Number.isNaN()判断是否为NaN,使用Number.isInteger()判断是否为整数。 - 数字转换,使用
parseInt(str,radix)从左向右读取字符串转换为整数直到非数字出现(可设置进制),使用parseFloat(str)从左向右读取字符串转换为浮点数直到除第一个.后的非数字出现,使用Number()将其他类型转换为数字。 - 更多内置 Math 对象文档
字符串类型的使用
- 字符串的引号,使用单引号、双引号、反引号(模板字符串)。
function myTag(strings, personExp, ageExp) {
let str0 = strings[0];
let str1 = strings[1];
let str2 = strings[2];
let ageStr = ageExp > 17 ? "adult" : "child";
return `${str0}${personExp}${str1}${ageStr}${str2}`;
}
const output = myTag`Hello,${person}! You are ${age}.`;
let age = +prompt("age", 18);
let person = prompt("person", "jojo");
console.log(output); // Hello,jojo! You are adult.
- 特殊符号,使用
\进行转义,例如\n换行,\t制表符,\b退格符,\r回车符,\f换页符,\v垂直制表符,\\反斜杠,\'单引号,\"双引号,\x十六进制,\uUnicode。 - 字符串的长度,使用
str.length获取字符串长度。 - 访问字符,使用
str[i]获取字符串第i个字符,使用str.charAt(i)获取字符串第i个字符,使用str.charCodeAt(i)获取字符串第i个字符的 Unicode 编码。这里不能单独赋值(字符串不可变) - 字符串大小写转换,使用
str.toUpperCase()将字符串转换为大写,使用str.toLowerCase()将字符串转换为小写。 - 字符串查找
// str.indexOf(substr,pos)从左向右和str.lastIndexOf(substr,pos)从右向左,找到一次就返回。
let str = "Hello, world!";
str.indexOf("Hello"); // 0
if (~str.indexOf("world")) {
// ~-1 = 0
// ~0 = -1
// ~1 = -2
}
// str.includes(substr,pos)从左向右、str.startsWith(substr,pos)从右向左、str.endsWith(substr,pos)找到一次就返回true。
- 字符串截取
str.slice(start,end),start为开始位置,end为结束位置,start和end可以为负数,start为负数表示从后往前数,end为负数(或空)表示截取到字符串末尾,start大于end则返回空字符串。str.substring(start,end),start为开始位置,end为结束位置,start和end不为负数,start大于end则调换start和end位置。str.substr(start,length),start为开始位置,length为截取长度,start可为负数表示从后往前数。
- 字符串的 UTF-16 编码,使用
str.codePointAt(pos)获取字符串第pos个字符的 UTF-16 编码,使用String.fromCodePoint(code)将 UTF-16 编码转换为字符串。
数组操作方法
- 声明数组
let lA = new Array(1, 2, 3);
let lA2 = [1, 2, 3, "asd"];
let lA3 = [ // 数组可以嵌套,可存储任意类型
[1, 2, 3],
["a", "b", "c"],
{name:"jojo"}, // console.log(lA3[2].name); // jojo
function (){console.log("asd")}, // lA3[3](); // asd
];
let lA2[2] = 4; // 修改数组元素 [1,2,4,"asd"]
let lA[3] = 4; // 数组添加元素 [1,2,3,4]
console.log(lA.length); // 数组长度 4,自动更细,可写(arr.length=0清空数组)
console.log(lA2); // 显示整个数组 [1,2,4,"asd"]
console.log(lA.at(-1)); // 获取数组最后一个元素 4
- 队列/栈操作(unshift,shift,push,pop)
let lA = [1, 2, 3, 4];
// 队列操作
lA.unshift(0); //从最前面进行插入
console.log(lA); // [0, 1, 2, 3, 4]
console.log(lA.shift()); //删除第一个
console.log(lA); // [1, 2, 3, 4]
// 栈操作
lA.push(5); //从最后进行插入
console.log(lA); // [1, 2, 3, 4, 5]
console.log(lA.pop()); //删除最后一个
console.log(lA); // [1, 2, 3, 4]
- 数组的遍历(for,for in,for of,forEach,reduce,reduceRight)
let lA = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//for循环遍历
for (let i = 0; i < lA.length; i++) {
console.log(lA[i]);
}
//for循环变形
for (let n in lA) {
console.log(n);
}
//for循环变形
for (let n of lA) {
console.log(n);
}
// arr.forEach(function (element, index, array) {...}); // arr指代数组,function指对数组元素进行操作的函数,element指数组元素,index指数组元素下标,array指数组
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
lA.forEach(function (element, index, array) {
// 只是创建一个可遍历数组的函数,不对数组内容修改
console.log(`${element} is at index ${index} in ${array}`);
});
// 0 is at index 0 in 0,1,2,3,4,5,6,7,8,9
// 1 is at index 1 in 0,1,2,3,4,5,6,7,8,9
// 2 is at index 2 in 0,1,2,3,4,5,6,7,8,9
// arr.reduce(function (accumulator, currentValue, currentIndex, array) {...}, initialValue); // arr指代数组,function指对数组元素进行操作的函数,accumulator指累加器,currentValue指数组元素,currentIndex指数组元素下标,array指数组,initialValue指累加器初始值
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.reduce(function (accumulator, currentValue, currentIndex, array) {
// 1. accumulator=0, currentValue=0, currentIndex=0, array=0,1,2,3,4,5,6,7,8,9
// 2. accumulator=0, currentValue=1, currentIndex=1, array=0,1,2,3,4,5,6,7,8,9
// 3. accumulator=1, currentValue=2, currentIndex=2, array=0,1,2,3,4,5,6,7,8,9
return accumulator + currentValue;
}, 0);
- 数组的
toString方法
let lA = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(lA.toString()); // 1,2,3,4,5,6,7,8,9
console.log(lA + 1); // 1,2,3,4,5,6,7,8,91
- 根据下标添加和删除元素(splice,slice,concat)
// arr.splice(start,num,element1,...,elementN);
// arr指代数组,star指代要对数组开始操作的元素下标,num指要对多少元素进行操作,elementN指修改后或被添加的元素
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// 删除操作
lA.splice(3, 3); // 删除,从下标3开始删除三个元素
console.log(lA); // [0, 1, 2, 6, 7, 8, 9]
// 插入操作
lA.splice(3, 0, 33); // 插入,从下标3插入一个33,元素数据向后移动
console.log(lA); // [0, 1, 2, 33, 6, 7, 8, 9]
// 插入多个数据
lA.splice(3, 0, 44, 55, 66); // 插入多个,原本元素向后移动
console.log(lA); // [0, 1, 2, 44, 55, 66, 33, 6, 7, 8, 9]
// 替换操作
lA.splice(3, 1, 333); // 替换,从下标3替换一个元素,其他元素不变
console.log(lA); // [0, 1, 2, 333, 55, 66, 33, 6, 7, 8, 9]
// 替换多个数据
lA.splice(3, 2, 333, 444, 555); // 替换多个,可一部分替换一部分插入
console.log(lA); // [0, 1, 2, 333, 444, 555, 66, 33, 6, 7, 8, 9]
// arr.slice(start,end); // arr指代数组,star指代要对数组开始操作的元素下标,end指要对数组结束操作的元素下标,返回一个新数组
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.slice(3, 6); // 截取,从下标3开始截取到下标6,不包含下标6,返回一个新数组
console.log(lA2); // [3, 4, 5]
// arr.concat(element1,...,elementN); // arr指代数组,elementN指要添加的元素,返回一个新数组
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.concat(10, 11, 12); // 拼接,将10,11,12添加到lA数组后面,返回一个新数组
console.log(lA2); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
- 数组的搜索(indexOf,lastIndexOf,includes,find,findIndex,findLastIndex,filter)
// arr.indexOf(item, from); // arr指代数组,item指要搜索的元素,from指从哪个下标开始搜索,返回元素下标,没有找到返回-1
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(lA.indexOf(3)); // 3
// arr.includes(item, from); // arr指代数组,item指要搜索的元素,from指从哪个下标开始搜索,返回布尔值,可以正确处理NaN数据。
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(lA.includes(3)); // true
// arr.find(function(element, index, array){}); // arr指代数组,function指对数组元素进行操作的函数,element指数组元素,index指数组元素下标,array指数组,返回找到的元素,没有找到返回undefined
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.find(function (element, index, array) {
return element > 5;
});
console.log(lA2); // 6
let lA3 = lA.find((item) => item > 5);
console.log(lA3); // 6
// arr.filter(function(element, index, array){}); // arr指代数组,function指对数组元素进行操作的函数,element指数组元素,index指数组元素下标,array指数组,返回一个新数组,新数组为原数组中满足条件的元素
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.filter(function (element, index, array) {
return element > 5;
});
console.log(lA2); // [6, 7, 8, 9]
let lA3 = lA.filter((item) => item > 5);
console.log(lA3); // [6, 7, 8, 9]
- 转换数组(map,sort,reverse,split,join)
//arr.map(function(element, index, array){}); // arr指代数组,function指对数组元素进行操作的函数,element指数组元素,index指数组元素下标,array指数组,返回一个新数组,新数组为原数组中满足条件的元素
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.map(function (element, index, array) {
return element * 2;
});
console.log(lA2); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
let lA3 = lA.map((item) => item * 2);
console.log(lA3); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
// arr.sort(function(a, b){}); // arr指代数组,function指对数组元素进行操作的函数,a,b指数组元素,计算得到的值用于排序,正值则不变,负值则交换。返回一个新数组但通常省略,原数组会改变。
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let lA2 = lA.sort(function (a, b) {
return a - b;
});
console.log(lA2); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let lA3 = lA.sort((a, b) => a - b);
console.log(lA3); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// arr.reverse(); // arr指代数组,返回一个新数组,原数组会改变。
let lA = [0, 1, 2, 3, 10, 5, 6, 7, 8, 9];
let lA2 = lA.reverse();
console.log(lA2); // [9, 8, 7, 6, 5, 10, 3, 2, 1, 0]
// str.split(separator, limit); // str指代字符串,separator指分隔符,limit指返回的数组最大长度,返回一个数组
let str = "a,b,c,d,e,f,g,h,i,j";
let arr = str.split(",", 5);
console.log(arr); // ["a", "b", "c", "d", "e"]
// arr.join(separator); // arr指代数组,separator指分隔符,返回一个字符串
let arr = ["a", "b", "c", "d", "e"];
let str = arr.join(",");
console.log(str); // "a,b,c,d,e"
- 数组的判断(isArray,instanceof)
let lA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(Array.isArray(lA)); // true
console.log(lA instanceof Array); // true
特别注意
上述操作大多数都可以在末尾添加thisArg,可以将定义的函数的this值改变为thisArg的值,例如:
let army = {
minAge: 18,
maxAge: 27,
canJoin: function (user) {
return user.age >= this.minAge && user.age <= this.maxAge;
},
};
let users = [{ age: 16 }, { age: 20 }, { age: 23 }, { age: 30 }];
let armyCanJoin = users.filter(army.canJoin, army);
console.log(armyCanJoin.length); // 2
console.log(armyCanJoin); // [{ age: 20 }, { age: 23 }]
console.log(armyCanJoin[0].age); // 20
可迭代对象
可迭代对象,都具有Symbol.iterator属性,因此在对象当中添加Symbol.iterator属性,就可以让对象成为可迭代对象。
对于for of循环启动时都需要调用Symbol.iterator方法。这个方法必须返回一个next方法的对象。
对于字符串、数组、Map、Set、arguments 对象、NodeList 对象这些都是内置可迭代对象,不需要自己添加Symbol.iterator。
let range = { // 创建一个对象
from: 1,
to: 5,
};
// 在对象当中添加Symbol.iteration属性,让对象成为可迭代对象。
// for of 方法会调用这个对象的 Symbol.iterator 方法
range[Symbol.iterator] = function () { // 返回迭代器对象(iterator object)
// for of 仅与下面的迭代器对象一起工作,需要提供一下个值
return { // 这里重新创建了一个对象,一个可以迭代的对象
current: this.from,
last: this.to,
// for of 的每轮循环迭代中都会调用 next 方法
next() { // next 方法是在Symbol.iterator方法中的,range对象自身没有。
if (this.current <= this.last) {
// 返回{done:..., value:...}格式的对象
return { done: false, value: this.current++ };
} else {
return { done: true };
}
},
};
};
// 迭代器对象和与其进行迭代的对象是分开的。
for (let num of range) {
console.log(num); // 1 2 3 4 5
}
/////////第二种实现方法
// 将range自身作为迭代器,将next方法定义在range对象当中。
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
this.current = this.from;
return this; // 这里直接返回自身即可,因为自身是一个可迭代的对象
}
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
}else{
return { done: true };
}
}
};
for (let num of range) {
console.log(num); // 1 2 3 4 5
}
对于可迭代对象,可以直接显示调用Symbole.iterator方法
let str = "hello";
// 等同于for (let char of str) console.log(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next();
if (result.done) break;
console.log(result.value);
} // h e l l o
可迭代(iterable)对象和类数组(array-like)对象
可迭代对象:上述自定义或内置的具有Symbole.iterator方法的对象。(上述都是)
类数组对象:具有length属性和索引属性的对象。(下述举例)
let arraylike = {
0: "Hello",
1: "World",
length: 2,
};
let arr = Array.from(arraylike);
console.log(arr); // ["Hello", "World"]
console.log(arr.pop()); // World
当然对于可迭代对象也是可以使用Array.from方法将其转换为数组。
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
this.current = this.from;
return this;
},
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
},
};
let arr = Array.from(range);
console.log(arr); // [1, 2, 3, 4, 5]
并且在使用Array.from()时,可以传入第二个参数(一个函数或表达式),用于对每个元素进行处理,返回一个新数组。
let str = "𝒳😂";
let arr = Array.from(str, (num) => num + num);
console.log(arr); // ['𝒳𝒳', '😂😂']
console.log(arr[0]); // 𝒳𝒳
console.log(arr.length); // 2
注意
- 可迭代对象需要包含 Symbol.iterator 属性,并且该属性需要是一个函数,该函数返回一个可迭代对象,该对象需要包含 next 方法。
- 第二种创建方法在出现多个 for of 循环且异步时,会出现共享迭代状态的情况
// 1. 定义“自己当迭代器”的 range 对象(状态 current 存在自身)
let range = {
from: 1,
to: 5,
current: 1, // 共享状态:所有遍历共用这个 current
// Symbol.iterator 返回 this(自己当迭代器)
[Symbol.iterator]() {
console.log(`[迭代器启动] 重置 current 为 ${this.from}`);
this.current = this.from; // 每次启动遍历,都会重置 current!
return this;
},
// next() 操作共享的 current 属性
next() {
if (this.current <= this.to) {
const value = this.current;
console.log(
`[next 执行] current = ${
this.current
} → 返回 ${value},current 自增为 ${this.current + 1}`
);
this.current++;
return { done: false, value };
}
console.log(
`[next 执行] current = ${this.current}(超过 to=${this.to})→ 遍历结束`
);
return { done: true };
},
};
// 2. 非阻塞暂停函数(用 Promise 包裹 setTimeout,不阻塞主线程)
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 3. 第一个遍历:遍历到 2 时暂停,释放主线程
async function firstTraversal() {
console.log("\n=== 第一个遍历开始 ===");
// for...of 会自动调用 range[Symbol.iterator]() 和 next()
for await (let num of range) {
// 用 for await...of 支持异步暂停
console.log("第一个遍历拿到值:", num);
// 当第一个遍历拿到 2 时,暂停 100ms(非阻塞,释放主线程)
if (num === 2) {
console.log("第一个遍历:拿到 2,暂停 100ms,释放主线程...");
await sleep(100); // 关键:非阻塞暂停,主线程可执行其他任务
console.log("第一个遍历:暂停结束,继续执行");
}
}
console.log("=== 第一个遍历结束 ===");
}
// 4. 第二个遍历:在第一个遍历暂停时启动
function secondTraversal() {
console.log("\n=== 第二个遍历开始 ===");
for (let num of range) {
// 普通 for...of(同步遍历)
console.log("第二个遍历拿到值:", num);
}
console.log("=== 第二个遍历结束 ===");
}
// 5. 启动测试:先跑第一个遍历,在它暂停时跑第二个遍历
firstTraversal();
// 第一个遍历启动后,主线程会在“拿到 2 并 await sleep(100)”时释放
// 此时设置一个 50ms 的定时器,确保第二个遍历在第一个遍历暂停期间启动
setTimeout(secondTraversal, 50);
映射(Map)与集合(Set)
Map 是一个带键的数据项集合,
Set 是一个唯一值的集合。
let map = new Map(); // 创建一个 Map 对象
map.set("name", "张三"); // 添加一个键值对,字符串键
map.set(1, "李四"); //数字键
map.set(true, "王五"); // 布尔值键
map.set({ name: "John" }, 123); // 对象键,对象键会被转换为字符串 "[object Object]"
map.set(null, "null").set(undefined, "undefined"); // null 键 和 undefined 键,可以链式调用
console.log(map.get("name")); // 获取键为"name"的值
console.log(map.has("name")); // 判断是否存在键为"name"的键值对
map.delete("name"); // 删除键为"name"的键值对
console.log(map.size); // 获取键值对的数量
map.clear(); // 清空所有键值对
// Map 的遍历
map.keys(); // 获取所有键的迭代器
map.values(); // 获取所有值的迭代器
map.entries(); // 获取所有键值对的迭代器,在for of循环中默认调用
map.forEach((value, key) => {
console.log(key, value); // 遍历所有键值对
});
// Map 和对象相互转换
let obj = {
name: "张三",
age: 18,
};
let map = new Map(Object.entries(obj)); // 对象 => Map
console.log(map); // Map { 'name' => '张三', 'age' => 18 }
let map = new Map([
["1", "value1"],
[1, "value2"],
[true, "value3"],
]);
let prices = Object.fromEntries(map); // Map => 对象
console.log(prices); // { 1: 'value2', true: 'value3' }
let set = new Set(); // 创建一个 Set 对象
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "John" };
set.add(john); // 添加一个对象
set.add(pete);
set.add(mary);
set.add(pete); // 再次添加同一个对象
set.add(john);
console.log(set.size); // 3 获取集合的大小,重复不计入
set.delete(mary); // 删除一个对象
set.has(pete); // 判断集合中是否存在某个对象
set.clear(); // 清空集合
// Set 的遍历
set.keys(); // 获取所有键的迭代器
set.values(); // 与 set.keys() 作用相同,这是为了兼容 Map
set.entries(); // 遍历并返回一个包含所有的实体 [value, value] 的可迭代对象,它的存在也是为了兼容 Map
set.forEach((value, key) => {
console.log(key, value); // 遍历所有键值对
});
弱映射(WeakMap)和弱集合(WeakSet)
在上述 Map 和 Set 的基础上,取消了对键值对引用的计数,即不会阻止垃圾回收器回收键值对,因此键值对只能是对象,不能是原始类型。
当对象被操作为 null 或对象被删除时,WeakMap 和 WeakSet 中的键值对也会被删除。
这样的设计使得 WeakMap 和 WeakSet 更适合用于存储那些不需要手动删除的键值对。
WeakMap 仅支持get set has delete方法,WeakSet 仅支持add delete has方法。WeakMap 的键必须为对象,WeakSet 的值必须为对象。
let weakMap = new WeakMap();
let weakSet = new WeakSet();
let john = { name: "John" };
weakMap.set(john, "..."); // john添加到WeakMap
john = null; // 覆盖引用,john 被从内存中删除了!
john = { name: "John" }; // 重新创建一个对象
weakSet.add(john); // john添加到WeakSet
john = null; // 覆盖引用,john 被从内存中删除了!
对 Object 使用keys,values,entries方法
let user = {
name: "John",
age: 30,
};
console.log(Object.keys(user)); // ["name", "age"]
console.log(Object.values(user)); // ["John", 30]
console.log(Object.entries(user)); // [["name", "John"], ["age", 30]]
解构赋值
let [name, age] = ["John", 30]; // 数组解构
console.log(name, age); // John 30
let [firstName, , title] = ["Julius", "Caesar", "Consul", "Republic"]; // 跳过第二个元素
console.log(title); // Consul
let [a, b, c] = "abc"; // 字符串解构
console.log(a, b, c); // a b c
let [guest, admin] = ["Jane", "Pete"]; // 交换变量值
[guest, admin] = [admin, guest];
console.log(guest, admin); // Pete Jane
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "Republic"]; // rest 是包含从第三项开始的其余数组项的数组
console.log(rest, rest.length); // ["Consul", "Republic"] 2
let [name = "Guest", surname = "Anonymous"] = ["Julius"]; // 默认值
console.log(name); // Julius(来自数组的值)
console.log(surname); // Anonymous(默认值被使用了)
let { height, width, title } = { title: "Menu", height: 200, width: 100 }; // 对象解构, 顺序不重要
console.log(name, age); // John 30
let { name: firstName, age: years } = { name: "John", age: 30 }; // 重命名
console.log(firstName, years); // John 30
let { name: firstName, age: years = 18 } = { name: "John" }; // 默认值
console.log(firstName, years); // John 18
let { name, ...restProps } = { name: "John", age: 30, job: "teacher" }; // restProps 是一个对象,包含除 name 属性之外的所有属性
console.log(restProps); // { age: 30, job: 'teacher' }
// 注意,在js当中{}代表的是代码块,而不是对象,所以解构赋值时,如果{}不在[]内,则会被解析为代码块,而不是对象。
// 所以在解构赋值时,需要将{}放在[]内,表示对象解构。
({ name, age } = { name: "John", age: 30 }); // 使用括号来消除歧义
let options = {
size: {
width: 100,
height: 200,
},
items: ["Cake", "Donut"],
extra: true,
};
// 为了清晰起见,解构赋值语句被写成多行的形式
let {
size: {
// 把 size 赋值到这里
width,
height,
},
items: [item1, item2], // 把 items 赋值到这里
title = "Menu", // 在对象中不存在(使用默认值)
} = options; // 这里得到的数据是 width、height、item1、item2 和具有默认值的 title 变量。
console.log(width); // 100
通常用于函数的参数部分,将形参变为一个对象,传入参数时只需要传入一个对象即可,函数内部则可得到解构完成的实参。
function showMenu({
title = "Menu",
width = 200,
height = 100,
items = [],
} = {}) {
// 这里使用多层默认值,防止传入空对象时,解构赋值失败
console.log(`Title: ${title}, W${wight}px, H${height}px`);
console.log(items);
}
let options = { title: "My menu", items: ["Item1", "Item2"] };
showMenu(options); // Title: My menu, W200px, H100px ['Item1', 'Item2']
showMenu(); // Title: Menu, W200px, H100px []
日期和时间
let now = new Date();
console.log(now); // 星期 月份 日期 年份 时:分:秒 时区 时区描述
// new Date(milliseconds) 时间戳 1234567890123
// new Date(datestring) 格式字符串 2022-01-01
// new Date(year, month, date, hours, minutes, seconds, ms) 多参数
// 在创建时会自动矫正输入错误
let wrongTime = new Date(2013, 0, 32);
console.log(wrongTime); // 2013-02-01
// 可以单独方位年月日时分秒
console.log(now.getFullYear()); // 年
console.log(now.getMonth()); // 月 0-11
console.log(now.getDate()); // 日
console.log(now.getDay()); // 星期 0-6 日-六
console.log(now.getHours()); // 时
console.log(now.getMinutes()); // 分
console.log(now.getSeconds()); // 秒
console.log(now.getMilliseconds()); // 毫秒
console.log(now.getTime()); // 时间戳 console.log(+now);console.log(Date.now());相同
console.log(now.getTimezoneOffset()); // 时区偏移
// 设置年月日时分秒
now.setFullYear(2022);
now.setMonth(0);
now.setDate(1);
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
now.setMilliseconds(0);
// 时间加减,不需要考虑闰年的情况,直接加减即可
now.setDate(now.getDate() + 1); // 加一天
now.setMonth(now.getMonth() - 1); // 减一月
// 字符串转换时间戳和日期格式
let ms = Date.parse("2012-01-26T13:51:50.417-07:00");
// YYYY-MM-DDTHH:mm:ss.sss+08:00(东八区)
console.log(ms); // 时间戳
console.log(new Date(ms)); // 日期格式
JSON 方法,toJSON
为了弥补 toString()方法的麻烦性,JSON 提供了 JSON.stringify(对象转换为 json) 和 JSON.parse(json 转换为对象) 方法。
JSON。stringify 支持Object{...}``Arrays[...]``strings``numbers``boolean values``null
不支持functions、存储undefined的属性、Symbol类型的键和值,遇到后直接忽略。
不能有循环引用。可以通过指定格式属性来解决。
// JSON.stringify(value, reolacer, spaces);
let student = {
name: "John",
age: 30,
isAdmin: false,
courses: ["html", "css", "js"],
spouse: null,
};
let json = JSON.stringify(student);
console.log(typeof json); // string
console.log(json);
/* JSON 编码的对象:
{
"name": "John",
"age": 30,
"isAdmin": false,
"courses": ["html", "css", "js"],
"spouse": null
}
*/
// toJSON 方法,可以自定义 JSON.stringify() 的行为
let room = {
number: 23,
toJSON() {
return this.number;
},
};
let meetup = {
title: "Conference",
room,
};
console.log(JSON.stringify(room)); // 23
console.log(JSON.stringify(meetup)); // {"title":"Conference","room":23}
// JSON.parse(value, reviver)
let numbers = "[1,2,3,4]";
numbers = JSON.parse(numbers);
console.log(numbers, typeof numbers); // [1,2,3,4] object
let userData =
'{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
let user = JSON.parse(userData);
console.log(user.friends[1]); // 1
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str, function (key, value) {
if (key == "date") return new Date(value);
return value;
});
console.log(meetup.date.getDate()); // 30
认识 jQuery
jQuery 是对 JS 的封装,简化 JS 的编程,作用效果和 JS 一样
当前 jQuery 兼容现在主流浏览器,加快开发效率
jQuery 的使用
jQuery 官网,一般使用旧版本,兼容性更高
jQuery 的引用
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
// 引用百度的jQuery
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
// jQuery的ready函数做为入口
$(document).ready(function () {
// 在jQuery中如果获取标签对象时,前缀要用一个$
let $d = $("#d1");
alert("jquery" + $d);
});
//简写法
$(function () {
let $d = $("#d1");
alert("简写" + $d);
});
</script>
</head>
<body>
<div class="" id="d1">asd</div>
</body>
</html>
jQuery 的选择器
快速选择标签元素,获取标签元素,选择规则和 css 一样。
选择器类型
- 标签选择器 $('li')
- 类选择器 $('.myClass')
- id 选择器 $('#myId')
- 层级选择器 $('#ul1 li span')选择 id 为 ul1 下的所有 li 标签下的 span 标签
- 属性选择器 $('input[name=first]')选择 name 属性等于 first 的 input 标签
判断是否选择成功用到.length,如果 lenght 大于 0 表示选择成功。
$(function () {
result = $("div").length;
alert(result);
});
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(function () {
//标签选择器
let $mk1 = $("h1");
$mk1.css({ background: "red" });
//类选择器
let $mk2 = $(".mp");
$mk2.css({ color: "blue" });
// id选择器
let $mk3 = $("#d1");
$mk3.css({ "font-size": "30px" });
// 层级选择器
let $mk4 = $("div a");
$mk4.css({ background: "red" });
// 属性选择器
let $mk5 = $("input[value=fgh]");
$mk5.css({ background: "blue" });
let $mk5 = $("input[type=password]");
$mk5.css({ background: "red" });
});
</script>
</head>
<body>
<h1>qwe</h1>
<p class="mp">asd</p>
<div class="" id="d1">zxc</div>
<div><a href="www.baiadu.com">rty</a></div>
<input type="text" name="" id="" value="fgh" />
<input type="password" name="" id="" value="vbn" />
</body>
</html>
选择器过滤
选择器过滤就是在选择的标签集合中过滤出自己需要的标签
- has(选择器名称)方法,选取包含指定选择器的标签
- eq(索引)方法,表示选取指定索引的标签
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(function () {
let $divs = $("div");
// alert($divs.length);
console.log($divs);
// has 过滤
let $mk1 = $divs.has("p");
$mk1.css({ background: "red" });
// last 过滤
let $mk2 = $divs.last();
$mk2.css({ background: "blue" });
// first 过滤
let $mk3 = $divs.first();
$mk3.css({ color: "blue" });
// eq 过滤
let $mk4 = $divs.eq(1);
$mk4.css({ color: "pink" });
});
</script>
</head>
<body>
<div><p>qwe</p></div>
<div>asd</div>
<div>zxc</div>
</body>
</html>
选择器转移
以某一个选择的标签为参照,获取转移后的标签
- $('#box').prev();表示选择 id 是 box 元素的上一个的同级元素
- $('#box').prevAll();表示选择 id 是 box 元素的上面所有同级元素
- $('#box').next();表示选择 id 是 box 元素的下一个的同级元素
- $('#box').nextAll();表示选择 id 是 box 元素的下面所有同级元素
- $('#box').parent();表示选择 id 是 box 元素的父元素
- $('#box').children();表示选择 id 是 box 元素的所有子元素
- $('#box').siblings();表示选择 id 是 box 元素的其他同级元素
- $('#box').find(".myClass");表示选择 id 是 box 元素的 class 为 myClass 的元素
设置标签内容
jQuery 中 html 方法可以获取和设置标签的 html 内容
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(function () {
let $div1 = $("#div1");
// alert($div1)
// 获取div当中的内容
let $mkp = $div1.html();
alert($mkp);
// 修改div当中的内容
$div1.html('<a href="www.baidu.com">baidu</a>');
// 添加div当中的内容
$div1.append("<hr>");
$div1.append('<a href="www.baidu.com">百度</a>');
});
</script>
</head>
<body>
<div id="div1"><p>hello</p></div>
</body>
</html>
设置标签元素属性
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(function () {
// 获取对象
let $inp = $('[type="text"]');
let $a = $("a");
// 通过prop()方法获取属性
$inp.prop({ value: "kkkk", class: "incla" });
$a.prop({ href: "www.biadu.com" });
// val方法可以快速获取value值
alert($inp.val());
$inp.val("ssss");
});
</script>
</head>
<body>
<input type="text" name="username" value="xxx" class="cin" id="inid" />
<a>百度</a>
</body>
</html>
jQuery 事件绑定
- click()鼠标单击
- blur()元素失去焦点
- focus()元素获得焦点
- mouseover()鼠标进入(进入子元素也触发)
- mouseout()鼠标离开(离开子元素也触发)
- ready()DOM 加载完成
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(function () {
let $div = $("div");
let $btn = $("#inb");
let $text = $("#in1");
let $lis = $("ol li");
// 给按钮绑定对象
$btn.click(function () {
alert("点到按钮了");
});
// 给列表绑定对象
$lis.click(function () {
// 点击后更改颜色,this指代当前的内容
$(this).css({ color: "red" });
// 弹出点击内容
alert($(this).html());
});
// 获取焦点
$text.focus(function () {
$(this).css({ background: "blue" });
$div.css({ background: "yellow" });
});
// 失去焦点
$text.blur(function () {
$(this).css({ background: "white" });
$div.css({ background: "white" });
});
// 鼠标移入
$div.mouseover(function () {
$(this).css({ width: "20px", height: "20px", background: "red" });
});
// 鼠标移出
$div.mouseout(function () {
$(this).css({
width: "100px",
height: "100px",
background: "yellow",
});
});
});
</script>
</head>
<body>
<div>aa</div>
<hr />
<input type="text" id="in1" />
<hr />
<input type="button" id="inb" value="ok" />
<hr />
<ol>
<li>第一</li>
<li>第二</li>
<li>第三</li>
</ol>
</body>
</html>
jQuery 事件代理
事件代理就是利用事件冒泡原理(事件会向父级一级一级传递),把事件加到父级身上,通过判断事件来源执行相对应的子元素操作,事件代理可以极大的减少事件绑定次数,提高性能;还可以让新加入的子元素也用有相同操作
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<style>
#fd {
width: 300px;
height: 300px;
background: red;
}
#cd {
width: 100px;
height: 100px;
background: blue;
}
</style>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<!-- 冒泡原理 -->
<!-- <script>
$(function(){
let $fd = $('#fd');
let $cd = $('div').eq(1);
$cd.click(function(){
alert("子标签被点击");
});
$fd.click(function(){
alert('父标签被点击了')
})
});
</script> -->
<!-- 代理父元素代理子元素 -->
<script>
$(function () {
let $ol = $("ol");
$ol.delegate("li", "click", function () {
$(this).css({ background: "red" });
alert($(this).html());
});
});
</script>
</head>
<body>
<div id="fd">
qwe
<div id="cd">asd</div>
</div>
<hr />
<ol>
<li>第一个</li>
<li>第二个</li>
<li>第三个</li>
</ol>
</body>
</html>
json
最外面一定要有容器进行包裹,内部只能用双引号
json 是一种数据格式,使用 js 的字符串进行描述
对象格式
{
"name": "tom",
"age": 18
}
数组格式
["tom", 18, "programmer"]
常出现的样式
{
"name":"jack",
"age":29,
"hobby":["reading","travel","photography"]
"school":{
"name":"Merrimack College",
"location":"North Andover, MA"
}
}
使用 js 进行解析 json
// 创建一个js的str类型以json数组为样式
let sjson1 = '[1,2,3,4,5,"a"]';
console.log(typeof sjson1);
// 数据解析成js数组
let jArray = JSON.parse(sjson1);
console.log(jArray);
// 第二种形式
let sjson2 = '{"name":"tom","age":21}';
let tom = JSON.parse(sjson2);
console.log(tom);
console.log(tom.name, tom.age);
ajax
ajax 是一个前后端配合的技术,可以让 JS 发送异步的 http 请求,与后台进行数据的获取,ajax 最大的优点就是实现局部的刷新,ajax 可以发送 http 请求,当获取到后台的数据后就可以更新页面显示数据,实现局部刷新,前端页面想同后端服务器进行数据交互就要使用 ajax
ajax 的使用
jQuery 将 ajax 封装成一个$.ajax()方法,使用方法就可以直接执行 ajax 请求。
基本样式
$.ajax({
// 1.url请求地址,可以写绝对地址,也可以写相对地址(可以自动补齐地址)
url: "",
// 2.type:请求方式,默认是"GET"请求方式,常用的还有"POST"
type: "",
// 3.dataType:设置返回的数据格式,常用的是"json"格式,还有"xml","html","text"
dataType: "",
// 4.data:设置发送给服务器的数据,没有参数可以不设置
data: {},
// 5.success:设置请求成功后的回调函数
success: function (response) {
console.log(response);
},
// 6.error:设置请求失败后的回调参数
error: function () {
alert("请求失败,请稍后再试");
},
// 7.async:设置是否异步进行,默认值为"true",表示异步,一般不用设置
async: true,
});
具体使用方法
|--ajax
|--data.json
|--page.html
{ "name": "苹果笔记本15", "price": 55000 }
<!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>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
// 这个方法传入的是一个js对象
$.ajax({
url: "data.json",
type: "GET",
dataType: "JSON",
// data:{page:1,count:20},
success: function (data) {
console.log(data);
let $ln = $("#ln").next();
$ln.html(data.name);
let $lp = $("#lp").next();
$lp.html(data.price);
},
error: function () {
alert("请求失败");
},
async: true,
});
</script>
</head>
<body>
<label id="ln">商品名:</label><label>a</label>
<hr />
<label id="lp">价格:</label><label>0</label>
</body>
</html>
ajax 简写
get
$.get(url,success(data,status,xhr),dataType).error(func)
$.get(
"data.json",
{ page: 1, count: 20 },
function (data) {
console.log(data);
let $ln = $("#ln").next();
$ln.html(data.name);
let $lp = $("#lp").next();
$lp.html(data.price);
},
"json"
).error(function () {
alert("请求失败");
});
post
$.post(url,success(data,status,xhr),dataType).error(func)
$.post("data.json", { page: 1, count: 20 }, function (data) {
console.log(data);
let $ln = $("#ln").next();
$ln.html(data.name);
let $lp = $("#lp").next();
$lp.html(data.price);
}).error(function () {
alert("请求失败");
});
Vue
Vue.js 使用的是 MVVM 框架,有容易上手的 API
Vue.js 是一个数据驱动的 Web 界面的库
Vue.js 是一套构建用户界面的渐进式框架
核心是一个响应的数据绑定系统
Vue.js 的官方文档
使用 Vue.js
vue 的导入
<!--导入Vue.js库进去-->
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
一般模板
<!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" />
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<!-- 导入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<!-- 定义一个标签,并给标签一个id值 -->
<div id="app"></div>
</body>
<!-- 创建vue实例 -->
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
message: "hello",
},
});
</script>
</html>
基础使用
<!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" />
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<!-- vue使用声明式渲染 -->
<a href="#">{{ message }}</a>
<hr />
<span>{{ hello }}</span>
<hr />
<!-- 使用v-bind给属性绑定数据 -->
<a v-bind:href="url">点击</a>
<hr />
<!-- 将v-bind进行简写 -->
<span :title="showmessage">鼠标放在这里</span>
<hr />
<!-- 使用v-if进行判断 -->
<a href="#" v-if="isLogin">欢迎</a>
<hr />
<!-- v-if与v-else及v-else-if必须挨着否则无效 -->
<a href="#" v-if="level === 1">青铜</a>
<a href="#" v-else-if="level === 2">白银</a>
<a href="#" v-else>王者</a>
<hr />
<!-- v-if和v-show的区别,使用f12打开控制台查看区别 -->
<span v-if="seen"> v-if </span>
<span v-show="seen"> v-shiw </span>
<hr />
<!-- v-for使用语法 -->
<ul>
<!-- 解包这个顺序不能改变 -->
<li v-for="(item,index) in items">{{index+1}} {{item}}</li>
</ul>
<hr />
<!-- 对对象进行遍历 -->
<ul>
<!-- 解包这个顺序不能改变 -->
<li v-for="(value,key) in object">{{key}} : {{value}}</li>
</ul>
<hr />
<!-- 使用v-on进行绑定动作 -->
<button v-on:click="login">登录</button>
<!-- 将v-on进行简写 -->
<a href="" @click="register">注册</a>
<button @click="add(counter)">+1</button>
<hr />
<!-- 绑定输入(双向绑定) -->
<table>
<tr>
<td>用户名</td>
<td><input type="text" name="username" v-model="username" /></td>
</tr>
<tr>
<td>密码</td>
<td>
<input type="password" name="password1" v-model="password1" />
</td>
</tr>
<tr>
<td>确认密码</td>
<td>
<input type="password" name="password2" v-model="password2" />
</td>
</tr>
<tr>
<td>性别</td>
<td>
男<input type="radio" name="sex" value="boy" v-model="sex" />
女<input type="radio" name="sex" value="girl" v-model="sex" />
</td>
</tr>
<tr>
<td>爱好</td>
<td>
足球<input
type="checkbox"
name="like"
value="足球"
v-model="like"
/>
篮球<input
type="checkbox"
name="like"
value="篮球"
v-model="like"
/>
乒乓球<input
type="checkbox"
name="like"
value="乒乓球"
v-model="like"
/>
</td>
</tr>
</table>
<button v-on:click="register2">注册</button>
</div>
</body>
<script type="text/javascript">
//创建一个app下的vue实例
let app = new Vue({
//挂载到id=app的标签上,el内写接管标签
el: "#app",
// 使用模板语言进行填充,数据进行双向绑定
// data内写绑定数据
data: {
// 内容message赋值一个hello vue
message: "hello vue",
hello: "你好",
// 将url发给绑定的位置
url: "https://www.baidu.com",
// 创建一个显示时间的函数赋值给showmessage
showmessage: "当前时间是" + new Date().toLocaleString(),
isLogin: true,
level: 1,
seen: false,
items: [1, 2, 3, 4, 5],
object: {
title: "How to do lists in Vue",
author: "Jane Doe",
publishedAt: "2016-04-10",
},
counter: 1,
username: "",
password1: "",
password2: "",
sex: "",
like: [],
},
// methods内写方法
methods: {
login: function () {
alert("成功");
},
register: function () {
alert("注册");
},
add: function (counter) {
// this表示当前vue,通过this.level访问data中的变量
this.level += counter;
},
register2: function () {
alert(
this.username +
this.password1 +
this.password2 +
this.sex +
this.like
);
},
},
});
</script>
</html>
todolist 示例
<!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" />
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<!-- 导入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<!-- 定义标签 -->
<div id="app">
<input type="text" name="todoitem" v-model="newitem" /><button
@click="add"
>
添加
</button>
<hr />
<ul>
<li v-for="(item,index) in items">
<a href="javascript:;" @click="up(index)">↑</a>
{{ item }}
<a href="#" @click="deleteitem(index)">删除</a>
<a href="javascript:;" @click="down(index)">↓</a>
</li>
</ul>
</div>
</body>
<!-- 创建vue实例 -->
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
items: ["asdasd", "asdasdqwe"],
newitem: "",
},
methods: {
add: function () {
this.items.push(this.newitem);
this.newitem = " ";
},
deleteitem: function (index) {
this.items.splice(index, 1);
},
up: function (index) {
// 获取当前元素
current = this.items[index];
// 把当前元素删除
this.items.splice(index, 1);
// 再添加,添加索引加1
this.items.splice(index - 1, 0, current);
},
down: function (index) {
// 获取当前元素
current = this.items[index];
// 把当前元素删除
this.items.splice(index, 1);
// 再添加,添加索引加1
this.items.splice(index + 1, 0, current);
},
},
});
</script>
</html>
vue 实例生命周期

<!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" />
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<!-- 导入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<!-- 定义标签 -->
<div id="app">
<span> {{message}} </span>
</div>
</body>
<!-- 创建vue实例 -->
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
message: "hello",
},
// 生命周器的钩子(函数),没有在methods内
// app对象实例化之前
beforeCreate: function () {
console.log("beforeCreate");
},
// app对象实例化后
created: function () {
console.log("created");
},
// app将作用标签之前
beforeMount: function () {
console.log("beforeMounted");
},
// app将作用标签之后
mounted: function () {
console.log("mounted");
},
// 数据或者属性更新之前
berforeDestroy() {
console.log("berforeDestroy");
},
// 数据或属性更新之后
destroyed: function () {
console.log("destroyed");
},
});
</script>
</html>
发送 ajax 请求
vue 本身不能发送 ajax 请求,可以使用 axios 发送,是 vue2.0 官方推荐的
cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<!-- 导入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 导入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Document</title>
</head>
<body>
<!-- 定义一个标签,并给标签一个id值 -->
<div id="app">
<!-- vue的大胡子模板语法和django/flask的模板语法冲突 -->
<span>[[ message ]]</span>
<button @click="login">登录</button>
<hr />
[[ username ]]
<hr />
<button @click="login2">post</button>
</div>
</body>
<!-- 创建vue实例 -->
<script type="text/javascript">
let app = new Vue({
el: "#app",
// 修改vue语法的分隔符
delimiters: ["[[", "]]"],
data: {
message: "hello",
username: "",
},
methods: {
// 这里发送ajax请求
login: function () {
alert("登录成功");
// 定义一个url地址
let url = "http://127.0.0.1:8000/rece?username=itcast&password=1234";
// then catch 用 => 箭头函数 this指向问题
axios
.get(url)
.then((response) => {
// 返回的数据responser(响应)-->response.data(数据)-->response.data.info(ajax返回的字典)-->username
console.log(response.data.info.username);
this.username = response.data.info.username;
})
.catch((error) => {
console.log(error);
});
},
login2: function () {
// axios.post().then().catch() then成功的回调,catach失败的回调
let url = "http://127.0.0.1:8000/rece/";
axios
.post(url, {
username: "李四",
password: "123456",
})
.then((response) => {
console.log(response.data.info.username);
this.username = response.data.info.username;
})
.catch((error) => {
console.log(response.data.info.username);
});
},
},
});
</script>
</html>
后端参考 python 的 django 部分文档 view 内主要代码
import json
from django.shortcuts import render
from django.views import View
from django.http import JsonResponse
# Create your views here.
class LoginView(View):
def get(self,request):
return render(request,'login.html')
def post(self,request):
pass
class ReceiveView(View):
def get(self,request):
# 1。接收参数
data = request.GET
username = data.get('username')
password = data.get('password')
return JsonResponse({'info':{'username':username}})
def post(self,request):
data = json.loads(request.body.decode())
username = data.get('username')
password = data.get('password')
return JsonResponse({'info':{'username':username}})
urls 内主要代码
主路由
from django.contrib import admin
from django.urls import path, include, re_path
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^',include('book.urls')),
]
分路由
from django.urls import path, re_path
from book.views import LoginView,ReceiveView
urlpatterns = [
re_path(r'^login/$',LoginView.as_view()),
re_path(r'^rece/$',ReceiveView.as_view()),
]
canvas
认识 canvas
可以使用 canvas 进行网页背景设计和图表设计
官方文档 mdn
一个简单的 canvas
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 填充矩形:fillRect(位置x,位置y,宽度,高度)
ctx.fillRect(100,200,300,300);
</script>
</body>
canvas 绘制基本图形
栅格

canvas 的宽高和 css 设置的宽高
css 相当于对图片的放大缩小
canvas 是指定图片的大小
修改 css 对 canvas 的图片进行拉伸或压缩,导致图片变形
矩形绘制填充模式
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 填充矩形:fillRect(位置x,位置y,宽度,高度)
ctx.fillRect(100,200,300,300);
// 填充矩形拆开写法:ctx.rect(100,200,300,300);ctx.fill();
// 单写ctx.rect()不会显示,
ctx.rect(100,200,300,300);
// 填充后才显示
ctx.fill();
</script>
</body>
矩形绘制路径模式
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 路径绘制矩形:strockeRect(位置x,位置y,宽度,高度)
ctx.strokeRect(100,100,200,100);
// 路径绘制矩形拆开写法:ctx.rect(100,200,300,300);ctx.stroke();
// 单写ctx.rect()不会显示,
ctx.rect(100,200,300,300);
// 填充后才显示
ctx.stroke();
</script>
</body>
矩形绘制清除模式
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 填充矩形:fillRect(位置x,位置y,宽度,高度)
ctx.fillRect(100,200,300,300);
// 清除填充内容
ctx.clearRect(0,0,c1.clientWidth,c1.clientHeight);
let height = 0;
// 逐渐清除(每10ms清除一个height)
let t1 = setInterval(()=>{
height++;
ctx.clearRect(0,0,c1.clientWidth,height);
// 判断清除函数
if(height > c1.clientHeight){
// 清除t1这个函数
clearInterval(t1);
}
},10)
</script>
</body>
画笔抬起
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 填充矩形:fillRect(位置x,位置y,宽度,高度)
// 下笔
ctx.bgeinPath();
ctx.rect(100,100,200,100);
ctx.stroke();
// 抬笔
ctx.closePath();
// 下笔
ctx.bgeinPath();
ctx.rect(200,150,200,100);
ctx.fill();
// 抬笔
ctx.closePath();
</script>
</body>
绘制圆,圆弧
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 绘制圆弧:ctx.arc(圆心x,圆心y,半径r,开始的角度,结束的角度,顺时针false /逆时针true)
ctx.arc(300,200,50,0,Math.PI*2);
ctx.fill();
// 起始点
ctx.moveTo(300,200);
// 绘制圆弧:ctx.arcTo(点1x,点1y,点2x,点2y,半径r)
ctx.arcTo(300,250,250,250,25);
ctx.strock();
</script>
</body>
使用 moveTo 移动点
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
ctx.beginPath();
// 绘制圆脸:
ctx.arc(75,75,50,0,Math.PI *2);
// 移动画笔
ctx.moveTo(110,75)
// 绘制嘴巴
ctx.arc(75,75,35,0,Math.PI);
// 移动画笔
ctx.moveTo(65,65)
// 绘制左眼
ctx.arc(60,65,5,0,Math.PI *2);
// 移动画笔
ctx.moveTo(95,65)
//绘制右眼
ctx.arc(90,65,5,0,Math.PI *2);
// 填充线段
ctx.stroke();
ctx.closePath();
</script>
</body>
绘制折线
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
ctx.beginPath();
// 绘制折线
// 开始位置在300,200
ctx.moveTo(300,200);
// 结束路径在350,250
ctx.lineTo(350,250);
ctx.strock();
ctx.closePath();
</script>
</body>
贝塞尔曲线
贝塞尔曲线由三个点控制的曲线,这三个点中一个起点,一个终点,还有一个控制点,控制点可以再增加其他点进行控制
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 绘制图形
// 贝塞尔曲线:quadraticCurveTo(控制点x,控制点y,终点x,终点y);
// 冒泡聊天框
// 起始点
ctx.moveTo(200,300);
// 控制点,终止点
ctx.quadraticCurveTo(150,300,150,200);
ctx.quadraticCurveTo(100,100,300,100);
ctx.quadraticCurveTo(450,100,450,200);
ctx.quadraticCurveTo(450,300,250,300);
ctx.quadraticCurveTo(250,350,150,300);
ctx.quadraticCurveTo(200,350,200,300);
ctx.strock();
// 贝塞尔曲线:ctx.bezierCurveTo(控制点1x,控制点1y,控制点2x,控制点2y,终点x,终点y);
// 爱心
// 起始点
ctx.moveTo(200,300);
// 控制点1,控制点2,终止点
ctx.bezierCurveTo(350,150,400,200,300,300);
ctx.bezierCurveTo(350,150,400,200,300,300);
ctx.strock();
</script>
</body>
path2D-封装路径
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 创建路径实例
let heartPath = new.Path2D();
// 路径起始点
heartPath.moveTo(200,300);
// 路径使用贝塞尔曲线绘制
heartPath.bezierCurveTo(350,150,400,200,300,300);
heartPath.bezierCurveTo(350,150,400,200,300,300);
// 绘制制定的路径
cxt.strock(heartPath);
// 创建一条折线
// 使用svg的写法
let polyline = new Path2D("M10 10 h 80 v 80 h -80 z");
ctx.strocke(polyline);
</script>
</body>
颜色修改
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
ctx.strokeStyle = "red";
ctx.strokeRect(100,100,200,100);
ctx.strokeStyle = "#ff00ff";
ctx.rect(100,200,300,300);
ctx.stroke();
</script>
</body>
封装绘制的物体
<bod>
<!--
id:标识元素的唯一性
width:画布的宽度
height:画布的高度
-->
<canvas id="c1" width="600" heigth="400">
当前浏览器不支持canvas请下载最新版的浏览器
<a herf="https://www.google.cn/intl/zh-CN/chrome">下载新版浏览器</a>
</canvas>
<script>
// 1.找到画布
let c1 = document.getElementById('c1');
// 判断浏览器是否支持canvas
if(!c1.getContext){
console.log("当前浏览器不支持canvas请下载最新版的浏览器")
}
// 2.获取画笔,上下文对象
let ctx = c1.getContext('2d');
// 封装
class Heart{
constructor(x,y){
this.x = x;
this.y = y;
this.color = "red";
this.isIn = false;
this.eventMapList = {
hover:[],
leave:[],
};
// 监听鼠标
c1.onmousemove = (e)=>{
// 获取鼠标位置
let x = e.offsetX;
let x = e.offsetX;
this.isIn = ctx.isPointInPath(
this.hearPath,x,y);
is(this.isIn){
this.eventMapList.hover.forEach((item)=>{item();});
}else{
this.eventMapList.hover.forEach((item)=>{item();});
}
};
}
onHover(fn){
this.eventMapList.hover.push(fn);
}
onLeace(fn){
this.eventMapList.leave.push(fn);
}
// 修改设置x,y
setPostition(x,y){
this.x = x;
this.y = y;
}
draw(){
this.heartPath = new path2D();
this.heartPath.moveTo(this.x,this.y);
heartPath.bezierCurveTo(
this.x+50,
this.y-50,
this.x+100,
this.y,
this.x,
this.y+50
);
heartPath.bezierCurveTo(
this.x-100,
this.y,
this.x-50,
this.y-50,
this.x,
this,y
);
ctx.save();
ctx.fillStyle = this.color;
ctx.fill(this.heartpath);
ctx.restore();
}
}
// 创建画图实例
let heart = new Heart(100,100);
heart.onHover(()=>{
heart.color = "blue";
})
heart.onLeave(()=>{
heart.color = "red";
})
// 改动更新
function render(){
ctx.clearRect(0,0,c1.width,c1.heigth);
heart.draw();
// 不定时异步刷新
requestAnimationFrame(render);
}
// 调用主函数
render();
</script>
</body>

浙公网安备 33010602011771号