数据类型
在编程语言中,一般固定值称之为字面量
值类型(基本类型):字符串(
String
)、数字(Number
)、布尔(Boolean
)、空(Null
)、未定义(Undefined
)、独一无二的值(Symbol
)引用数据类型(对象类型):对象(
Object
)、数组(Array
)、函数(Function
),还有两个特殊的对象:正则(RegExp
)和日期(Date
)
JavaScript
变量能够保存多种数据类型:数值、字符串值、数组、对象等等:
未定义变量和未赋值变量的数据类型为 undefined
var length = 7; // 数字
var y = 123e5; // 12300000 超大或超小的数值可以用科学计数法来写
var lastName = "Gates"; // 字符串
var cars = ["Porsche", "Volvo", "BMW"]; // 数组
var x = {firstName:"Bill", lastName:"Gates"}; // 对象,对象属性是 name:value对
在JavaScript
中,null
表示 "nothing"
,它被看做不存在的事物,在 JavaScript
中,null
的数据类型是对象,可以通过设置值为 null
清空对象
Undefined
与Null
的区别:其值相等,但是类型不相等
typeof undefined // undefined
typeof null // object
null === undefined // false
null == undefined // true
数值
数字(Number
)字面量可以是整数或者是小数,或者是科学计数(e
)
JavaScript
只有一种数值类型,书写数值时带不带小数点均可
JavaScript
数值始终是 64 位的浮点数,整数(不使用指数或科学计数法)会被精确到 15 位
除了加法运算,在其他所有数字运算中,JavaScript
会先将字符串转换为数字,再进行运算:
var x = "100";
var y = 10;
var z = x / y; // z 将是 10
在加法运算中,
JavaScript
用 + 运算符对字符串进行了级联
NaN
属于JavaScript
保留词,表示某个数不是合法数,使用一个非数字字符串进行被除会得到 NaN
,其类型为Number
,在数学运算中使用了 NaN
进行任何除加法的运算,则结果也将是 NaN
Infinity
(或 -Infinity
)是 JavaScript
在计算数时超出最大可能数范围时返回的值,除以 0(零)也会生成 Infinity
,其类型为Number
BigInt
JavaScript
中BigInt
变量用于存储太大而无法用普通 JavaScript
数字表示的大整数值,普通的JavaScript
整数最多只能精确到 15 位,超过15位的整数值可以考虑使用BigInt
数据类型
如果需要创建 BigInt
,可以在整数末尾添加 n
,或调用 BigInt()
函数
let y = 9999999999999999999n;
let y = BigInt(1234567890123456789012345)
BigInt
的JavaScript
类型是 "bigint
"BigInt
是JavaScript
中的第二个数值数据类型(在Number
之后)可用于
Number
的运算符也可用于BigInt
,但是不允许数据类型为BigInt
和Number
的数值之间进行算术运算(类型转换会丢失信息)BigInt
数字类型的数值无法进行无符号右移操作(>>>),因为它没有固定的宽度BigInt
类型的数值不能有小数BigInt
也可以写成十六进制、八进制或二进制,构造方法和整数的构造方法差不多
将BigInt
类型的数转化为Number
类型的数的方法为:Number(x)
, 其中x
的类型为BigInt
类型
数字方法
方法 | 描述 |
---|---|
isInteger() | 判断是否为整数,返回的是bool 类型的数据 |
isSafeInteger() | 判断参数是否为安全整数,安全整数的范围是-9007199254740991到9007199254740991 |
toString() | 将数字作为字符串返回,调用方式:num.toString() |
toExponential() | 返回以指数表示法书写的数字,调用方式:num.toExponential(2) ;其中括号中的参数定义小数点后的字符数,如果指定的位数小于原先的位数,就会进行一个四舍五入操作 |
toFixed() | 返回一个字符串,其中的数字带有指定位数的小数部分,原理和toExponential() 差不多 |
toPrecision() | 返回一个字符串,其中包含指定长度的数字,不输入参数返回与原数据一致的字符串;长度是指不算小数点的数字长度,会进行四舍五入 |
转化为数字类型
方法 | 描述 |
---|---|
Number() | 返回从其参数转换而来的数字,原始的字符串允许有空格,如果是字符串小数,则返回该小数;不支持多个转化多个数据,会返回NaN ;如果无法转换数字,则返回 NaN |
parseFloat() | 解析其参数并返回浮点数,原始的字符串允许有空格,但是仅返回第一个数字;如果无法转换数字,则返回 NaN |
parseInt() | 解析其参数并返回整数,原始的字符串允许有空格,但是仅返回第一个数字,数字必须在该字符串的前面,才能取到;如果是小数,则返回的数直接去掉小数部分,如果无法转换数字,则返回 NaN |
以上的方法不是数字方法,它们是全局 JavaScript
方法
Number(true) // 返回1
Number(false) // 返回0
parseInt("10 years") // 返回10
parseInt("10.33") // 返回10
parseInt("years 10") // 返回NaN
数字属性
数字属性只能作为 Number.MAX_VALUE
来访问,使用其他访问将返回undefined
属性 | 描述 |
---|---|
EPSILON | 1 和大于 1 的最小数之间的差 |
MAX_VALUE | JavaScript 中可能的最大数 |
MIN_VALUE | JavaScript 中可能的最小数 |
MAX_SAFE_INTEGER | 最大安全整数 (2^53 - 1 ) |
MIN_SAFE_INTEGER | 最小安全整数 -(2^53 - 1) |
POSITIVE_INFINITY | 无穷大(溢出时返回) |
NEGATIVE_INFINITY | 负无穷大(溢出时返回) |
NaN | “非数字”值 |
将一个不能转化成数值的类型数据通过
Number()
转换为数值,是不能转换的,结果就返回NaN
,如:console.log(Number("jjj"))
其次,我们尝试用一个数值来和一个字符串进行运算的时候,得到的结果也是
NaN
,如console.log(2 / "jjj")
NaN
类型是不能和NaN
类型进行比较的,如:console.log(NaN == NaN) // 结果显示false
常用的数学方法
方法 | 描述 |
---|---|
Math.max(1,3,2,4,5,6,7) | 取得最大值 |
Math.min(1,3,2,4,5,6,7) | 取得最小值 |
Math.ceil(5.1) | 向上取整 |
Math.floor(5.2) | 向下取整 |
Math.round(5.56, 1) | 小数点后保留几位 |
Math.random() | 获取随机数,从0-1之间(包含0,不包含1) |
从数组中取最大值:
let grade = [90,100,120,110];
console.log(Math.max.apply(null, grade)); // apply表示调用函数,用数组的形式传参
获取0-6之间的随机整数:
console.log(Math.floor(Math.random() * 6))
随机点名案例,可以控制点名范围:
const students = ["张三", "李四", "王五", "赵六"];
function arrayRandomValue(array, start = 1, end){
end = end ? end : array.length;
start--;
const index = start + Math.floor(Math.random() * (end - start));
return array[index];
}
console.log(arrayRandomValue(students, 1, 3));
日期
日期通常有两种表现形式:标准时间(当前计算机时间的年月日和时分秒)和时间戳(从1970年0时0分0秒到此刻的所经过的毫秒数)
JS
中创建日期通常有两种方式:
// 方式一,通过构造函数进行创建
const date = new Date();
console.log(date); // 返回当前的时间
console.log(typeof date); // 返回object
console.log(date * 1); // 返回时间戳
// 方式二
const date = Date();
console.log(date); // 返回当前的时间
console.log(typeof date); // 返回string
console.log(date * 1); // 返回NaN,因为字符串乘以数字返回NaN
直接获取时间戳:
const date = Date.now();
console.log(date); // 获取时间戳
标准时间和时间戳的类型转换
由标准时间转换为时间戳
const date = new Date("2024-5-24 19:31:20");
// 方式一
console.log(date * 1);
// 方式二
console.log(Number(date));
// 方式三
console.log(date.valueOf());
// 方式四
console.log(date.getTime());
由时间戳转换为标准时间
const date = new Date("2024-5-24 19:31:20");
const timestamp = date.valueOf();
// 时间戳转换为标准时间
console.log(new Date(timestamp));
日期类型的综合使用
const date = new Date("2024-5-24 19:31:20");
console.log(date.getFullYear()); // 获取日期的年份
console.log(date.getMonth() + 1); // 获取日期的月份
console.log(date.getDate()); // 获取日期的日
console.log(date.getHours()); // 获取日期的小时
console.log(date.getMinutes()); // 获取日期的分钟
console.log(date.getSeconds()); // 获取日期的秒数
如果需要频繁的使用,一般封装成函数:
// 将标准时间转化为任何输入想要的形式
function dateFormat(date, format = "YYYY-MM-DD HH:mm:ss"){
const config = {
YYYY: date.getFullYear(),
MM: date.getMonth() + 1,
DD: date.getDate(),
HH: date.getHours(),
mm: date.getMinutes(),
ss: date.getSeconds()
};
for(const key in config){
format = format.replace(key, config[key])
}
return format;
}
// 测试
const date = new Date("2024-5-24 19:31:20");
console.log(dateFormat(date, "YYYY年MM月DD日")) // 结果显示为:2024年5月24日
专门用处理日期的第三方库:Moment.js
console.log(moment().format("YYYY-MM-DD HH:mm:ss"));
根据设置的格式打印当前时间
数组
数组(Array
)字面量定义一个数组
JavaScript
数组用于在单一变量中存储多个值
创建数组的语法:var array = [item1, item2, ...];
创建数组的声明可跨多行,通常在逗号的位置进行换行
可以引用数组名来访问完整的数组
在创建数组的时候,需要额外注意:
let arr = new Array(6); // 这样创建的数组长度是6,每一个的值都是undefined,这是这种方式创造的缺陷
如果想要创造一个固定长度的数组,可以通过以下的方法:
// 方法一
let arr = [6];
// 方法二
let arr = Array.of(6);
数组是一种特殊类型的对象,在
JavaScript
中对数组使用typeof
类型判断会返回object
JavaScript
不支持命名索引的数组,数组只能使用数字去索引通过数字索引修改内容,会导致原数组中的内容进行修改,因为两者使用同一个内存地址
如果希望元素名为字符串(文本)则应该使用对象,如果希望元素名为数字则应该使用数组,对象和数组的内容可以是数字或者字符串
通过控制台查看数组,可以通过两种方式进行查看:
const array = [1, 2, 3];
console.log(array); // 常规形式展现
console.table(array); // 以表格的形式展现,更加清晰直观
const
和var
声明数组
通过const
声明数组:const cars = ["Saab", "Volvo", "BMW"];
用 const
声明的数组不能重新赋值,但是可以通过索引进行元素的更改,因为修改的数组和原数组是共用内存地址的
const arr = [1, 2];
arr[1] = 3;
console.log(arr); // 结果显示为[1, 3]
对于数组,如果修改下标内容的范围超出了原数组的最大下标,这样添加位置也能添加内容,只是原数组之间的内容用undefined
进行填充:
let name = ["jlc"];
name[3] = "zhangshna";
consloe.log(name.length); // 结果显示4
console.log(name[2]); // 结果显示undefined
一般情况下,都不会这样进行内容的追加,会通过其他数组方法进行追加
const
变量在声明时必须赋值,声明后赋值是不起作用的,但是用 var
声明的数组可以随时初始化
用 const
声明的数组具有块作用域,用 var
声明的数组没有块作用域
在程序中的任何位置都允许用 var
重新声明数组;不允许在同一作用域或同一块中将数组重新声明或重新赋值给 const
;不允许在同一作用域或同一块中重新声明或重新赋值现有的 const
数组;允许在另一个作用域或另一个块中使用 const
重新声明数组
数组的比对:数组的比对不仅仅是比对其值,还要比对其内存地址(对象的比对也同理)
let a = [];
let b = [];
console.log(a == b); // 结果显示false 数组a和b的内存地址不一样
// 如果将a赋值给b
let a = [];
let b = a;
console.log(a == b); // 结果显示true a和b共用同一个内存地址
数组的嵌套
可以在数组里面嵌套数组,在引用的时候需要两个方括号
let array = [["qqq"], ["www", "eee"]];
console.log(array[1][0]) // 结果显示www
一般以后都是通过对象进行嵌套
let lessons = [{name: "jlc", age: 24}, {name: "jlc1", age: 23}]
console.log(lessons[1].name) // 结果显示jlc1
数组的类型检测和转换
类型检测
通过isArray()
判断括号中的内容是否为数组
类型转换
数组转换为字符串
// 方法一
[1, 2, 3].toString()
// 方法二
String([1, 2, 3])
// 方法三
[1, 2, 3].join(",")
// 结果都显示'1,2,3'
将字符串转换为数组
let str = "baidu";
// 方法一
str.split("") // 结果显示:["b","a","i","d","u"]
// 方法二,使用from静态方法
Array.from(str); // 结果显示:["b","a","i","d","u"]
数组的追加
通过push方法
单个元素追加到数组中
let arr = ["1", "2"];
// 方法一:通过下标索引
arr[arr.length] = "3";
// 方法二:通过对象方法push
arr.push("3");
// 结果显示["1", "2", "3"]
一个数组追加到另外一个数组中
let arr1 = ["1", "2"];
let arr2 = ["3", "4"];
for(const value of arr2){
arr1.push(value);
}
// 结果显示["1", "2", "3", "4"]
通过点语法
let arr1 = ["1", "2"];
let arr2 = ["3", "4"];
// 方法一
arr1 = [...arr1, ...arr2];
// 方法二:通过push方法
arr1.push(...arr2);
如果想要将DOM
元素按照数组方式操作,可以通过点语法将其转换为数组
解构语法
对象和数组中使用解构语法是较为常见的
数组的解构语法:将右侧数组里面的值,平均赋值给左侧的变量;如果右边的数值元素比左边的多,可以使用点语法,将余下的数组元素都以数组的形式接受过来
let arr = ["jlc", "24"];
// 传统形式的方法:通过的将数组的值赋值给变量的方法
let name = arr[0];
let age = arr[1];
// 通过解构语法
let [name, year] = arr;
let [, year] = arr; // 只希望取到第二个数组元素
// 如果后面数组元素少,其赋值的变量显示undefined
let [name, age, year] = arr; // year打印的结果为undefined
// 也可以给year输入默认值
let [name, age, year = 2000] = arr;
// 解构和点语法的整合使用
let [name, ...args] = arr; // 数组的第一个元素给了name变量,后面的元素以数组的形式全部给args
点语法放到变量的位置,就是吸收;放到值的位置,就是打散
解构语法也可以用在字符串中:字符串转换为数组
const [...arr] = "jlc";
console.log(arr); // 结果显示为:["j","l","c"]
解构语法在函数传值中的使用:
function show([name, year]) {
console.log(name, year);
}
show(["jlc", 24])
数组的属性和方法
属性/方法 | 描述 |
---|---|
length | 属性返回数组的长度(数组元素的数目) |
push() | 向数组添加新元素,添加到元素数组的最后,同时该方法返回新数组的长度,如:var x = fruits.push("a"); x 的值是新数组的长度 |
unshift() | 在开头向数组添加新元素,并“反向位移”旧元素,旧元素的索引变大,该方法返回新数组的长度(与push 方法的返回值是一样的) |
fill() | 数组元素的填充,可以全部填充一样的内容,也可以指定位置进行填充 |
join() | 将所有数组元素结合为一个字符串,其中的参数表示连接符号,如:fruits.join(" * ") ,数组元素以* 符号连接成一个字符串 |
pop() | 从数组中删除最后一个元素,可以返回被弹出的值,var x = fruits.pop(); x 的值就是被弹出的值 |
shift() | 删除首个数组元素,并把所有其他元素“位移”到更低的索引,该方法返回被“位移出”的字符串 |
concat() | 合并(连接)现有数组来创建一个新数组,如var myChildren = myGirls.concat(myBoys); 表示连接数组myGirls 和myBoys ,将myBoys 数组连接到myGirls 数组后面,该方法不会更改现有数组,它总是返回一个新数组,该方法可以使用任意数量的数组参数:arr1.concat(arr2, arr3); 方法括号中可以有多个数组,进行依次的追加 |
slice() | 数组的某个片段切出新数组,可接受两个参数,比如 (start, end) ,从开始参数作为元素的索引选取元素,直到结束参数(不包括)为止,返回的是切出来的新数组,第二个参数可以省略,表示裁到最后,该方法不会对原数组进行改变,一般是将截取到的内容赋值给新的数组 |
splice() | 该方法法删除/截取功能:截取数组元素,会对原数组进行改变(直接从原数组中拿取截取片段),(start, length) ,第一个参数表示从哪个位置开始,第二个参数表示截取几个元素 |
splice() 方法还有替换的功能:向数组中移除元素后,添加新项,如:fruits.splice(2, 0, "Lemon", "Kiwi"); 第一个参数(2)定义了应添加新元素的位置(其参数就是新元素的索引值);第二个参数(0)定义应删除后面多少元素;其余参数定义要添加的新元素 | |
copyWithin() | 复制数组元素,arr.copyWithin(2, 0, 2) ,第一个参数表示要复制到哪个下标位置;第二个参数表示从哪个下标位置开始复制元素,第三个参数表示复制元素到哪个下标位置结束(这个下标的元素不包括) |
indexof() | 从左侧开始查找,查找数组中的元素(查找是一个严格类型匹配,值和类型都要一样),返回查找到相同元素的下标,找不到,则返回-1,该方法有两个参数,第一个参数表示要查找的内容,第二个参数表示从哪个下标位置开始查找 |
lastIndexof() | 从右侧开始查找,查找数组中的元素(查找是一个严格类型匹配,值和类型都要一样),返回距离右侧近的元素下标,该方法有两个参数,第一个参数表示要查找的内容,第二个参数表示从哪个下标位置开始查找 |
includes() | 查找数组中是否包含某个元素,找到则返回true ,找不到则返回false |
find() | 查找相关的元素,并将其元素值返回 |
findIndex() | 查找相关的元素,并将其元素的索引值返回 |
toString() | 自动把数组转换为字符串,与直接展示数组有相同的结果,所有 JavaScript 对象都拥有 toString() 方法 |
sort() | 以开头字母顺序对数组进行排序 |
reverse() | 反转数组中元素的顺序 |
使用案例:
通过索引数组重新赋值进行更改元素:
jsfruits[0] = "Kiwi"; // 把 fruits数组的第一个元素改为 "Kiwi"
通过
push()
和unshift()
可以获取改变后数组的长度:jsvar = fruits = [1,2,3,4]; var x = fruits.push('a'); console.log(x); // 5 console.log(fruits); // [1, 2, 3, 4, 'a']
对于数组元素填充的方法
full()
:js// 在空数组中进行元素的填充,将给定长度的空数组,添加相同给定的元素 consloe.log(Array(3).fill("jlc"));//结果显示["jlc","jlc","jlc"] // 也可以在特定的位置进行数组的填充 consloe.log([1,2,3,4,5].fill("jlc", 1, 3)); // 结果显示[1, "jlc", "jlc", 4, 5]
join()
方法的使用:jsvar = fruits = [1,2,3,4,'a']; console.log(fruits.join("*")); // '1*2*3*4*a'
splice()
方法进行数组元素的删除:jsfruits.splice(0, 1); // 删除fruits数组中的第一个元素 // fruits就是切除原先第一个元素的数组
splice()
方法进行数组元素的替换:jsvar fruits = [1,2,3,4]; // 在索引为2的位置移除0个元素,在添加"Lemon", "Kiwi"两个内容 fruits.splice(2, 0, "Lemon", "Kiwi"); console.log(fruits); // [1, 2, 'Lemon', 'Kiwi', 3, 4]
copyWithin()
复制函数方法的使用:jsvar fruits = [1,2,3,4]; fruits.copyWithin(2,0,2); console.log(fruits); // [1, 2, 1, 2]
indexOf()
方法在数组中搜索元素值并返回其位置(索引值),如果未找到项目,Array.indexOf()
返回 -1,如果项目多次出现,则返回第一次出现的位置基本语法:
array.indexOf(item, start)
item
表示要检索的项目(内容);start
表示从哪里开始搜索,负值将从结尾开始的给定位置开始,并搜索到结尾js// 检索数组中的项目 "Apple" var fruits = ["Apple", "Orange", "Apple", "Mango"]; var a = fruits.indexOf("Apple"); console.log(a); // 0
Array.lastIndexOf()
与Array.indexOf()
类似,但是从数组结尾开始搜索查找元素
find()
方法的使用:find()
方法返回通过自定义测试函数的第一个数组元素的值该函数最多接受 3 个参数:项目值,项目索引,数组本身
(value, index, array)
js// 查找(返回)大于 18 的第一个元素的值 var numbers = [4, 9, 16, 25, 29]; function myFunction(value, index, array) { return value > 18; } var first = numbers.find(myFunction); console.log(first); // 25
findIndex()
方法返回通过自定义测试函数的第一个数组元素的索引js//查找大于 18 的第一个元素的索引 var numbers = [4, 9, 16, 25, 29]; function myFunction(value, index, array) { return value > 18; } var first = numbers.findIndex(myFunction); console.log(first); // 3
拓展案例:
jslet lessons = [{name: "js"}, {name: "css"}]; // 查找并返回值 let status = lessons.find(function(item){ return item.name == "js"; }) console.log(status); // 结果显示 {name: "js"} // 查找并返回其索引位置 let index = lessons.findIndex(function(item){ return item.name == "js"; }) console.log(index); // 结果显示 0
find
方法的底层实现:jsfunction find(array, callback){ for(const value of array){ if(callback(value)){ return value } } return undefined; } // find方法的使用 let arr = [2, 3, 4]; console.log(find(arr, function(item){ return item == 2; })); // 结果显示为2
通过一个比值函数对数字数组进行排序:
jsvar points = [40, 100, 1, 5, 25, 10]; points.sort(function(a, b){return a - b}); // 返回的数组顺序由小到大进行排序 points.sort(function(a, b){return b - a}); // 返回的数组顺序由大到小进行排序 points.sort(function(a, b){return 0.5 - Math.random()}); //返回的数组随机进行排序
小案例:购物车内价格排序
jslet cart = [ {name: "iphone", price: 12000}, {name: "imac", price: 18000}, {name: "ipad", price: 2000}, ]; cart = cart.sort(function(a, b){ return b.price - a.price; });
返回数组中的最大最小值:
// 返回数组最大值:
function myArrayMax(arr) {
return Math.max.apply(null, arr);
}
// 返回数组最小值:
function myArrayMin(arr) {
return Math.min.apply(null, arr);
}
设计一个移动数组的函数:
function move(array, from, to){
if(from < 0 || to >= array.length || to <= 0){
console.error("参数错误");
}
const newArray.splice(from, 1);
newArray.splice(to, 0, ...item);
return newArray;
}
let array = [1, 2, 3, 4];
console.log(move(array, 1, 3)) // 把索引为1的元素移动到索引为4的位置
// 结果显示为:[1, 3, 4, 2]
清空数组的常见方法:
let arr = [1, 2, 3];
// 方法一
arr = []; // 重新开辟了一块内存空间,存放空数组,原先的内存空间还是存在的
// 方法二
arr.length = 0; // 将原先的内存数据中的数值元素进行一个清空,清除数组更加彻底
// 方法三
arr.splice(0); // 将原数组的元素全部拿走,原数组就清除了
// 方法四
while(arr.pop()){} // 循环一次数组中所有元素的弹出
数组循环
数组的循环有常见的for
循环,for of
循环和for in
循环
// 将数组字典内的name前面批量添加内容
let lessons = [
{ name: "jlc", age: 24},
{ name: "xiaoming", age: 23}
];
// 方式一:通过普通的for循环
for(let i = 0; i < lessons.length; i++){
lessons[i].name = `姓名${lessons[i].name}`;
}
// 方式二:通过for of循环
for(const value of lessons){
value.name = `姓名${value.name}`;
}
// 方式三:通过for in循环
for(const key in lessons){
lessons[key].name = `姓名${lessons[key].name}`;
}
数组迭代
数组迭代方法是对每个数组项(元素)进行操作
forEach()
方法为每个数组元素调用一次函数(回调函数)
该函数最多接受 3 个参数:项目值,项目索引,数组本身(value, index, array)
// 将每个元素竖直排序
var txt = "";
var numbers = [45, 4, 9, 16, 25];
numbers.forEach(myFunction);
document.getElementById("demo").innerHTML = txt;
function myFunction(value) {
txt = txt + value + "<br>";
}
iterator
迭代器
let arr = ["jlc", "24"];
let keys = arr.keys(); // keys表示获取其索引值
console.log(keys.next); // {value: 0, done: false}其中value表示索引,done表示是否迭代完成
console.log(keys.next); // {value: 1, done: false}
console.log(keys.next); // {value: undefined, done: true}
let values = arr.value(); // values表示获取值
let {value, done} = values.next(); // 进行了一次迭代
console.log(value, done); // 结果显示 jlc, false
// 迭代方法遍历数组中的元素,和for of循环方法类似
let arr = ["jlc", "24"];
let values = arr.value();
while(({value, done} = values.next()) && done === false){
console.log(value);
}
// 结果显示:
jlc
24
高效处理数组的方法
every()
every()
方法检查所有数组值是否通过测试,对一个数据进行统一判断,当全部为真,表达式才为真
该函数最多接受 3 个参数:项目值,项目索引,数组本身(value, index, array)
let arr = ["jlc", "24"];
arr.every(function(value, index, arr){
console.log(value); // jlc 回车 24
console.log(index); // 0 回车 1
console.log(arr); // ["jlc", "24"] 回车 ["jlc", "24"]
return true; // 如果写的是false,就只会打印第一组(一次)的对应的数据
});
// 检查所有学生的成绩是否都及格
const user = [
{ name: "张三", chengjji: 88 },
{ name: "李四", chengjji: 92 },
{ name: "王五", chengjji: 58 },
];
const res = user.every(function(item){
retuen item.chengji >= 60;
});
console.log(res ? "全部及格" : "有同学没有及格");
// 检查所有数组值是否大于 18,返回的是一个bool类型的数据,数组中的全部元素通过测试返回true
var numbers = [45, 4, 9, 16, 25];
function myFunction(value) {
return value > 18;
}
var allOver18 = numbers.every(myFunction);
console.log(allOver18); // false
some()
some()
方法检查是否有数组值通过了测试,如果有任何为真,该表达式就为真
该函数最多接受 3 个参数:项目值,项目索引,数组本身(value, index, array)
let arr = ["jlc", "24"];
arr.some(function(value, index, arr){
console.log(value); // jlc
console.log(index); // 0
console.log(arr); // ["jlc", "24"]
return true; // 如果写的是false,就会打印全部的对应数据,与every相反
});
// 检查是否有数组值是否大于 18,返回的是一个bool类型的数据,数组中的存在元素通过测试返回true
var numbers = [45, 4, 9, 16, 25];
function myFunction(value) {
return value > 18;
}
var someOver18 = numbers.some(myFunction);
console.log(someOver18); // true
filter()
filter()
方法创建一个包含通过测试的数组元素的新数组(过滤数组,符合条件的拿出来,在数组的操作中使用的比较多)
该函数最多接受 3 个参数:项目值,项目索引,数组本身(value, index, array)
let arr = ["jlc", "24"];
arr.filter(function(value, index, arr){
console.log(value); // jlc 回车 24
console.log(index); // 0 回车 1
console.log(arr); // ["jlc", "24"] 回车 ["jlc", "24"]
return true; // true表示每一次循环的时候,这个元素符合条件,提取这个元素;如果写的是false,舍弃元素
});
let newArray = arr.filter(function(value, index, arr){
return true;
});
console.log(newArray); // 结果显示["jlc", "24"]
// 将成绩为88的对象从数组中提取出来
const user = [
{ name: "张三", chengji: 88 },
{ name: "李四", chengji: 92 },
{ name: "王五", chengji: 88 },
];
const userFilter = user.filter(function(user){
return user["chengji"] == 88;
});
console.log(userFilter);
// 结果显示:[{ name: "张三", chengjji: 88 },{ name: "王五", chengjji: 88 }]
// 将原始数组中值大于18的元素创建一个新数组
var numbers = [45, 4, 9, 16, 25];
function myFunction(value) {
return value > 18;
}
var over18 = numbers.filter(myFunction);
map()
map()
方法(数组映射,复印一份数组,当然在复印的过程中可以进行二次处理),通过对每个数组元素执行函数来创建新数组。该方法不会对没有值的数组元素执行函数,也不会改变原始数组
该函数最多接受 3 个参数:项目值,项目索引,数组本身(value, index, array)
let arr = ["jlc", "24"];
arr.every(function(value, index, arr){
console.log(value); // jlc 回车 24
console.log(index); // 0 回车 1
console.log(arr); // ["jlc", "24"] 回车 ["jlc", "24"]
value = `这是前缀-${value}`;
return value;
});
// 将每个元素都乘以2
var numbers1 = [45, 4, 9, 16, 25];
function myFunction(value) {
return value * 2;
}
var numbers2 = numbers1.map(myFunction); // 对新数组进行映射改变,原数组是没有改变的
// 数组对象中增加一个click,其值为100
const user = [
{ name: "张三", chengji: 88 },
{ name: "李四", chengji: 92 }
];
// 直接在原数组上进行改变
user.map(function(value){
value.click = 100;
});
console.log(user);
// [{ name: "张三", chengjji: 88, click: 100 },{ name: "李四", chengjji: 92, click: 100 }]
// 在新数组上进行改变
let newArray = user.map(function(value){
// 方式一:
return Object.assign({click: 100}, value);
// 方式二:
return {
name: value.name,
chengji: value.chengji,
click: 100
};
});
reduce()
reduce()
方法在每个数组元素上运行函数,以生成(减少它)单个值,该方法在数组中从左到右工作,且方法不会减少原始数组
该函数最多接受 4 个参数:总数(初始值/先前返回return
的值),项目值,项目索引,数组本身(pre/total, value, index, array)
// 语法介绍
let arr = [1, 2, 3, 4, 5];
arr.reduce(function(pre, value, index, array){
console.log(pre, value);
return 99;
});
// 结果显示:
1 2 pre为初始值,value为第二个值
99 3 pre为返回值,value为第三个值
99 4 pre为返回值,value为第四个值
99 5 pre为返回值,value为第五个值
arr.reduce(function(pre, value, index, array){
console.log(pre, value);
return 99;
}, 0); // 添加的参数为初始值
// 结果显示:
0 1 pre为初始值,value为第一个值
99 2 pre为返回值,value为第二个值
99 3 pre为返回值,value为第三个值
99 4 pre为返回值,value为第四个值
99 5 pre为返回值,value为第五个值
let arr = [1, 2, 3, 1, 1];
// 统计数组中某个元素出现的次数
function arrayCount(array, item){
retrun array.reduce(function(total, value){
total += item == value ? 1 : 0;
return total;
}, 0);
}
console.log(arrayCount(arr, 1)); // 结果显示3
// 获取数组中的最大值
function arrayMax(array){
retrun array.reduce(function(pre, value){
return pre > value ? pre : value;
});
}
console.log(arrayMax(arr)); // 结果显示3
// 数组去重
let newArr = arr.reduce(function(arr, cur){
if(arr.includes(cur) === false){
arr.push(cur);
}
return arr;
}, []);
console.log(newArr); // 结果显示为[1, 2, 3]
// 数组左到右计算数组中所有数字的总和
var numbers1 = [45, 4, 9, 16, 25];
function myFunction(total, value) {
return total + value;
}
var sum = numbers1.(myFunction);
let cart = [
{name: "iphone", price: 12000},
{name: "imac", price: 18000},
{name: "ipad", price: 2000},
];
// 看购物车中最贵的商品
function maxPrice(goods){
retrun goods.reduce(function(pre, value){
return pre.price > value.price ? pre : value;
});
}
console.log(maxPrice(cart));// 结果显示{name: "imac", price: 18000}
// 汇总购物车中商品的总价格
function sum(goods){
return goods.reduce(function(total, value){
return (total += value["price"]);
}, 0);
}
console.log(sum(cart)); // 结果显示32000
// 获取价格超过1万元商品的名称
function getNameByPrice(goods, price){
return goods.reduce(function(arr, cur){
if(cur.price > price) arr.push(cur);
return arr;
}, []).map(function(item){return item.name});
}
console.log(getNameByPrice(cart), 10000);
// 结果显示:[{name: "iphone"},{name: "imac"}]
// 过滤掉重复的商品
function filterGoods(goods){
return goods.reduce(function(arr, cur){
let find = arr.find(function(v){
return v.name == cur.name;
});
if(!find) arr.push(cur);
return arr;
}, []);
}
reduceRight()
方法在每个数组元素上运行函数,以生成(减少它)单个值,该方法在数组中从右到左工作,且方法不会减少原始数组
该函数最多接受 4 个参数:总数(初始值/先前返回的值),项目值,项目索引,数组本身(pre/total, value, index, array)
// 从数组右到左计算数组中所有数字的总和
var numbers1 = [45, 4, 9, 16, 25];
var sum = numbers1.reduceRight(myFunction);
function myFunction(total, value) {
return total + value;
}
集合
Set
集合类型Set
,数组/对象里面可以放多个数据,集合类型也是,但是集合类型不能放重复的数据
基本语法
// 声明set类型
let set = new Set();
set.add(1);
set.add('1');
console.log(set); // 结果显示:Set(2) {1, '1'}
// 也可以在执行构造的时候直接添加元素
let set = new Set([1, '1']);
// 和对象对比,在对象中属性都会转换为字符串
let obj = {
1: "第一个值",
"1": "第二个值" // 两个属性都是“1”
};
console.log(obj); // 结果显示:{1: "第二个值"} 当属性名一样时,后一个会将前一个进行覆盖
let hd = new Set("123456");
console.log(hd); // 结果显示为 Set(6) {"1", "2", "3", "4", "5", "6"}
常用的集合方法:
let set = new Set([1, "1"]);
// 获取元素的数量:
console.log(set.size); // 结果显示2
// 判断是否有某些元素:
console.log(set.has("1")); // 结果显示true
// 删除元素:
console.log(set.delete("1")); // 结果显示true true表示已删除元素,元素不存在则返回false
// 清空元素:
set.clear()
类型转换
set
类型转换成数组:
let set = new Set(["jlc", "baidu"]);
// 方法一:通过构造函数进行转换
console.log(Array.from(set)); // 结果显示["jlc", "baidu"]
// 方法二:通过点语法进行转换
console.log([...set]); // 结果显示["jlc", "baidu"]
实用的小案例:
// 将set类型中值大于5的值移除
// 思路:先转换为数组,对数组进行操作,最后在转换为set类型
let hd = new Set("123456");
let arr = [...hd].filter(function(item){
return item < 5;
});
hd = new Set(arr);
// 去除数组中的重复元素
// 思路:将数组转换为set类型,再转换为数组
let arr = [1, 1, 2, 3];
arr = [...new Set(arr)];
遍历set类型
let set = new Set(["jlc", "baidu"]);
// 通过forEach遍历
set.forEach(function(value, key, set){
console.log(set);
})
// 结果显示:
Set(2) {"jlc", "baidu"}
Set(2) {"jlc", "baidu"}
// 通过for of进行遍历
for(const value of set){
console.log(value);
};
// 结果显示
jlc
baidu
处理并集,交集和差集
let a = new Set([1, 2, 3, 4, 5]);
let b = new Set([4, 5, 2, 9]);
// 并集
console.log(new Set([...a, ...b])); // Set(6) {1, 2, 3, 4, 5, 9} 集合会把重复的元素剔除,只保留一个
// 交集,a和b中共同存在的
consloe.log(
new Set(
[...a].filter(function(item){
return b.has(item);
})
)
); // 结果显示为 Set(3) {2, 4, 5}
// 差集,a中的某些元素在b中是不存在的
consloe.log(
new Set(
[...a].filter(function(item){
return !b.has(item);
})
)
); // 结果显示为 Set(2) {1, 3}
WeakSet
WeakSet
主要特点是保存对象数据
WeakSet
类型和Set
类型一样,不能有重复的值,但是WeakSet
要求必须是引用类型(对象,数组)
let set = new WeakSet("jlc"); // 报错
let set = new WeakSet(["jlc", "baidu"]); // 报错
// 但是可以先创建,再添加
let set = new WeakSet();
set.add(["jlc", "baidu"]);
弱引用特性
强引用的垃圾回收机制:
let hd = {name: 'jlc'};
let ed = hd; // 内存地址中的{name: 'jlc'}被两个地方引用了
hd = null; // 将hd与内存内容的引用连接断开,引用次数减一,内存地址中的{name: 'jlc'}被一个地方引用了
console.log(ed); // 结果还是显示{name: 'jlc'}
ed = null; // 如果ed也不引用了,那么该内存地址中的内容就变成了垃圾,需要进行垃圾回收,释放内存
系统会自动进行检测,如果某内存地址的内容,没有被任何变量进行引用,就会进行垃圾回收
WeakSet
的弱引用特性:
let hd = {name: 'jlc'};
let ed = hd; // 强引用
let set = new WeakSet();
set.add(hd); // 弱引用,系统不会在引用连接计数器中加一
// 将引用连接断开
hd = null;
ed = null;
// 强引用断完后,内存地址就会被垃圾回收,那么set就读不出内容,但是它不知道,它认为里面是还有数据的
因为存在弱引用的特性,所以js
中规定WeakSet
类型是不能进行循环遍历操作的,因此WeakSet
类型相比与Set
类型只能使用add
,has
和delete
方法,其他方法都是不能使用的(这也是弱引用类型的方便)
Map
Map
类型可以将我们的对象,函数,标准类型,字符串,数值作为键名,在以往的对象中,只能将字符串作为键名(常规数组中的键名0是字符串'0'):
let obj = {
name: "jlc", // name是字符串
1: "111" // 数值类型会自动转换为字符串类型,在对象中,如果键名是一样的,后面的会覆盖前面的
};
// 读取对象的方式有以下几种方式:obj.name obj['name'] obj[index]
console.log(obj.name) // jlc
console.log(obj['1']) // 111
console.log(obj[1]) // 111
在新增的Map
类型中,什么类型都可以作为键名:
let map = new Map();
// 通过set方式往map集合中追加数据
// 字符串作为键名
map.set("name", "jlc");
console.log(map); // Map(1) {"name" => "jlc"} key为"name",value为"jlc"
// 函数作为键名
map.set(function(){}, "jlc");
console.log(map); // Map(1) {function(){} => "jlc"} key为f(),value为"jlc"
// 对象作为键名
map.set({}, "jlc");
console.log(map); //Map(1) {Object => "jlc"} key为{},value为"jlc"
// 数值作为键名
map.set(1, "jlc");
console.log(map); // Map(1) {1 => "jlc"} key为1,value为"jlc"
// 也可以使用构造的时候直接往map集合中添加数据
let map = new Map([["name", "jlc"], ["age", 24]]);
console.log(map) // Map(2) {"name" => "jlc", "age" => 24}
// 链式追加元素
let map = new Map();
map.set("name", "jlc").set("age", 24); // Map(2) {"name" => "jlc", "age" => 24}
增删改查操作
let obj = {};
let map = new Map();
map.set(obj, "jlc"); // 增,改
console.log(map.get(obj)) // 查,通过引用类型进行查找,返回的是对应的value
console.log(map.has(obj)) // 检测某个键名的元素是否存在,存在则返回true
console.log(map.delete(obj)) // 指定键名进行删除,删除成功返回true
map.clear() // 删除全部
遍历Map类型
let map = new Map([["name", "jlc"], ["age", "24"]]);
// 遍历键相关
// 遍历所有的键
console.log(map.keys()); // MapIterator {"name", "age"}
// 循环所有的键
for(const key of map.keys()){
console.log(key);
}
// name
// age
// 遍历值相关
// 遍历所有的值
console.log(map.values()); //MapIterator {"jlc", "24"}
//循环所有的值
for(const value of map.values()){
console.log(value);
}
// jlc
// 24
// 遍历键值对相关
// 遍历所有键值对
console.log(map.entries()); // MapIterator {"name" => "jlc", "age" => "24"}
// 循环所有的键值对
for(const [key, value] of map.entries()){
console.log(key, value);
}
// name jlc
// age 24
类型转换
将Map
类型转换为数组类型
let map = new Map([["name", "jlc"], ["age", "24"]]);
// 将Map类型转换为数组,通过点语法进行转化
console.log(...map);
// ["name", "jlc"]
// ["age", "24"]
console.log([...map]); // [0: ["name", "jlc"], 1: ["age", "24"]]
// 单独转换键
console.log([...map.keys()]); // ["name", "age"]
// 单独转换值
console.log([...map.values()]); // ["jlc", "24"]
WeakMap
WeakMap
类型中的键,只能是引用类型(对象,数组)
let map = new WeakMap();
map.set("name", "jlc"); // 报错
map.set([], "jlc"); // 不报错 key为[],value为"jlc"
WeakMap
相较于Map
类型,只能使用set
,delete
和has
方法,其他Map
类型有的属性WeakMap
类型都是用不了的,同时WeakMap
也是不可迭代的(不可遍历循环的)
弱引用特性
let hd = {name: "jlc"};
let cms = hd; // 强引用
let map = new WeakMap();
map.set(hd, "24"); // 弱引用,将hd引用为key
// 断开强连接
hd = null;
cms = null;
// 强引用断完后,内存地址就会被垃圾回收,那么map就读不出内容,但是它不知道,它认为里面是还有数据的
所以WeakMap
中的读取键,keys
等方法是不能使用的,因为它是弱引用类型,如果能使用keys
方法,其读取到的值可能是不正确的,所以js
中就规定其是不能使用的
字符串
字符串(String
)字面量可以使用单引号或双引号
JavaScript
中字符串用于存储和操作文本,JavaScript
字符串是引号中的零个或多个字符
可以在字符串中使用引号,只要不匹配围绕字符串的引号即可(与最外层包裹的引号不同即可),外面双里面单,外面单里面双,如果一定要内外使用一样的引号,可以使用转义字符来分隔内部的引号
代码 | 结果 | 描述 |
---|---|---|
\' | ' | 单引号 |
\" | " | 双引号 |
\\ | \ | 反斜杠 |
var x = "中国是瓷器的故乡,因此 china 与\"China(中国)\"同名。"
console.log(x) // 中国是瓷器的故乡,因此 china 与"China(中国)"同名。
当然,转义字符(\)也可用于在字符串中插入其他特殊字符:
代码 | 结果 |
---|---|
\b | 退格键 |
\f | 换页 |
\n | 新行 |
\r | 回车 |
\t | 水平制表符,相当于一个tab |
\v | 垂直制表符 |
html
特性,对于转义字符,无论在字符串中加入多上个同类型的转义字符,在控制台中打印可以正常显示,但是在网页中只能显示同类型的一个转义字符效果,如果想要在网页中正常显示,需要加入
let web = "ba\t\t\t idu.com"
字符串方法
字符串方法帮助我们便捷的处理字符串
字符串原始值是无法拥有属性和方法的(因为它们不是对象),但是通过 JavaScript
,方法和属性也可用于原始值,因为在执行方法和属性时 JavaScript
将原始值视为对象
方法 | 描述 |
---|---|
length | 返回字符串中内容的长度,如:txt.length ,空格也会算在长度中 |
indexOf() | 返回字符串中指定文本首次出现的索引(位置),如:var pos = str.indexOf("China"); 未找到则返回-1,同时该方法可以指定开始检索的位置,如:var pos = str.indexOf("China", 18); |
lastIndexOf() | 返回指定文本在字符串中最后一次出现的索引,如:var pos = str.lastIndexOf("China"); 未找到则返回-1,同时该方法可以指定开始检索的位置,如:var pos = str.lastIndexOf("China", 50); |
search() | 搜索特定值的字符串,并返回匹配的位置,该方法与indexOf() 是等价的,但是,search() 方法无法设置第二个开始位置参数 |
match() | 根据正则表达式在字符串中搜索匹配项,并将匹配项作为 Array 对象返回,如:text.match(/ain/gi) ,返回数组[ain,ain,ain] ,返回一个数组,放入找到的所有字符串,gi 表示执行不区分大小写的全局搜索,但是如果有大写,返回的数组中的元素仍然保留大写 |
includes() | 判断字符串是否包含指定值,返回一个bool 类型的数据,具体语法:string.includes(searchvalue, start) ,前一个是必需的,即需要搜索的字符串;第二个元素可选,表示开始搜索的位置,默认为 0 |
startsWith() | 判断字符串是否以指定值开头,返回的是一个bool 类型的数据,其具体语法与includes() 相似,该方法区分大小写 |
endsWith() | 判断字符串是否以指定值结尾,具体形式为:string.endsWith(searchvalue, length) ,第二个参数表示要搜索的长度,默认为字符串的长度 |
slice(start, end) | 提取字符串的某个部分,并在新的字符串中返回被提取的部分,如:var res = str.slice(7,13); 切取的部分索引包括start ,但是不包括end ;如果参数为负,则从字符串的结尾开始计数,字符串最后一个字符表示-1;如果省略第二个参数,则该方法将裁剪字符串的剩余部分 |
substring(start, end) | substring() 类似于 slice() ,不同之处在于 substring() 无法接受负的索引 |
substr(start, length) | substr() 类似于 slice() ,不同之处在于第二个参数表示被切取部分的长度,如果省略第二个参数,则该 substr() 将裁剪字符串的剩余部分,如果首个参数为负,则从字符串的结尾开始计算位置 |
replace() | 用另一个值替换在字符串中指定的值,var n = str.replace(str, str) ,前一个str 为原先字符串中的值,后一个str 为需要替换进去的值,该方法不会改变调用它的字符串,它会返回新的字符串,该方法默认只是替换首个匹配到的字符串,不能进行全部替换,如需替换所有匹配,请使用正则表达式的 //g 标志(用于全局搜索)如:var n = str.replace(/my/g, "our"); 注意包裹后不带引号;同时对大小写区分敏感,如需执行大小写不敏感的替换,请使用正则表达式 /i (大小写不敏感),如:var n = str.replace(/MY/i, "our"); 通过//i 包裹原先的字符串,注意包裹后不带引号 |
toUpperCase() | 把字符串全部字符转换为大写 |
toLowerCase() | 把字符串全部字符转换为小写 |
concat() | 连接两个或多个字符串,该方法可用于代替加法运算符,text = text1.concat(" ",text2); 方法第一个参数表示连接方式,例子表示用空格连接,将text2 用空格连接到text1 字符串后面 |
trim() | 删除字符串两端的空白符,如:text.trim() |
charAt(position) | 返回字符串中指定下标(位置)的字符串,也可以通过[position] 来访问某个下标的字符串 |
charCodeAt(position) | 返回字符串中指定索引的字符 unicode 编码,a 的unicode 编码为97 |
split() | 将字符串转换为数组,其方法的参数表示用什么进行分割,如txt.split(","); 表示用逗号进行分隔,如果省略分隔符,被返回的数组将包含index[0] 中的整个字符串(将整个字符串放到数组的第一个位置) |
repeat() | 重复输出,效果类似于字符串相乘:"*".repeat(3) |
所有字符串方法都会返回新字符串,它们不会修改原始字符串,因为字符串是不可变的,具体来说:字符串不能被更改,只能替换
不建议对字符串进行属性访问(str[int]
),因为如果找不到字符(输入的索引超过了字符串的长度),返回的结果是 undefined
,而 charAt()
返回空字符串;它是只读的。str[0] = "A"
不会产生错误(但也不会生效),如果想要进行属性访问,可以先把它转换为数组
// 手机号码模糊处理
function phone(mobile, len = 3){
return String(mobile).slice(0, len * -1) + "*".repeat(len);
}
console.log(phone(13456670000, 6))
// 结果显示为:13456******
字符串字面量
JavaScript
中的字面量字符串是一种方便的字符串语法,允许你在字符串中嵌入表达式和变量
模板字面量是用反引号(`)分隔的字面量,允许多行字符串、带嵌入表达式的字符串插值和一种叫带标签的模板的特殊结构
模板字符串中可以同时使用单引号和双引号:
let text = `He's often called "Runoob"`;
// 显示的结果为:He's often called "Runoob"
模板字符串还支持多行文本,而无需使用特殊的转义字符,``中的内容形式是怎么样的,在控制台中就怎么样:
const multiLineText = `
This is
a multi-line
text.
`;
通过字面量连接字符串:
let name = 'jlc';
let age = '24';
console.log(`我的名字是${name},年龄为${age}岁`)
字面量${}
中不仅可以调用变量,也可以调用函数,只要这个函数有返回值即可
字面量中可以在添加字面量
字符串插值
模板字面量提供了一种将变量和表达式插入字符串的简单方法,该方法称为字符串插值
具体语法为:${...}
,用真实值自动替换变量称为字符串插值
let firstName = "Bill";
let lastName = "Gates";
let text = `Welcome ${firstName}, ${lastName}!`;
//结果为:Welcome Bill, Gates!
字符串插值在HTML中的使用:
let header = "Templates Literals";
let tags = ["template literals", "javascript", "es6"];
let html = `<h2>${header}</h2><ul>`;
for (const x of tags) {
html += `<li>${x}</li>`;
}
html += `</ul>`;
标签模板
可以将标签中的变量拿出来,进行二次处理
let name = "jlc";
let age = "24";
function tag(strings, ...vars){
console.log(vars);
console.log(strings);
}
console.log(tag`我的名字是${name},年龄为${age}。`);
// 结果显示为
{"jlc", "24"}
{"我的名字是", ",年龄为", "。"} // 如果没有默认字符串,其前后也会加上空:{"","",""},字符串的数量一定是大于变量数量的
字符串类型转换
字符串转换为数值
let text = "9"
// 隐式转换:
console.log(text * 1) // 结果显示9
// 通过构造函数进行转换
console.log(Number(text)) //结果显示9
数值转换为字符串
const num = 66
// 隐式转换
const srt = num + "";
// 通过构造函数进行转换
String(num)
字符串转换为数组
const web = "baidu"
// 通过字符串方法进行转换
console.log(web.split("")); // ['b', 'a', 'i', 'd', 'u']
数组转换为字符串
const array = ["jlc", "24"];
// 通过join方法将数组转换为字符串
console.log(array.join("")); // 'jlc24'
// 通过构造函数进行转换
console.log(array.toString()); // 默认是通过使用逗号进行连接的 'jlc,24'
对象
为什么要进行面向对象的学习:使用函数编程会导致出现复杂的代码块,函数编程会暴露到全局,可能带来一系列问题,函数编程需要改良成面向对象的编程,将函数作为对象中的一个方法,将关联性的业务封装起来,变成一个对象
对象(Object
)字面量定义一个对象,对象是一个引用类型,赋值的时候给的是内存地址(共用一个内存区域)
对象也是变量,但是对象可以包含很多值,在JavaScript
中,几乎“所有事物”都是对象,所有JavaScript
值,除了原始值(原始值指的是没有属性或方法的值),都是对象
JavaScript
对象中的名称:值对被称为属性,属性:属性值
对象方法:方法是在对象上执行的动作,方法以函数定义被存储在属性中
var person = {
firstName: "Bill",
lastName : "Gates",
id : 678,
fullName : function() {
return this.firstName + " " + this.lastName;
}
};
在函数定义中,this
引用该函数的“拥有者”
在上面的例子中,this
指的是“拥有” fullName
函数的 person
对象,即this.firstName
的意思是 person
对象的 firstName
属性
访问对象的属性
有两种方式可以访问对象的属性:
// 方式一:objectName.propertyName,如上面的对象所示:
person.lastName; // 推荐使用
// 方式二:objectName["propertyName"],如上面的对象所示:
person["lastName"];
// 一般是在循环遍历的时候会经常使用方式二:
for(const key in person){
console.log(person[key]); // key是一个字符串,将所有对象中的值打印出来:Bill等
}
访问对象的方法
可以通过以下方式访问对象中的方法:
// objectName.methodName()
name = person.fullName();
// 如果您不加()访问 fullName 方法,则将返回整个函数定义内容的字符串
通过关键词new
来声明JavaScript
变量,该变量会被创建为对象,但是不建议把字符串、数值和布尔值声明为对象,他们会增加代码的复杂性并降低执行速度
JavaScript
对象是无法进行对比的,比较两个 JavaScript
对象(不管是==比较还是===比较),将始终返回 false
动态管理对象的属性
对象的属性和方法的动态添加
// 给person对象添加一个age属性,其属性值为18
person.age = 18;
person["age"] = 18;
// 给person对象添加一个get方法
person.get = function(){
return `${this.lastName}的年龄是${this.age}`
}
对象的属性和方法的动态删除
delete person.age; // 删除person对象的age属性
属性的检测
判断对象中是否有某个属性:
方法 | 描述 |
---|---|
hd.hasOwnProperty("age") | 判断hd 对象中是否存在age 属性(只看自己是否有该属性) |
原型链属性:对象的原型,可以理解为对象的父级,对象可以拿(继承)父级的属性进行使用的
对于数组和对象来说,length
属性是存在数组对象中的,还有一个参数__proto__
表示原型(理解为该数组的父级,在父级里面会有很多的属性)
hasOwnProperty
方法是不能看到父级的属性的,如果需要看到当前自己和父级的属性,可以通过"属性" in arr
方式进行查找,该属性在数组自己或父级中,则返回true,反之,返回false
判断对象的属性是否存在:
// 将b对象做成a对象的父亲
let a = {
name: "jlc"
};
let b = {
age: 24
};
Object.setPrototypeOf(a, b); // 为对象a设置一个新的父亲,父亲是b,这样a属性的原型中就有b的属性
console.log(a.hasOwnProperty("age")); // false
console.log("age" in a); // true
对象属性参与计算
对象的属性是通过计算之后产生的,有时候需要对数据结构进行处理,来改变其属性
let hd = {};
let title = "name";
hd[title] = "jlc";
console.log(hd.name); // jlc
有时候为了改变从后台返回的数据结构的形式,我们需要对对象属性进行计算,动态的改变属性:
// 后台拿到的数据的形式:
const lessons = [
{
title: "布局",
category: "css"
},
{
title: "数据库",
category: "mysql"
}
];
// 我们需要将拿到的对象数据进行动态的修改,方便我们后续的使用
let res = lessons.reduce((obj, cur, index) => {
obj[`${cur["category"]}-${index}`] = cur;
return obj;
},{});
console.log(JSON.stringify(res, null, 2));
// 使用系统提供处理json转换为字符串的方法,null表示保留所有内容,2表示tab键为两位
// 最后得到的结果为:
{
"css-0": {
title: "布局",
category: "css"
},
"mysql-1": {
title: "数据库",
category: "mysql"
}
}
assign方法
Object.assign()
方法用于将两个对象进行合并
let hd = Object.assign({ a; 1 }, { b: 2 });
console.log(hd); // {a: 1, b: 2}
获取对象属性和值的方法
let hd = {
name: "jlc",
age: 24
};
console.log(Object.keys(hd)); // 获取对象中的属性名 ["name", "age"]
console.log(Object.values(hd)); // 获取对象中的属性值 ["jlc", 24]
console.log(Object.entries(hd)); // 对象的属性名和属性值一起获取,组成一个数组
// [["name", "jlc"], ["age", 24]] 得到的是二维数组的结构
对象的循环遍历操作
let hd = {
name: "jlc",
age: 24
};
for(const key in hd){
console.log(hd[key]);
}
// jlc 回车 24
for(const iterator of Object.keys(hd)){ // 这了的keys可以改为values和entries
console.log(iterator);
}
// name 回车 age
for(const [key, value] of Object.entries(hd)){
console.log(key); // name 回车 age
console.log(value); // jlc 回车 24
}
展开语法在对象中的使用
展开语法是对数组和对象的批量处理
let user = {name: "jlc", age: "24"};
let hd = { ...user, lang: "zh"};
console.log(hd); // {name: "jlc", age: "24", lang: "zh"}
通过展开语法进行内容的修改:
function upload(params){
let config = {
type: "*.jpeg,*.png",
size: 10000
};
config = { ...config, ...params}; // 属性一样,后面的属性值会覆盖前面的属性值
console.log(config);
}
// 不传,就使用默认的;传元素,就使用你传递的内容,也可以传入新的属性和属性值
upload({size: 99}); // {type: "*.jpeg,*.png", size: 99}
解构语法特性
解构语法特性是对元素的结构进行分解处理,将结构打散后赋值给变量
let user = {name: "jlc", age: "24"};
let {name: n, age: age} = user; // 对象的解构,解构的变量不用获取全部,可以只获取一部分
console.log(n); // jlc
// 解构后的名字可以和对象的属性值一样,那么可以进行简写
let {name, age} = user; // 对象解构的简写
console.log(name); // jlc
解构的思路在模块化编程中的使用非常多
只要数据是一个对象,都可以进行解构处理,如果函数的返回值是对象,也可以进行解构处理:
function hd(){
return {name: "jlc", age: "24"};
}
let {name, age} = hd();
console.log(name); // jlc
在函数传递参数的时候,也可以使用解构特性:一般是传递数据库中拿过来的数据
function user({ name, age }){
console.log(name, age);
}
user({ name: "jlc", age: 24 }); // jlc 24
// 也可以穿插着使用,部分使用解构特性
function hd(name, {age, sex}){
console.log(name, age, sex);
}
hd("jlc", {age: 24, sex: "男"}); // jlc 24 男
在严格模式中解构的差异
在严格模式中,解构语句前面必须要用let/const
进行声明,不然会报错:
let {name, age} = {name: "jlc", age: "24"};
// 非严格模式下可以简写为:
({name, age} = {name: "jlc", age: "24"});
多层对象的结构
如果对象中包含对象,那么这个对象被称为多层对象,对于多层对象可以进行解构
let hd = {
name: "jlc",
lesson: {
title: "js"
}
};
let {name, lesson: {title}} = hd;
console.log(name, title); // jlc js
解构的默认值
当解构后的变量,其值不存在时,那么如果设置了默认值,就会显示默认值,如果值存在,就会显示其值
let user = {name: "jlc", age: "24"};
let {name, age, sex="男"} = user;
console.log(name, age, sex); // jlc 24 男 如果没有加默认值,那个sex显示undefined
对象的复制
传统情况的对象赋值,传递的是内存地址
let hd = { name: "jlc" };
let cm = hd; // 将对象hd的内存地址给cm
cm.name = "jlc123";
console.log(hd); // { name: "jlc123" } 如果改变cm中的属性值,同时会改变原对象hd中的值,公用地址
浅拷贝
不能将原始对象中深层次(嵌套)的对象进行复制给另外一个新的对象
那么正常情况下对象要怎么进行复制呢?
let hd = { name: "jlc" };
let cm = {
name: hd.name // 将hd中的name属性的值取出来,作为cm对象中name属性的值
};
// 这样hd和cm对象就是两个不同的内存数据,开辟了两块不同的内存空间,对象cm的属性值改变,不会影响hd的属性值
如果被复制对象的属性特别多,我们需要用循环进行复制:(一般用于复制的时候想要内容发生一些改变时使用的,如将复制的内容都加上一个后缀)
let hd = { name: "jlc", age: 24};
let obj = {};
for(const key in hd){
obj[key] = hd[key];
}
// 此时开辟的是两块内存地址,但是内容是一样的,修改obj内部的值对hd是没有影响的
除了循环的方式进行复制,也可以通过提供的assign()
方法进行复制:
let hd = { name: "jlc", age: 24};
let obj = Object.assign({}, hd); // 加对象hd中的值压到新对象中
通过展开语法进行复制:
let hd = { name: "jlc", age: 24};
let obj = {...hd}; // 将hd对象的结构拿过来,放到obj对象新开辟的空间中
如果想要复制的时候,新对象发生一些改变,可以通过使用for
循环进行复制,否则建议使用后面两种方法
深拷贝
可以将原始对象中深层次(嵌套)的对象进行复制给另外一个新的对象
通过浅拷贝,对于对象中的对象,如果采用浅拷贝,新对象和原对象对于对象中的对象元素还是共用一个内存地址的
我们可以通过深层次的复制:深拷贝就是一层一层的处理,通过递归算法
let data = {
name: "jlc",
user: {
age: "24"
},
arr: [1, 2]
};
function copy(obj){
let res = obj instanceof Array ? [] : {};
for(const [key, value] of object.entries(obj)){
res[key] = typeof value == 'object' ? copy(value) : value;
}
return res;
}
let hd = copy(data);
创建对象的封装和抽象
在一类对象中,其往往处理的内容都是一样的,这类对象中可能都有类似的方法,我们没有必要每次创建这类对象的时候反复的复制这些方法代码,我们可以通过以下的方法进行简化
工厂函数
工厂是生成我们的对象的,将方法动作统一生成我们的对象
// 工厂函数
function user(name) {
return {
name,
show: function() {
console.log(this.name + `-姓名`);
}
};
}
// 通过工厂函数生成对象
let obj = user("jlc");
console.log(obj); // 结果显示为:{name: "jlc", show: f}
obj.show() // jlc-姓名
对于工厂函数中对象的方法进行修改后,同时会影响所有使用该工厂函数创建的对象
使用工厂创建对象,可以使我们的代码更加规范,可以对我们的统一动作进行定制
构造函数
定义构造函数要求首字母大写,构造函数可以在创建的时候传递参数,来为我们的对象做初始值
// 构造函数,this是只当前调用这个方法的对象
function User(name){
this.name = name;
this.show = function(){
console.log(this.name);
};
}
// 通过构造函数来创建对象
let obj = new User('jlc')
console.log(obj); // 结果显示:User {name: "jlc", show: f}
obj.show(); // jlc
拓展:在js
中,所有的数据类型都是可以通过构造函数进行创建的(但是对于大多数的类型都不建议通过构造函数进行创建,一般是直接创建即可)
// 通过构造函数创建对象类型
let obj = new Object();
// 通过构造函数创建数值类型
let num = new Number(1);
// 此时num是一个对象,如果想要取到值可以通过:num.valueOf()为数值类型
// 当然直接通过运算后,会直接变成数值:num + 3 结果为4
// 通过构造函数创建字符串
let str = new String('jlc');
// 此时str是一个对象,如果想要取到值可以通过:str.valueOf()为字符串类型
所以说,我们为什么可以去使用字符串的方法,就是通过构造函数创建后,其实质上就是一个对象,构造函数中的一些方法可以去使用,当然有些方法可能不在构造函数中,可能在原型的父级中
抽象
被封装的对象,其属性还是可以被外部进行访问到的,还没有实现到抽象
抽象可以理解为一个手机的内部有很多复杂的逻辑,复杂逻辑的功能实现都被封装到手机的内部了,只给外部提供了一些案件的操作(这个逻辑称为抽象:把一些属性和方法封装到内部,不让外部进行访问)
function User(name, age){
let data = {name, age}; // 对属性的抽象化处理
let info = function(){
return data.age > 50 ? "老年" : "青年";
};
this.show = function(){
console.log(data.name + info()); // 通过闭包特性
};
}
let obj = new User("jlc", 24);
obj.show(); // jlc青年
// 外部是不能修改其封装的属性的
this.name = "JLC" // 无效,并不影响之前的结果,改变方法info也是改变不了的
对象的属性特征
我们可以对一个对象属性的一些操作进行控制,比如添加属性,删除属性等等,但是更加深度的控制还不能实现,如,不能设置让一个属性不能被修改,不能被删除等等
const user = {
name: "jlc",
age: 24
};
// 查看对象中某一个属性特征的描述
console.log(Object.getOwnPropertyDescriptor(user, "name"))
// 显示如下的内容:
{
"value": "jlc", // 属性的值
"writable": true, // 属性是否可修改
"enumerable": true, // 属性是否可遍历
"configurable": true // 属性是否可以被删除
}
// 所以在默认的情况下,对象中的某个属性是可以被编辑、遍历和删除的
// 查看对象中所有属性的特征
console.log(Object.getOwnPropertyDescriptors(user))
更改对象的的属性,如果属性存在,就更改这个属性,如果这个属性不存在,那么就创建这个属性(相当于新增属性)
// 对象还是之前的user对象
Object.defineProperty(user, "name",{
value: "JLC", // 更改user对象中属性的value值
"writable": false, // 设置属性不能被修改
});
user.name = "jlc123"; // 在严格模式下就会报错,在非严格模式下不报错但不生效
// 对多个属性进行统一的设置
Object.definePropertys(user,{
name: {
value: "jlc12",
"writable": false,
"enumerable": false,
"configurable": false
},
age: {
value: 23,
"writable": false,
"enumerable": false,
"configurable": false
}
});
有了这种方法,我们就可以将对象中的某些属性进行保护起来
系统提供了简化API
用于对对象属性的设置
// 禁止向对象中添加属性API
Object.preventExtensions(user);
// 判断对象属性是否可添加:Object.isExtensible(user)
// 封闭对象(不能向对象中添加属性,也不能删除对象,也不能修改对象的特征)的API
// configurable被设置为false
Object.seal(user);
// 判断一个对象是否处于封闭状态:Object.isSealed(user)
// 冻结对象属性,writable和configurable都变成了false
Object.freeze(user)
// 判断对象是否处于冻结状态:Object.isFrozen(user)
访问器
一个对象的属性值在默认情况下是可以在外界进行修改的,这就导致这个对象会很不稳定,外界访问对象需要有一个把关的环节,这个环节就是访问器,访问器用于检测修改内容的质量(数据的安全过滤)
cosnt user = {
name: "jlc",
age: 24
}
user.age = 19999 // 质量不好的修改,应该拒绝修改
// 设置访问器进行质量的控制
const user = {
data: { name: "jlc", age: 10 },
set age(value){
if(typeof value != "number" || value < 0 || value > 100){
throw new Error("年龄不符合规范");
}
this.data.age = value;
},
get age(){
return data.age;
}
};
user.age = 87;
console.log(user.age); // 87
使用访问器伪造属性
使用访问器伪造属性,这样我们在获取的时候就会比较方便
let Lesson = {
lists: [
{ name: 'js', price: 100 },
{ name: 'css', price: 200 },
{ name: 'vue', price: 150 }
],
get total(){
return this.lists.reduce((t, l) => t + l.price, 0);
}
};
console.log(Lesson.total) // 450
// total是伪造器伪造的属性,不是真正意义上的属性
Lesson.total = 500; // 在严格模式下会报错,普通模式下不生效
使用访问器批量设置属性
const web = {
name: "jlc",
url: "baidu.com",
set site(value){
[this.name, this.url] = value.split(",");
},
get site(){
return `${this.name}的网址是${this.url}`;
}
};
// 提供访问器批量设置属性的value
web.site = "网站,www.baidu.com";
console.log(web.name); // 网站
console.log(web.site); // 网站的网址是www.baidu.com
访问器的应用
在前后端分离开发的时候,为了与后端的接口进行对接,我们需要存储一个token
,令牌的处理可以通过访问器来进行控制,一般获取到令牌后,需要存储到本地
let Request = {
// 保存token
set token(content){
localStorage.setItem('token', content);
},
// 获取token
get token(){
let token = localStorage.getItem('token');
if(!token){
alert('请登录')
}
return token;
}
}
Request.token = "123456789";
console.log(Request.token); // 123456789 同时在网络当中也能看到这些数据
// 这些数据没有放到对象中,只是在我们的本地存储中
冲突
访问器控制属性和使用原始方法控制属性的冲突
const user = {
name: "jlc",
age: 24,
set name(value){
console.log(value + "姓名");
}
};
user.name = "JLC"; // 访问器的优先级高,结果显示:jlc姓名
console.log(user); // 同时打印变量丢失了name的属性:{age: 24}
普通属性的优先级比访问器的优先级低
如果想要将name
进行保存,可以将其定义为一个私有的属性
const user = {
data: { name },
age: 24,
set name(value){
this.data.name = value;
},
get name(){
return this.data.name;
}
};
user.name = "jlc";
console.log(user.name); // jlc
使用类定义访问器
类可以理解为构造函数的一个简写的形式,通过类的形式(语法糖的形式)来定义访问器:
class User{
constructor(name, age){
this.data = { name, age };
}
get name(){
return this.data.name;
}
set name(value){
if(value.trim() == "" || value.length > 20){
throw new Error("用户名不合法");
}
this.data,name = value;
}
get age(){
return this.data.age;
}
}
let obj = new User("jlc", 24);
obj.name = "JLC";
console.log(obj.name); // JLC
对象的代理
访问器是对对象的某个属性进行控制,而对象的代理是对整个对象进行控制
代理就相当于买房的中介公司,我们通过中介公司来买房子,在计算机中,我们不直接操作数据,一般是通过代理来访问数据
代理在Vue
框架的数据绑定等都会使用到对象代理的概念
代理的基本语法
const hd = { name: "jlc" };
// 代理hd这个对象
const proxy = new Proxy(hd,{
// 写一些方法
// 访问值
get(obj, property){
return obj[property];
}
// 设置值
set(obj, property, value){
obj[property] = value;
return true; // 在严格模式下需要有这一行
}
});
// 通过代理进行访问
console.log(proxy.name); // jlc
proxy.name = "JLC";
console.log(proxy.name); // JLC
// 打印整个代理
console.log(proxy); // Proxy {name: "JLC"} 获取到整个对象的属性
代理不仅可以对对象进行代理,也可以对函数进行代理:
function factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1);
}
let proxy = new Proxy(factorial,{
// 通过代理来访问就会访问apply方法
// func表示代理的函数;obj表示上下文;args表示传入的参数
apply(func, obj, args){
console.log(func)
console.log(obj)
console.log(args)
}
})
// 通过代理进行访问
proxy(5);
// 依次打印
f factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1);
}
undefined
[5] // 代理是通过数组传参的
打印console.log(obj)
显示的是undefined
,是因为没有上下文,我们可以通过proxy.apply({}, [5])
的方式进行调用,这个时候console.log(obj)
打印的就是{}
,括号中前半部分传递的是什么,打印的就是什么
通过代理来查看函数阶乘执行的时间:
function factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1);
}
let proxy = new Proxy(factorial,{
apply(func, obj, args){
console.time('run')
func.apply(this, args); // 调用函数
console.timeEnd('run')
}
})
把函数通过代理,来作为中间桥梁进行工作
代理对数组进行的拦截操作:我们可以通过代理对获取的数组元素的某个内容的长度进行截断处理操作
const lessons = [
{
title: 'aaaaaaaa',
},
{
title: 'bbbbbbbb',
},
{
title: 'cccccccc',
}
]
let proxy = new Proxy(lessons, {
get(array, key){
const title = array[key].title;
const len = 5;
retrn title.length > len ? title.substr(0, len) + '.'.repeat(3) : title;
}
});
console.log(proxy[2]) // ccccc...
// 如果要使其返回的是当前索引的对应截取数组,可以进行以下的修改
let proxy = new Proxy(lessons, {
get(array, key){
const title = array[key].title;
const len = 5;
array[key].title = title.length > len ? title.substr(0, len) + '.'.repeat(3) : title;
retrn array[key];
}
});
console.log(proxy[2])
// 返回的结果是:
{
title: 'ccccc...',
}
在vue
双向数据绑定中使用代理拦截的例子:
<body>
<input type="text" v-model="title" />
<input type="text" v-model="title" />
<h4 v-bind="title">这里的数据也会跟着发生变化</h4>
</body>
<script>
function View() {
// 创建一个代理,在这个代理中使用对象来进行存储数据
let proxy = new Proxy({}, {
// 获取数据的方法,传递的参数是对象和属性
get(obj, property){},
// 设置数据的方法相比获取数据多了一个值参数
set(obj, property, value){
// 设置页面元素的渲染
document.querySelectorAll(`[v-model="${property}"]`)
.forEach(item => {
item.value = value;
});
document.querySelectorAll(`[v-bind="${property}"]`)
.forEach(item => {
item.value = value;
});
}
});
// 绑定事件
this.init = function(){
// 找到所有绑定v-model的元素
const els = document.querySelectorAll("[v-model]");
// 为其加上键盘抬起的触发事件
els.forEach(item => {
item.addEventListener("keyup", function(){
// 通过代理的形式来更新数据
proxy[this.getAttribute("v-model")] = this.value;
});
});
};
}
</script>
对于绑定同一个模型的文本展示区域,其内容会同步的发生变化,在其底层逻辑中,我们可以理解为:当任何一个表单更改了数据之后,实际上是更改数据容器中的数据,数据容器中的数据发生更改后,会要求页面进行重新的渲染,同步的更改其他的两个容器的数据,这个数据容器就是我们所说的代理
使用代理来进行表单验证:
一般是定义一个类,在类中写一些方法来进行提供一些功能的验证:
<body>
<input type="text" validate rule="max: 12, min: 3" />
<input type="text" validate rule="max: 3, isNumber" />
</body>
<script>
// 功能类
class Validate{
// 数据长度的最大值
max(value, len){
return value.length < len;
}
// 数据长度的最小值
min(value, len){
return value.length > len;
}
// 判断是否为数组
isNumber(value){
return /^\d+$/.test(value);
}
}
let validate = new Validate();
console.log(validate.max("abc", 2)) // 结果为false
console.log(validate.isNumber("abc")) // 结果为false
function ProxyFactory(target){
return new Proxy(target, {
get(target, key){
return target[key];
},
set(target, key, el){
const rule = el.getAttribute("rule");
const validate = new Validate();
let state = rule.split(",").every(rule => {
const info = rule.split(":");
return validate[info[0]](el.value, info[1]);
});
return true;
}
});
}
const proxy = ProxyFactory(document.querySelectorAll("[validate]"));
// 当表单触发键盘抬起事件时,要触发验证处理
proxy.forEach((item, i) => {
item.addEventListener("keyup", function(){
proxy[i] = this;
})
})
</script>
rule
表示我们的验证规则,在自定义的类中完成验证
表单的代理验证一般不用自己写,可以找一些库进行之间的使用即可
布尔类型
JavaScript
提供一种布尔数据类型,它只接受值true
或false
所有具有“真实”值的即为
True
;所有不具有“真实”值的即为
False
(0,-0," ",undefined
,null
,NaN
,false
)的布尔值为false
let bool = true;
console.log(typeof bool) // 结果打印boolean
空数组/对象转换成布尔类型,其结果为真console.log(Boolean([])) // 结果为true
总结:
- 数值类型,除了0之外都是真,0是假
- 字符串类型,除了空字符串类型都为真,空字符串为假
- 对于引用类型,任何数组和对象都为真
布尔类型的显示转换
let num = 0;
// 通过!进行转换,!有两部分的功能:1,将其转换成布尔类型;2.将布尔类型取反
num = !!num;
console.log(num); // 结果为false num为布尔类型
// 通过构造函数进行转换
Boolean(num)
字符串和数组,对象转换为布尔类型同理
while(true){
const year = prompt("我是几几年出生的?").trim();
if(!year) continue;
console.log(year == "2010" ? "回答正确" : "回答错误");
break;
}
JSON
数据
在编程的时候,我们需要与多个语言或多种网站进行沟通,因此我们需要使用一种通用的格式,JSON
数据格式就是一种通用的沟通格式
JSON
是用于存储和传输数据的格式,JSON
通常用于服务端向网页传递数据
JSON
英文全称 JavaScript Object Notation,是一种轻量级的数据交换格式,往往是后端传递过来的大字符串
JSON
使用 JavaScript
语法,但是 JSON
格式仅仅是一个文本,文本可以被任何编程语言读取及作为数据格式传递
JavaScript
程序可以很容易的将 JSON
数据转换为JavaScript
对象
JSON
语法规则和相关函数
{"text":[
{"name":"Runoob", "url":"www.runoob.com"},
{"name":"Google", "url":"www.google.com"},
{"name":"Taobao", "url":"www.taobao.com"}
]}
JSON
数据类型是一种键值对的格式,其属性和值都要通过双引号来进行包裹
数据为键/值对;数据由逗号分隔;使用斜杆 \ 来转义字符;大括号保存对象;方括号保存数组,数组可以包含多个对象
JSON
数据结构和js
中的对象结构样式上是非常像的,但是JSON
数据是一个传输类型的数据结构,js
中的对象是没有办法传递给后台的,我们需要将其转化为JSON
格式(任何的编程语言都通过将其的数据结构转化成JSON
格式,或者将JSON
的格式(JSON
格式是字符串类型)转化为其所需要的格式)
let data = {
name: "jlc",
title: "111"
};
// 将js对象转化为JSON类型的数据
let json = JSON.stringify(data);
console.log(json); // 结构显示为:{"name":"jlc","title":"111"}
JavaScript
中使用内置函数JSON.parse()
可以将JSON
字符串转换为JavaScript
对象:obj = JSON.parse(text);
,转化后的对象就可以在前端直接进行使用了使用内置函数
JSON.stringify()
可以将JavaScript
值转换为JSON
字符串,对象的属性没有加双引号,但是变成JSON
格式后,会自动的补上双引号
在parse()
方法中有常见的参数用于转化后格式的处理:
该参数可以使我们对
JSON
数据进行解析之后再进行进一步的处理:js// JSON的内容为{"name":"jlc","title":"111"} let obj = JSON.parse(json, (key, value) => { if(key == "name"){ value = "myname-" + value; } return value; }); console.log(obj); // {name:"myname-jlc",title:"111"}
在stringify()
方法中有几个常见的参数:JSON.stringify(data, null, 2);
第一个参数是值我们在转化为
JSON
数据后要保留哪些属性
JSON.stringify(data, ["name"]);
表示转化后要保留name
的属性,其他属性将不会保留,写null
表示保留所有的属性第二个参数用于控制缩进,可以使我们的数据类型看起来更加美观
不仅是对象数据类型可以转化为JSON
格式,数组类型的数据也可以转化为JSON
格式
let arr = ["jlc", "111"]
let json = JSON.stringify(arr);
console.log(json); // 结构显示为:["0": "jlc", "1": "111"]
自定义返回JSON
格式
我们可以在对象中自定义编写要返回的Json
格式:
let data = {
name: "jlc",
title: {
url: "111"
},
toJSON: function(){
return {
name: this.name
mytitle: this.title.url
};
}
};
let json = JSON.stringify(data);
console.log(json); // {"name":"jlc","mytitle":"111"} 根据自定义的数据格式进行返回JSON格式