…运算符
作用
将复杂的数据结构,如对象、数组等里面的每一项数据获取(展开)出来。
数组
展开数组
<script>
let iArray = ['1', '2', '3'];
console.log(...iArray);
</script>
结果:
数组添加元素
<script>
let iArray = ['1', '2', '3'];
let iArray1 = ['1', ...iArray];
console.log(...iArray1);
</script>
结果:
拷贝数组
扩展运算符可简洁地对数组进行深拷贝(注意不是对数组元素进行深拷贝),即改变原数组的内容后,拷贝的新数组内容不会随之改变。
先看数组的浅拷贝:拷贝了数组的地址
代码:
<script>
var arr = [1, 2, 3, 4];
var arr1 = arr;
console.log(...arr1); //1 2 3 4
arr.push(5);
console.log(arr); //1 2 3 4 5
console.log(...arr1) //1 2 3 4 5
</script>
结果:
可以看到,对原数组arr进行了操作之后,拷贝的数组arr1也进行了改变。下面演示用扩展运算符进行深拷贝(只改变了数组元素的值)。它将避免这个问题
<script>
var arr = [1, 2, 3, 4];
var arr1 = [...arr];
console.log(...arr1); //1 2 3 4
arr.push(5);
console.log(...arr); //1 2 3 4 5
console.log(...arr1) //1 2 3 4
</script>
结果:
函数
作为形参
可利用扩展运算符进行实参的传递
<script>
var arr=[1,2,3];
function fn(a,b,c,d){
console.log(a,b,c,d);
}
fn(0,...arr); //0 1 2 3
</script>
作为实参
<script>
function fn(a,...rest){
console.log(a); //0
console.log(...rest) //1 2 3
}
fn(0,1,2,3);
</script>
伪数组转换为真数组
伪数组是可遍历但不能调用数组中方法的对象,比如字符串,通过getElementbyClassName等获取的一堆dom对象,通过ajax返回的一些json对象等。
通义千问补充:
Q:伪数组是可遍历但不能调用数组中方法的对象,比如字符串,通过getElementbyClassName等获取的一堆dom对象,通过ajax返回的一些json对象等。
对于上面这句话,能给句示例吗?
当然可以,让我们一步步通过示例来理解这句话:
- 字符串是伪数组:在JavaScript中,字符串看起来和表现得像数组,你可以遍历它们,但它们并不是真正的数组,因此不能直接调用数组的方法。例如:
let str = "Hello, world!";
// 遍历“伪数组”字符串
for(let char of str){
console.log(char);
}
// 尝试使用数组方法,会报错
try {
console.log(str.push("!")); // 错误,因为str不是数组
} catch(e) {
console.error(e.message); // 打印错误信息
}
- 通过
getElementsByClassName
获取的一堆DOM对象:这个方法返回的是一个HTMLCollection,它也是一个伪数组,可以遍历但不能直接使用数组方法。
let elements = document.getElementsByClassName("example");
// 遍历“伪数组”
for(let i = 0; i < elements.length; i++) {
console.log(elements[i].tagName);
}
// 要使用数组方法,通常需要转换为真正的数组
let realArray = Array.from(elements);
realArray.forEach(element => console.log(element.tagName)); // 使用forEach方法
- 通过Ajax返回的JSON对象:这里有一点小误会,从Ajax返回的数据通常是一个JSON字符串,解析后成为普通对象,并不是伪数组。但为了说明概念,如果我们假设有一个对象结构类似数组,也可以讨论其“遍历”性,尽管这不常见也不准确地称为“伪数组”。
// 假设这是Ajax返回并已解析的JSON对象,实际上这不是典型的伪数组情况
let jsonData = {"items": ["item1", "item2", "item3"]};
// “遍历”jsonData中的items
for(let item of jsonData.items) {
console.log(item);
}
// jsonData.items本身是个真数组,可以直接使用数组方法
jsonData.items.map(item => console.log(item.toUpperCase()));
总结来说,伪数组指的是具有length属性且可遍历,但不具备或不完全具备Array.prototype上的方法的对象。在处理这些对象时,如果需要使用数组方法,通常需要先将其转换为真正的数组,如使用Array.from()
、扩展运算符...
或者slice()
方法。
下面将用字符串简单地进行演示。
结果:
解构
与解构结合使用
扩展运算符可以结合解构赋值使用,方便快捷,形式上类似数组的深拷贝
<script>
var arr = [1, 2, 3];
var [x, y, z] = [...arr]
console.log(x, y, z) //1 2 3
</script>
结果:
对象
对象深拷贝
类似遍历数组(要写在花括号内)
<script>
var obj = {
name: '张三',
age: 18,
}
console.log({ ...obj }) //{name: '张三', age: 18}
console.log(obj) //{name: '张三', age: 18}
</script>
结果:
在JavaScript中,使用扩展运算符(...
)解构对象确实会创建一个新的对象,这个新对象的内容与原对象相同,因此在表面上看起来打印结果是一样的。但是,这样做有几个潜在的目的和好处:
-
深拷贝浅拷贝:直接使用
{...obj}
实际上是执行了一个浅拷贝,创建了原对象的一个副本。这意味着如果你修改新对象的属性,不会影响到原对象。这对于需要保留原始数据不变,同时又想基于原始数据进行操作的场景非常有用。 -
传递属性:在对象字面量中使用扩展运算符可以很容易地将一个对象的属性合并到另一个对象中,用于快速组合对象属性,而不需要显式地一一列举。
-
避免引用问题:如果不使用扩展运算符而直接传递或操作原对象,可能会无意间修改原对象。尤其是在函数参数传递、循环体中或者与其他代码共享数据时,解构出一个新的对象可以避免这种意料之外的副作用。
举例说明差异:
var originalObj = { name: '张三', age: 18 };
var copiedObj = { ...originalObj };
originalObj.age = 20;
console.log(originalObj); // {name: '张三', age: 20}
console.log(copiedObj); // {name: '张三', age: 18}
在这个例子中,即使修改了originalObj
的age
属性,copiedObj
的age
属性保持不变,证明了它们是两个独立的对象。
总结来说,虽然在简单的打印示例中{...obj}
和obj
看起来效果一样,但在实际编程中,解构创建新对象可以避免状态共享引起的问题,提供更多的灵活性和安全性。
对象合并
类似第2点,用扩展运算符也可以当作对象中的元素,即对象的合并。若合并的数组中有与原数组相同的key,则按书写顺序进行value的覆盖。后写覆盖先写的
<script>
var a = {
a: 1,
c: 3
}
var b = {
...a,
b: 2,
c: 1,
}
console.log(b) //{a: 1, c: 1, b: 2}
</script>
结果:
解构运算符
作用
解构赋值是对赋值运算符的扩展。
它是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
原理
在解构中,有下面两部分参与:
解构的源,解构赋值表达式的右边部分;
解构目标,解构赋值表达式的左边部分;
在ES5中,为变量赋值只能直接指定变量的值:
<script>
let a = 1;
let b = 2;
</script>
在ES6中,变量赋值允许写成:
<script>
// 解构目标 = 解构的源
let [a,b,c] = [1,2,3];
console.log(a); // 输出1
console.log(b); // 输出2
console.log(c); // 输出3
</script>
数组 - 解构赋值
数组解构赋值,变量可以随意命名,但是下标与数组中下标位置对应。就是指模式要匹配。等号两边的模式相同。
基本用法
<script>
let [a,b,c]=[1,2,3]
console.log(a,b,c)
</script>
可嵌套
<script>
let [a,[b,c]]=[1,[2,3]]
console.log(a,b,c)
</script>
可忽略
<script>
// 可忽略
// 数组中的内容 在解构赋值 提取的过程中,不考虑提取他,就把他忽略掉
let[a,,b]=[1,2,3]
console.log(a,b)
</script>
解构默认值
解构赋值允许指定默认值。
当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。
<script>
let [f = true] = []
console.log(f)
let [a = 1] = [undefined];
console.log(a)
</script>
注意,ES6 内部使用严格相等运算符(===)又叫全等,判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
字符串解构
在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据。
<script>
// 字符串的解构赋值
let [a,b,c]='hellojs';
console.log(a,b,c);
</script>
对象解构
与数组一样,解构也可以用于嵌套结构的对象。对象嵌套解构赋值模式要匹配。等号两边的模式相同。
<script>
// 嵌套对象的解构赋值
let obj={
p:[
'hello',
{
y:'world'
}
]
}
let{p:[x,{y}] } = obj;
console.log(x)
console.log(y)
</script>
通义千问
Q:解构中的p:是什么来的?
在这个例子中,p:
在解构赋值语法中起到了别名和深层解构的作用?
-
别名 (Aliasing): 在解构赋值中,
p:
实际上是为了解构对象的p
属性。如果没有冒号,那么你会直接解构出p
属性的值。但是,当使用了冒号,如p:
,这意味着你为解构出来的值指定一个新的变量名。在这个例子中,你实际上并不关心这个新变量名(因为它没有直接被赋给任何变量),你更感兴趣的是立即进一步解构p
属性指向的数组。 -
深层解构 (Deep destructuring): 通过
p: [x, { y }]
,你不仅解构了obj
对象的p
属性,还进一步解构了p
属性指向的数组。这里[x, { y }]
表示你想要从数组中解构出第一个元素赋给变量x
,以及第二个元素(一个对象)的y
属性赋给变量y
。
总结来说,p:
在这里是一个解构语法的一部分,它帮助你间接地访问并进一步解构嵌套的结构——首先通过p:
访问到obj.p
,然后立即对得到的数组进行进一步解构。这种机制使得处理嵌套数据结构变得非常直观和简洁。
js的数据类型
基本数据类型
- String:字符串类型,用于表示文本。
- Number:数值类型,包括整数和浮点数。
- Boolean:布尔类型,只有两个值:true 和 false。
- null:表示一个特意设置的空值,只有一个值null,表示没有对象。
- undefined:表示一个变量已被声明但尚未被赋值,只有一个值undefined。
- symbol:符号类型,ES6引入的新类型,表示独一无二的值,常用于对象的唯一属性键。
- bigint:BigInt类型,也是ES10(ECMAScript 2020)引入的,用于表示大于2^53 - 1(即Number.MAX_SAFE_INTEGER)的整数。
引用数据类型
function、Array、Date等对象
数据类型的隐式转换
<script>
// 加 +
console.log(10 + 100); // 110
console.log(10 + 'string'); // 10string 当加号有一边是字符串,另一边也转成字符串,变成字符串的拼接
console.log(19 + 10 + 'age' + 18 + 10) //29age1810
console.log(10 + '100'); //10100
console.log(10 + true); // 11 boolean 类型在计算是转成 number
console.log(true + false); // 1 1 + 0 = 1
console.log('10' + true); // 10true
console.log('' + 100); // '100'
console.log(10 + null); // 10 null在计算是转成 number 0
console.log(10 + undefined); // NaN,undefined调Number方法转成NaN
// 减 -
console.log(100 - 10); // 90
console.log(100 - 't'); // NaN
console.log(100 - ''); // 100 空字符串转成number 0
console.log(100 - true); // 99 boolean 类型在计算是转成 number 1
console.log(100 - '80'); // 20 '80'转成 number 80
console.log(100 - null); // 100
console.log(100 - undefined); // NaN
// 乘 *
console.log(100 * 'a'); // NaN
console.log(100 * ''); // 0 ''转成 number 0
console.log(100 * '100');// 10000
console.log(100 * null); // 0
console.log(100 * undefined);// NaN
// 除 /
console.log(100 / 'a'); // NaN
console.log(100 / ''); // Infinity
console.log(100 / '70'); // 10/7
console.log(100 * null); // 0
console.log(100 * undefined); // NaN
// 取余 %
console.log(100 % 'a'); // NaN
console.log(100 % ''); // NaN,''转成0,100%0为 NaN
console.log(100 % '70'); // 30
console.log(100 % null); // NaN
console.log(100 % undefined); // NaN
// ++
var n = '10';
n++;
console.log(n); // 11 n 转成 number
// 取反
console.log(!true); // false
console.log(!10); // false
console.log(!'web'); // false
</script>
js和java基本运算符的区别
比较运算符
JS中的比较运算符,有下面两个与Java不同
意思为:需要判断数据类型和值是否相同
- ===
- !==
函数
创建函数的不同
// 声明函数
function 函数名() {
//函数体代码
}
匿名函数的不同
// 这是函数表达式写法,匿名函数后面跟分号结束
var fn = function(){...};
// 调用的方式,函数调用必须写到函数体下面
fn();
反引号
定义
模板字符串是 JavaScript ES6 引入的一个新特性,使用反引号 `(反引号)来创建,也称为模板字面量。模板字面量是一种可以包含嵌入式表达式的字符串字面量。下面将详细介绍模板字符串的各个方面。
创建模板字符串
使用反引号 ` 来创建模板字符串。可以包含任意字符,包括转义字符和多行字符串。
var greeting = `Hello, ${name}!`;
console.log(greeting);
模板字符串中的变量
使用 ${变量名} 的形式在模板字符串中插入变量。变量可以是任何有效的 JavaScript 表达式
var name = 'John';
vbar greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, John!
模板字符串中的表达式
模板字符串可以包含任意有效的 JavaScript 表达式,它们会被求值并插入到结果字符串中。
var a = 5;
var b = 10;
var sum = `The sum of ${a} and ${b} is ${a + b}.`;
console.log(sum); // The sum of 5 and 10 is 15.