目录
3、用动object.defineProperty()对象态添加 get,set
放在script标签之间
引入js脚本
注意,到了框架之后,引入方式会有不同
let ⭐
let 变量名 = 值;
let 声明的变量可以被多次赋值
let a = 100; // 初始值是100 a = 200; // ok,被重新赋值为200
const ⭐
const 常量 = 值;
const 修饰的叫常量,只能赋值一次
const b = 300; //初始值是300 b = 400; //error,不能再赋值
const 并不意味着它引用的内容不可修改,例如
const c = [1,2,3]; c[2] = 4; //ok,数组内容被修改成[1,2,4] c = [5,6]; // error.不能再次赋值
var
var声明的变量可以被多次赋值,例如
let a = 100; // 初始值是100 a = 200; // ok,被重新赋值为200
执行表达式或函数,没有返回结果,出现undefined
访问数组不存在的元素,访问对象不存在的属性,出现undefined
定义变量,没有初始化,出现undefined
二者共同点
都没有属性、方法
Nullish(undefined和null共同的称呼)
二者区别
undefined 由 js 产生
null 有程序员提供
html 代码如下,用java和js种的字符串如何表示?
超链接
java
String s1 = "超链接"; String s2 = """超链接 """;
js
let s1 = '超链接'; let s2 = `超链接`;
需求:拼接URI的请求参数,如
/test?name=zhang&age=18 /test?name=li&age=20
传统方法拼接:可读性低
let name = ; //zhang li ... let age = ; // 18 20 ... let uri = "/test?name" + name + "&age=" + age;
模板字符串方式:可读性高
let name = ; //zhang li ...
let age = ; // 18 20 ...
// 模板字符串方式 需要使用反引号使用
let uri = `/test?name=${name}&age=${age}`
number 类型表示的是双精度浮动小数,例如
10 / 3; //结果 3.3333333333333335
既然是浮点小数,那么可以除零
10 / 0; // 结果 Infinity 正无穷大 -10 / 0; // 结果 -Infinity 负无穷大
浮点小数都有运算精度问题,例如
2.0 - 1.1 // 结果 0.899999999999999
字符串转数字
parseInt("10"); // 结果是数字 10
parseInt("10.5"); // 结果是数字 10,去除了小数部分
parseInt("10") / 3; // 结果仍是为 number 浮点数,因此结果为 3.3333333333333335
parseInt("abc"); // 转换失败,结果是特殊值 NaN (Not a Number)
需要表示真正的整数,需要用 bigint,数字的结尾用 n 表示它是一个 bigint 类型
10n / 3n; // 结果 3n,按整数除法处理
Truthy
Falsy
在js种,并不是 boolean 才能用于条件判断,你可以在if语句种使用【数字】、【字符串】...自拍为判断条件
let b = 1;
if(b){ // true
console.log("进入了");
}
这时就有一个规则,当需要条件判断时,这个值被当作 true 还是 false,当作 true 的值归类为 truthy,当作 false的值归类为 falsy
falsy
false
Nullish(null,undefined)
0 或 0n 或 NaN(0和 非数字)
"" 或 '' 或 `` (长度为零的空字符串)
定义
function 函数名(参数){
// 函数体
return 结果; // 不是必须的
}
例子
function add(a,b){
return a + b;
}
调用函数
函数名(实参);
例子
add(1,2); // 返回 3
add('a','b'); // 返回 ad
add(4,5,6); // 返回 9,第三个参数没有被用到,不会报错
add(1); // 返回 NaN,这是 b 没有定义是 undefined,undefined做数学运算结果就是 NaN
java中(spring)要实现默认参数的效果
@RestController
public class MyController {
@RequestMapping("/page")
@ResponseBody
public void page(
@RequestParam(defaultValue="1") int page,
@RequestParam(defaultValue="10") int siza
){
// ...
}
}
js
function pagination(page = 1,size = 10){
console.log(page,size)
}
// 显示默认值
pagination() // 1 10
// page 显示默认值
pagination(undefined,20) // 1 20
// size 显示默认值
pagination(2) // 2 10
语法
(function(参数){
// 函数体
return 结果;
})
例
(function(a,b){
return a + b;
})
第一种场景:定义完毕后立刻调用
(function(a,b){
return a + b;
})(1,2)
第二种场景:作为其它对象的方法,例如
页面有元素
点我啊
此元素有一个onclick方法,会在鼠标单击这个元素后被执行,onclick方法刚开始是null,需要赋值后才能使用
document.getElementById("p1").onclick = (function(){
console.log("鼠标单机了...")
});
(参数) => {
// 函数体
return 结果;
}
如果没有参数,()还是要保留
如果只有一个参数,()可以省略
如果函数体内只有一行代码,{}可以省略
如果函数体内的一行代码是返回代码,那么return也可以省略
例:
document.getElementById("p1").onclick = () => console.log("鼠标单机了...箭头函数")function abc(){ console.log("bb"); } document.getElementById("p1").onclick = abc;
以下形式在 js 中非常常见!
可以参与赋值,例,具名函数也能参与赋值
function abc(){
console.log("bb");
}
document.getElementById("p1").onclick = abc;
// 如果console.log(abc) 看不到对象的内部结构
// 就是用console.dir() 查看对象的内部结构
console.dir(abc)
有属性、有方法
f abc()
arguments: null
caller: null
length: 0
name: "abc"
➡prototype: {constructor: f}
[[FunctionLocation]]: VM1962:1
➡[[Prototype]]: f()
➡[[Scopes]]: Scopes[1]
其中带有 f 标记的是方法,不带的是属性
带有➡符号的可以继续展开,限于篇幅省略了
带有 [[ ]] 的是内置属性,不能访问,只能查看
相对重要的是 [[Prototype]] 和 [[Scopes]] 会在后面继承和作用域时讲到
可以作为方法参数
function a(){
console.log('a');
}
// 接受了函数作为参数的函数叫做 "高阶函数"
function b(fn){ //fn 将来可以是一个函数对象
console.log('b');
fn(); // 调用函数对象
}
// 将来调用b的时候可以把a传进去
b(a)
可以作为方法返回值
// c函数把b函数当成了自己的返回值,那么c函数就是高阶函数
function c(){
console.log("c");
function d(){
console.log("d");
}
return d;
}
c(); // 返回结果 c f d(){ console.log("d"); }
c()(); // 返回函数对象 c d
函数可以嵌套( js 代码中很常见,只是嵌套的形式更多是匿名函数,箭头函数)
function a(){
function b(){
}
}
看下面的例子
function c(){
var z = 30;
}
var x = 10;
function a(){
var y = 20;
function b(){
// 看这里
console.log(x,y);
}
b();
}
a();
以函数为分界线划定作用域,所有函数之外是全局作用域
查找变量时,由内向外查找
在内层作用域找到变量,就会停止查找,不会再找外层
所用作用域都找不到变量,报错
作用域本质上时函数对象的属性,可以通过 console.dir 来查看调式
var x = 10;
function a(){
var y = 20;
function b(){
console.log(x,y);
}
return b;
}
a()(); // 在外面执行了 b
函数定义时,他的作用域已经确定好了,因此无论函数将来去了哪,都能从它的作用域中找到当时哪些变量
别被概念忽悠了,闭包就是指函数能够访问自己的作用域中变量
如果函数外层引用的是let变量,那么外层普通的{}也会作为作用域边界,最外层的let 也占一个 script 作用域
let x = 10;
if(true){
let y = 20;
function b(){
console.log(x,y);
}
console.dir(b);
}
// 3个作用域(不包含自己):1、if() 2、let x = 10 3、Global
如果函数外层引用的是 var 变量,外层普通的 {} 不会被视为边界
var x = 10;
if(true){
var y = 20;
function b(){
console.log(x,y)
}
console.dir(b);
}
// 1个作用域(不包含自己) 1、Global
如果 var 变量出现了重名,则他俩会被视为同一作用域中的同一个变量
var e = 10;
if(true){
var e = 20;
console.log(e); // 打印 20
}
console.log(e); // 因为是同一个变量,还是打印 20
如果是let,则视为两个作用域中的两个变量
let e = 10;
if(true){
let e = 20;
console.log(e); // 打印 20
}
console.log(e); // 打印10
要想里面的 e 和外面的 e 能区分开来,最简单的办法时改成let,或者用函数来界定作用域范围
var e = 10;
if(true){
function b(){
var e = 20;
console.log(e);
}
}
console.log(e);
语法
// 创建数组
let arr = [1,2,3];
// 获取数组元素
console.log(arr[0]); // 输出 1
// 修改数组元素
arr[0] = 5; // 数组元素变成了 [5,2,3]
// 遍历数组元素,其中 length 是数组属性, 代表数组长度
for(let i = 0; i < arr.length; i++){
console.log(arr[i])
}
API
let arr = [1,2,3] arr.push(4); // 向数组尾部(右侧)添加元素,结果 [1,2,3,4] arr.shift(); // 从数组头部(左侧)移除元素,结果 [2,3,4] arr.splice(1,1) // 删除【参数1】索引位置的【参数2】个元素,结果[2,4] arr.splice(1,0,1) // 在【参数1】索引位置的添加【参数3】元素并且【参数3】的索引为【参数1】 // 结果[2,1,4] 【参数2】的意思是删除的意思0是删除0个元素 arr.unshift(100) // 向数组头部(左侧)添加元素,结果[100,2,4] arr.slice(start,end)// slice()通过索引位置获取新的数组,该方法不会修改原数组,只是返回一个新的子数组。 左闭右开 [start,end) // 【参数1】:start - 必填,设定新数组的起始位置;如果是负数,则表示从数组尾部开始算起(-1指最后一个元素,-2 指倒数第二个元素,以此类推)。 // 【参数2】end - 可选;设定新数组的结束位置;如果不填写该参数,默认到数组结尾;如果是负数,则表示从数组尾部开始算起(-1 指最后一个元素,-2指倒数第 二个元素,以此类推)。 Array.splice(start,delete_count,value,...) // 插入、删除、替换数组 // (1) start:开始插入和(或)删除的数组元素的下标。 // (2) delete_count:结束截取的数组下标,如果end是负数,表明从数组尾部开始计算. // (3)要插入数组的元素。value,-..: // 返回:如果从数组中删除了元素,则返回的是被删除的元素的数组
let arr = ['a','b','c'];
arr.join(); // 默认使用【,】作为连接符,结果'a,b,c'
arr.join(''); // 结果 'abc'
arr.join('-'); // 结果 'a-b-c'
map 例子
let arr = [1,2,3,6];
// [10,20,30,60]
function a(i){ // 代表的新旧元素之间的变化规则
return i * 10;
}
arr.map(a); // 具名函数,结果 [10,20,30,60]
// ↓ 进一步简化
arr.map( (i)=>{return i * 10}); // 箭头函数
// ↓ 进一步简化
arr.map( i => i * 10); // 箭头函数
传给 map 的函数,参数代表旧元素,返回值代表新元素,map的内部实现(伪代码)
function map(a){ // 参数是一个函数
let narr = [];
for(let i = 0; i < arr.length; i++){
let o = arr[i]; // 旧元素
let n = a(o); // 新元素
narr.push(n);
}
return narr;
}
filter 例子
let arr = [1,2,3,6]; arr.filter( i => i % 2 == 1); // 结果 [1,3]
传给 filter 的函数,参数代表旧元素,返回值 true 表示要留下的元素
forEach例子
let arr = [1,2,3,6];
for(let i = 0; i < arr.length; i++){
console.log(arr[i])
}
arr.forEach( i => console.log(i));
arr.forEach((v,i) => {console.log(`n[${i}]=${v}`)})
字符串转数组
let arr4 = '中国人'.split('')
console.log(arr4) // 输出结果 ['中','国','人']
注意:num是一个非空数字数组
升序:num.sort((a,b) => a-b)
降序:num.sort((a,b) => b-a)
洗牌:num.sort(() => Math.random() > .5 ? a : b)
根据元素查找元素
let n = [1,2,3,4,5,6,7,8,9,10,2,4,2,1] console.log(n.find(e => e === 10)) // 10
// 去重
console.log('-------------去重')
let nn = [1,2,3,4,5,6,5,7,5,8,6,9,4,6]
console.log(nn)
// 方法1 过滤
let arr = nn.filter((v,i)=>nn.indexOf(v)===i)
console.log(arr)
// 方法2 set方法
let arr2 = [...new Set(nn)]
console.log(arr2)
let nn = [1,2,3,4,5,6,5,7,5,8,6,9,4,6]
let n2 = [1,2,3,4]
console.log(nn)
console.log(n2)
console.log('-------------数组和')
nn.push(...n2)
console.log(nn)
let a1 = [1, 2, 3] let a2 = [11, 22, 33, 1, 2, 3] // 差集 let a4 = a2.filter(e => !a1.includes(e)) console.log(a4) // 交集 let a3 = a1.filter(e => a2.includes(e)) console.log(a3) // 并和 去重 a1.push(...a2.filter(e => !a1.includes(e))) console.log(a1)
let obj = {
属性名: 值,
方法名: 函数,
get 属性名() {},
set 属性名(新值) {}
}
例1 写法1
let stu1 = {
name: "小明",
age: 18,
study: function(){
console.log(this.name + "爱学习");
}
}
例2 写法2
let name = "小黑";
let age = 20;
let study = function(){
console.log(this.name + "爱学习");
}
let stu2 = {
name,
age,
study
}
例3 (例1的简写方式 重点)⭐
let stu3 = {
name: "小明",
age: 18,
study(){
console.log(this.name + "爱学习");
}
}
注意:对象方法这么写,仅限于对象内部
例4
let stu4 = {
_name: null, // 类似于java中私有成员变量
get name(){
console.log("进入了get");
return this._name;
},
set name(name){
console.log("进入了set");
this._name = name;
}
}
调用 get,set
stu4.name = "小白"; // 调用set 赋值语句 console.log(stu4.name) // 调用get
对比以下 Java 中的 Object
Java 的 Object 是以类作为模板来创建,对象不能脱离类模板的范围,一个对象的属性,能用的方法都是确定好的
js 的对象,不需要什么模板,它的属性和方法可以随时加减
let stu = {name: '张三'};
stu.age = 18; // 这个age属性是创建stu后加上的
delete stu.age; // 删除 stu这个对象中的age属性
stu.study = function() {
console.log(this.name + "在学习"); // 这个study方法是后加的
}
let stu = {
_name:null
};
// 第一个参数 给哪一个对象定义属性
// 第二个参数 属性名 不要和_name冲突
// 第三个参数 包含了get,set的定义
object.defineProperty(stu,"name",{
get(){
return this._name
},
set(name){
this._name = name;
}
});
先来对 Java 中的 this 有个理解
public class TestMethod{
static class Student{
private String name;
public Student(String name){
this.name = name;
}
// 隐式参数Student this
public void study(Student this,String subject){
System.out.println(this.name + ": 在学习" + subject);
}
}
public static void main(String[] args){
Student stu = new Student("小明");
// 下面的代码,本质上是执行 study(stu, "java"),因此 this 就是 stu
stu.study("java");
}
}
js 中的 this 也是隐式参数,但是它与函数运行时上下文相关
①、一个”落单“的函数
function study(subject){ console.log(this.name + "在学习" + subject); }测试以下
study("js"); // 输出 在学习 js
这是因为此时函数执行,全局对象 window 被当作了 this, window 对象的 name 属性是空串
②、同样的函数,如果作为对象的方法
let stu = {
name: "小白",
study
}
这种情况下,会将当前对象作为 this
③、.call 还可以动态改变this
let stu = {name:"小黑"};
// 第一个参数:你要把this视为谁?
// 第二个参数:传入study方法需要的形参
study.call(stu,"js"); // 输出 小黑在学习 js
这回 study 执行时,就把 call 的第一个参数 stu 作为 this
例外:在箭头函数内出现的 this,以外层 this 理解
用匿名函数
let stu = {
name: "小花",
friends: ["小白","小黑","小明"],
play:function(){ // play:function() == play()
this.friends.forEach(function(e){
console.log(this.name + "与" + e + "在玩耍");
});
}
}
stu.play();
// 第一个this指的时stu 第二个this指的是window(因为第二个函数时落单的函数)
this.name 所在的函数时【落单】的函数,因此 this 代表 window
输出结果为
与小白在玩耍 与小黑在玩耍 与小明在玩耍
用箭头函数
let stu = {
name: "小花",
friends: ["小白","小黑","小明"],
play(){
this.friends.forEach(e =>{
console.log(this.name + "与" + e + "在玩耍");
});
}
}
//在箭头函数内出现的 this,以外层 this 理解
this.name 所在的函数是箭头函数,因此 this 要看它外层的 play 函数, play 又是属于 stu 的方法,因此 this 代表 stu 对象
输出结果为
小花与小白在玩耍 小花与小黑在玩耍 小花与小明在玩耍
不用箭头函数的做法
let stu = {
name: "小花",
friends: ["小白","小黑","小明"],
play(){
let me = this;
this.friends.forEach(function(e){
console.log(me.name + "与" + e + "在玩耍");
});
}
}
let father = {
f1: '父属性',
m1: function(){
console.log("父方法");
}
}
// .create:以父对象为原型创建一个子对象
let son = Object.create(father);
console.log(son.f1); // 打印 父属性
son.m1 // 打印 父方法
father 是父对象,son 去调用 .m1 或 .f1 时,自身对象没有,就到父对象找
son 自己可以添加自己的属性和方法
son 里有特殊属性 __proto__ 代表它的父对象,js 术语:son 的原型对象
不同浏览器对打印 son 的 __proto__ 属性时显示不同
Edge 打印 console.dir(son) 显示 [[prototype]]
Firefox 打印 console.dir(son) 显示
出于方便的原因,js 又提供了一种基于函数的原型继承
函数职责
负责创建子对象,给子对象提供属性,方法,功能上相当于构造方法
函数有个特殊的属性 prototype,它就是函数创建的子对象的父对象
注意! 名字有差异,这个属性的作用就是为新对象提供原型
function cons(f2){
// 创建子对象(this),给子对象提供属性和方法
this.f2 = f2,
this.m2 = function (){
console.log("子方法");
}
}
// cons.prototype 就是父对象
cons.prototype.f1 = "父属性";
cons.prtotype.m1 = function(){
console.log("父方法");
}
配合 new 关键字,创建子对象
let son = new cons("子属性");
子对象的__proto__就是函数的 prototype 属性
之前我们将 http 请求格式时,讲过 json 这种数据格式,他的语法看起来与 js 对象非常相似,例如:
一个 json 对象可以张这样:
{
"name":"张三",
"age":18
}
一个 js 对象长这样
{
name:"张三",
age:18
}
本质不同
json 对象本质上就是个字符串,它的职责是作为客户端和服务器之间传递数据的一种格式,它的属性只是样子货
js 对象是切切实实的对象,可以有属性方法
语法细节不同
json 中只能有null、true|false、数字、字符串(只有双引号)、对象、数组
json 中不能有除以上的其他 js 对象的特性、如方法等
json 中的属性必须用双引号引起来
// 把 json 字符串转化为 js 对象 返回对象js对象 JSON.parse(json字符串); // 把 js 对象转换成 json 字符串 返回json字符串 JSON.stringify(js对象);
let json = `{
"name":"张三",
"age":18
}`;
let obj = JSON.parse(json);
静态类型语言,如 Java,值有类型,变量也有类型、赋值给变量时,类型要相符
int a = 10; String b = "abc"; int c = "abc"; // 错误
而 js 属于动态类型语言,值有类型,但变量没有类型,赋值给变量时,没要求
例如
let a = 200; let b = 100; b = 'abc'; b = true;
动态类型看起来比较灵活,但变量没有类型,会给后期维护带来困难,例如
function test(obj){
// obj 的类型未知,必须根据不同类型做出各种容错处理
}
+ - * / % **
+= -= *= /= %= **=
++ --
位移算、移位运算(估计大家用不着,用到时候上网搜索)
== != > >= < <=
=== !==⭐
&& || !⭐
?? ?.⭐
...⭐
解构赋值[]{}⭐
**:乘方
**=:乘方等
===(严格相等)严格相等运算符,用作逻辑判等。
1 == 1 // 返回 true 1 == '1' // 返回 true,会先将右侧的字符串转为数字,再做比较 1 === '1' // 返回 false。类型不等,直接返回 false
补充:typeod 查看某个值的类型
typeof 1; // 返回 'number' typeof 'a'; // 返回 'string'
||(逻辑或)需求,如果参数 n 没有传递,给它一个 【男】
推荐做法
function test(n = '男'){
console.log(n);
}
你可能的做法
function test(n){
if(n === undefined){
n = '男';
}
console.log(n)
}
还可能时这样
function test(n){
n = (n === undefined) ? '男' : n;
console.log(n);
}
一些老旧代码中可能的做法(不推荐,有潜在问题)
function test(n){
n = n || '男';
console.log(n);
}
它的语法时
值1 || 值2
如果值1 时 Truthy,返回值1,如果值1 时 Falsy 返回值2
?? 与 ?.?. 可选链操作符用于访问可能为空或未定义的属性或方法,它允许我们安全地访问嵌套对象的属性
?? 空值合并操作符用于检查一个变量是否为 null 或 undefined,如果是,则返回一个默认值,否则返回该变量的值。与传统的逻辑运算符 || 不同,?? 只会在左侧的值为 null 或 undefined 时返回右侧的默认值,对于其他假值(如空字符串、0、false 等)并不会返回默认值,而是会返回它本身。
需求,如果参数 n 没有传递值或是 null,给它一个【男】
如果用传统办法
function test(n){
if(n === undefined || n === null){
n = '男';
}
console.log(n);
}
用 ??
function test(n){
n = n ?? '男';
console.log(n)
}
需求,函数参数是一个对象,可能包含有子属性
例如,参数可能是
let stu1 = {
name:"张三",
address:{
city:'北京'
}
}
let stu2 = {
name:"李四",
}
let stu3 = {
name:"李四",
address: null
}
现在要访问子属性
function test(stu){
console.log(stu.address.city)
}
现在希望当某个属性是 nullish 时,短路并返回 undefined
function test(stu){
console.log(stu.address?.city)
}
用传统办法
function test(stu){
if(stu.address === undefined || stu.address === null){
console.log(undefined);
return;
}
console.log(stu.address.city);
}
...展开运运算符
作用1:打散数组传递给多个参数
let arr = [1,2,3];
function test(a,b,c){
console.log(a,b,c);
}
传统的打散写法
test(arr[0],arr[1],arr[2]); // 输出 1,2,3
展开运算符写法
test(...arr); // 输出 1,2,3
打散可以理解为【去掉了】数组外侧的中括号,只剩下数组元素
作用2:复制数组或对象
数组
let arr1 = [1,2,3]; let arr2 = [...arr1]; // 复制数组
对象
let obj1 = {name:'张三',age:18};
let obj2 = {...obj1}; // 复制对象
注意:展开运算符复制属于浅拷贝(只能复制一层,多层的话就是引用了),例如
let o1 = {name:'张三',address:{city:'北京'}}
let o2 = {...o1};
作用3:合并数组或对象
合并数组
let a1 = [1,2]; let a2 = [3,4]; let b1 = [...a1,...a2]; // 合并数组 [1,2,3,4] let b2 = [...a2,5,...a1] // 输出 [3,4,5,1,2]
合并对象
let o1 = {name:'张三'};
let o2 = {age:18};
let o3 = {name:'李四'};
let n1 = {...o1, ...o2}; // 结果 {name:'张三',age:18}
let n2 = {...o1, ...o2, ...o3}; // 结果 {name:'李四',age:18}
[]{}解构赋值
[]
用在声明变量是
let arr = [1,2,3]; // 我们把中括号叫做数组的解构赋值 let [a,b,c] = arr // 结果 a=1,b=2,c=3
用在声明参数时
let arr = [1,2,3];
function test([a,b,c]){
console.log(a,b,c);
}
test(arr); // 结果 a=1,b=2,c=3
{}
用在声明变量时
let obj = {name:"张三",age:18};
// 声明的变量名称要和obj的对象属性一致
let {name,age} = obj;
用在声明参数时
let obj = {name:"张三",age:18};
function test({name,age}){
console.log(name,age);
}
test (obj);
if ... else
switch
while
do ... while
for
for ... in⭐
for ... of⭐
try ... catch⭐
主要用来遍历对象
let father = {name:'张三',age:18,study:function(){}};
for(const n in father){
console.log(n);
}
// 结果 name age study
其中 const n 代表遍历出来的属性名
注意1:方法名也能被遍历出来(它其实也算一种特殊属性)
注意2:遍历子对象时,父对象的属性会跟着遍历出来
let son = object.create(father);
son.sex = "男";
for(const n in son){
console.log(n);
}
// 结果 sex name age study
注意3:在 for in 内获取属性值,要使用 [] 语法,而不能用 . 语法
for(const n in son){
console.log(n,son[n]);
}
// 结果
// sex 男
// name 张三
// age 18
// study f (){}
主要用来遍历数组,也可以时其它可迭代对象,如Map,Set等
let a1 = [1,2,3];
for(const i of a1){
console.log(i);
}
let a2 = [
{name:'张三',age:18},
{name:'李四',age:20},
{name:'王五',age:22}
];
for(const obj of a2){
console.log(obj.name,obj.age);
}
for(const {name,age} of a2){
console.log(name,age);
}
let stu1 = {name:'张三',age:18,address:{city:'北京'}};
let stu2 = {name:'张三',age:18};
function test(stu){
try{
console.log(stu.address.city);
} catch(e){
console.log('出现了异常',e.message);
}
}
Fetch API 可以用来获取远程数据,他有两种方式接受结果,同步方式与异步方式
格式
fetch(url,options) // 返回 Promise对象
同步方式
// const 结果 = await fetch(url,options); const 结果 = await Promise; // 后续代码
await 关键字必须在一个标记了 async 的 function 内来使用
后续代码不会在结果返回前执行
异步方式
fetch(url,options).then(结果 => {...})
// 后续代码
后续代码不必等待结果返回就可以执行