Skip to content

数据类型

在编程语言中,一般固定值称之为字面量

  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、独一无二的值(Symbol

  • 引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date

JavaScript 变量能够保存多种数据类型:数值、字符串值、数组、对象等等:

未定义变量和未赋值变量的数据类型为 undefined

js
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的区别:其值相等,但是类型不相等

js
typeof undefined              // undefined
typeof null                   // object
null === undefined            // false
null == undefined             // true

数值

数字(Number)字面量可以是整数或者是小数,或者是科学计数(e)

JavaScript 只有一种数值类型,书写数值时带不带小数点均可

JavaScript 数值始终是 64 位的浮点数,整数(不使用指数或科学计数法)会被精确到 15 位

除了加法运算,在其他所有数字运算中,JavaScript 会先将字符串转换为数字,再进行运算:

js
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

JavaScriptBigInt 变量用于存储太大而无法用普通 JavaScript 数字表示的大整数值,普通的JavaScript 整数最多只能精确到 15 位,超过15位的整数值可以考虑使用BigInt数据类型

如果需要创建 BigInt,可以在整数末尾添加 n,或调用 BigInt() 函数

js
let y = 9999999999999999999n;
let y = BigInt(1234567890123456789012345)
  • BigIntJavaScript 类型是 "bigint"

  • BigIntJavaScript 中的第二个数值数据类型(在 Number 之后)

  • 可用于 Number 的运算符也可用于 BigInt,但是不允许数据类型为BigIntNumber 的数值之间进行算术运算(类型转换会丢失信息)

  • 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 方法

js
Number(true)  // 返回1
Number(false) // 返回0
parseInt("10 years")  // 返回10
parseInt("10.33")  // 返回10
parseInt("years 10")  // 返回NaN

数字属性

数字属性只能作为 Number.MAX_VALUE 来访问,使用其他访问将返回undefined

属性描述
EPSILON1 和大于 1 的最小数之间的差
MAX_VALUEJavaScript 中可能的最大数
MIN_VALUEJavaScript 中可能的最小数
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)

从数组中取最大值:

js
let grade = [90,100,120,110];
console.log(Math.max.apply(null, grade));  // apply表示调用函数,用数组的形式传参

获取0-6之间的随机整数:

js
console.log(Math.floor(Math.random() * 6))

随机点名案例,可以控制点名范围:

js
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中创建日期通常有两种方式:

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

直接获取时间戳:

js
const date = Date.now();
console.log(date);  // 获取时间戳

标准时间和时间戳的类型转换

由标准时间转换为时间戳
js
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());
由时间戳转换为标准时间
js
const date = new Date("2024-5-24 19:31:20");
const timestamp = date.valueOf();
// 时间戳转换为标准时间
console.log(new Date(timestamp));

日期类型的综合使用

js
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());  // 获取日期的秒数

如果需要频繁的使用,一般封装成函数:

js
// 将标准时间转化为任何输入想要的形式
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, ...];

创建数组的声明可跨多行,通常在逗号的位置进行换行

可以引用数组名来访问完整的数组

在创建数组的时候,需要额外注意:

js
let arr = new Array(6);  // 这样创建的数组长度是6,每一个的值都是undefined,这是这种方式创造的缺陷

如果想要创造一个固定长度的数组,可以通过以下的方法:

js
// 方法一
let arr = [6];
// 方法二
let arr = Array.of(6);
  • 数组是一种特殊类型的对象,在 JavaScript 中对数组使用 typeof 类型判断会返回 object

  • JavaScript 不支持命名索引的数组,数组只能使用数字去索引

  • 通过数字索引修改内容,会导致原数组中的内容进行修改,因为两者使用同一个内存地址

  • 如果希望元素名为字符串(文本)则应该使用对象,如果希望元素名为数字则应该使用数组,对象和数组的内容可以是数字或者字符串

通过控制台查看数组,可以通过两种方式进行查看:

js
const array = [1, 2, 3];
console.log(array);    // 常规形式展现
console.table(array);  // 以表格的形式展现,更加清晰直观

constvar声明数组

通过const声明数组:const cars = ["Saab", "Volvo", "BMW"];

const 声明的数组不能重新赋值,但是可以通过索引进行元素的更改,因为修改的数组和原数组是共用内存地址的

js
const arr = [1, 2];
arr[1] = 3;
console.log(arr);  // 结果显示为[1, 3]

对于数组,如果修改下标内容的范围超出了原数组的最大下标,这样添加位置也能添加内容,只是原数组之间的内容用undefined进行填充:

js
let name = ["jlc"];
name[3] = "zhangshna";
consloe.log(name.length);  // 结果显示4
console.log(name[2]); // 结果显示undefined

一般情况下,都不会这样进行内容的追加,会通过其他数组方法进行追加

const 变量在声明时必须赋值,声明后赋值是不起作用的,但是用 var 声明的数组可以随时初始化

const 声明的数组具有块作用域,用 var 声明的数组没有块作用域

在程序中的任何位置都允许用 var 重新声明数组;不允许在同一作用域或同一块中将数组重新声明或重新赋值给 const;不允许在同一作用域或同一块中重新声明或重新赋值现有的 const 数组;允许在另一个作用域或另一个块中使用 const 重新声明数组

数组的比对:数组的比对不仅仅是比对其值,还要比对其内存地址(对象的比对也同理)

js
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共用同一个内存地址

数组的嵌套

可以在数组里面嵌套数组,在引用的时候需要两个方括号

js
let array = [["qqq"], ["www", "eee"]];
console.log(array[1][0])  // 结果显示www

一般以后都是通过对象进行嵌套

js
let lessons = [{name: "jlc", age: 24}, {name: "jlc1", age: 23}]
console.log(lessons[1].name)  // 结果显示jlc1

数组的类型检测和转换

类型检测

通过isArray()判断括号中的内容是否为数组

类型转换

数组转换为字符串

js
// 方法一
[1, 2, 3].toString()
// 方法二
String([1, 2, 3])
// 方法三
[1, 2, 3].join(",")  
// 结果都显示'1,2,3'

将字符串转换为数组

js
let str = "baidu";
// 方法一
str.split("")  // 结果显示:["b","a","i","d","u"]
// 方法二,使用from静态方法
Array.from(str); // 结果显示:["b","a","i","d","u"]

数组的追加

通过push方法

单个元素追加到数组中

js
let arr = ["1", "2"];
// 方法一:通过下标索引
arr[arr.length] = "3";
// 方法二:通过对象方法push
arr.push("3");
// 结果显示["1", "2", "3"]

一个数组追加到另外一个数组中

js
let arr1 = ["1", "2"];
let arr2 = ["3", "4"];
for(const value of arr2){
    arr1.push(value);
}
// 结果显示["1", "2", "3", "4"]
通过点语法
js
let arr1 = ["1", "2"];
let arr2 = ["3", "4"];
// 方法一
arr1 = [...arr1, ...arr2];
// 方法二:通过push方法
arr1.push(...arr2);

如果想要将DOM元素按照数组方式操作,可以通过点语法将其转换为数组

解构语法

对象和数组中使用解构语法是较为常见的

数组的解构语法:将右侧数组里面的值,平均赋值给左侧的变量;如果右边的数值元素比左边的多,可以使用点语法,将余下的数组元素都以数组的形式接受过来

js
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

点语法放到变量的位置,就是吸收;放到值的位置,就是打散

解构语法也可以用在字符串中:字符串转换为数组

js
const [...arr] = "jlc";
console.log(arr);   // 结果显示为:["j","l","c"]

解构语法在函数传值中的使用:

js
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);表示连接数组myGirlsmyBoys,将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()反转数组中元素的顺序

使用案例:

  • 通过索引数组重新赋值进行更改元素:

    js
    fruits[0] = "Kiwi"; // 把 fruits数组的第一个元素改为 "Kiwi"
  • 通过push()unshift()可以获取改变后数组的长度:

    js
    var = 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()方法的使用:

    js
    var = fruits = [1,2,3,4,'a'];
    console.log(fruits.join("*"));  // '1*2*3*4*a'
  • splice()方法进行数组元素的删除:

    js
    fruits.splice(0, 1); // 删除fruits数组中的第一个元素
    // fruits就是切除原先第一个元素的数组
  • splice()方法进行数组元素的替换:

    js
    var 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()复制函数方法的使用:

    js
    var 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

    拓展案例:

    js
    let 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方法的底层实现:

    js
    function 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
  • 通过一个比值函数对数字数组进行排序:

    js
    var 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()});  //返回的数组随机进行排序

    小案例:购物车内价格排序

    js
    let cart = [
        {name: "iphone", price: 12000},
        {name: "imac", price: 18000},
        {name: "ipad", price: 2000},
    ];
    cart = cart.sort(function(a, b){
        return b.price - a.price;
    });

返回数组中的最大最小值:

js
// 返回数组最大值:
function myArrayMax(arr) {
    return Math.max.apply(null, arr);
}

// 返回数组最小值:
function myArrayMin(arr) {
    return Math.min.apply(null, arr);
}

设计一个移动数组的函数:

js
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]

清空数组的常见方法:

js
let arr = [1, 2, 3];
// 方法一
arr = [];  // 重新开辟了一块内存空间,存放空数组,原先的内存空间还是存在的
// 方法二
arr.length = 0; // 将原先的内存数据中的数值元素进行一个清空,清除数组更加彻底
// 方法三
arr.splice(0);  // 将原数组的元素全部拿走,原数组就清除了
// 方法四
while(arr.pop()){}  // 循环一次数组中所有元素的弹出

数组循环

数组的循环有常见的for循环,for of循环和for in循环

js
// 将数组字典内的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)

js
// 将每个元素竖直排序
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迭代器

js
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
js
// 迭代方法遍历数组中的元素,和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)

js
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,就只会打印第一组(一次)的对应的数据
});
js
// 检查所有学生的成绩是否都及格
const user = [
    { name: "张三", chengjji: 88 },
    { name: "李四", chengjji: 92 },
    { name: "王五", chengjji: 58 },
];
const res = user.every(function(item){
    retuen item.chengji >= 60;
});
console.log(res ? "全部及格" : "有同学没有及格");
js
// 检查所有数组值是否大于 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)

js
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相反
});
js
// 检查是否有数组值是否大于 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)

js
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"]
js
// 将成绩为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 }]
js
// 将原始数组中值大于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)

js
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;
});
js
// 将每个元素都乘以2
var numbers1 = [45, 4, 9, 16, 25];
function myFunction(value) {
    return value * 2;
}
var numbers2 = numbers1.map(myFunction);  // 对新数组进行映射改变,原数组是没有改变的
js
// 数组对象中增加一个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)

js
// 语法介绍
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为第五个值
js
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]
js
// 数组左到右计算数组中所有数字的总和
var numbers1 = [45, 4, 9, 16, 25];
function myFunction(total, value) {
  return total + value;
}
var sum = numbers1.(myFunction);
js
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)

js
// 从数组右到左计算数组中所有数字的总和
var numbers1 = [45, 4, 9, 16, 25];
var sum = numbers1.reduceRight(myFunction);

function myFunction(total, value) {
  return total + value;
}

集合

Set

集合类型Set,数组/对象里面可以放多个数据,集合类型也是,但是集合类型不能放重复的数据

基本语法
js
// 声明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"}

常用的集合方法:

js
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类型转换成数组:

js
let set = new Set(["jlc", "baidu"]);
// 方法一:通过构造函数进行转换
console.log(Array.from(set));  // 结果显示["jlc", "baidu"]
// 方法二:通过点语法进行转换
console.log([...set]);  // 结果显示["jlc", "baidu"]

实用的小案例:

js
// 将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类型
js
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
处理并集,交集和差集
js
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要求必须是引用类型(对象,数组)

js
let set = new WeakSet("jlc"); // 报错
let set = new WeakSet(["jlc", "baidu"]); // 报错
// 但是可以先创建,再添加
let set = new WeakSet();
set.add(["jlc", "baidu"]);
弱引用特性

强引用的垃圾回收机制:

js
let hd = {name: 'jlc'};
let ed = hd;    // 内存地址中的{name: 'jlc'}被两个地方引用了
hd = null;      // 将hd与内存内容的引用连接断开,引用次数减一,内存地址中的{name: 'jlc'}被一个地方引用了
console.log(ed);  // 结果还是显示{name: 'jlc'}
ed = null;  // 如果ed也不引用了,那么该内存地址中的内容就变成了垃圾,需要进行垃圾回收,释放内存

系统会自动进行检测,如果某内存地址的内容,没有被任何变量进行引用,就会进行垃圾回收

WeakSet的弱引用特性:

js
let hd = {name: 'jlc'};
let ed = hd;   // 强引用
let set = new WeakSet();
set.add(hd);  // 弱引用,系统不会在引用连接计数器中加一
// 将引用连接断开
hd = null;
ed = null;
// 强引用断完后,内存地址就会被垃圾回收,那么set就读不出内容,但是它不知道,它认为里面是还有数据的

因为存在弱引用的特性,所以js中规定WeakSet类型是不能进行循环遍历操作的,因此WeakSet类型相比与Set类型只能使用add,hasdelete方法,其他方法都是不能使用的(这也是弱引用类型的方便)

Map

Map类型可以将我们的对象,函数,标准类型,字符串,数值作为键名,在以往的对象中,只能将字符串作为键名(常规数组中的键名0是字符串'0'):

js
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类型中,什么类型都可以作为键名:

js
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}
js
// 链式追加元素
let map = new Map();
map.set("name", "jlc").set("age", 24);  // Map(2) {"name" => "jlc", "age" => 24}
增删改查操作
js
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类型
js
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类型转换为数组类型

js
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类型中的键,只能是引用类型(对象,数组)

js
let map = new WeakMap();
map.set("name", "jlc");  // 报错
map.set([], "jlc");  // 不报错   key为[],value为"jlc"

WeakMap相较于Map类型,只能使用set,deletehas方法,其他Map类型有的属性WeakMap类型都是用不了的,同时WeakMap也是不可迭代的(不可遍历循环的)

弱引用特性
js
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 字符串是引号中的零个或多个字符

可以在字符串中使用引号,只要不匹配围绕字符串的引号即可(与最外层包裹的引号不同即可),外面双里面单,外面单里面双,如果一定要内外使用一样的引号,可以使用转义字符来分隔内部的引号

代码结果描述
\''单引号
\""双引号
\\\反斜杠
js
var x = "中国是瓷器的故乡,因此 china 与\"China(中国)\"同名。"
console.log(x)  // 中国是瓷器的故乡,因此 china 与"China(中国)"同名。

当然,转义字符(\)也可用于在字符串中插入其他特殊字符:

代码结果
\b退格键
\f换页
\n新行
\r回车
\t水平制表符,相当于一个tab
\v垂直制表符

html特性,对于转义字符,无论在字符串中加入多上个同类型的转义字符,在控制台中打印可以正常显示,但是在网页中只能显示同类型的一个转义字符效果,如果想要在网页中正常显示,需要加入&nbsp;

js
let web = "ba\t\t\t&nbsp;&nbsp;&nbsp;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 编码,aunicode编码为97
split()将字符串转换为数组,其方法的参数表示用什么进行分割,如txt.split(","); 表示用逗号进行分隔,如果省略分隔符,被返回的数组将包含index[0] 中的整个字符串(将整个字符串放到数组的第一个位置)
repeat()重复输出,效果类似于字符串相乘:"*".repeat(3)

所有字符串方法都会返回新字符串,它们不会修改原始字符串,因为字符串是不可变的,具体来说:字符串不能被更改,只能替换

不建议对字符串进行属性访问(str[int]),因为如果找不到字符(输入的索引超过了字符串的长度),返回的结果是 undefined,而 charAt() 返回空字符串;它是只读的。str[0] = "A" 不会产生错误(但也不会生效),如果想要进行属性访问,可以先把它转换为数组

js
// 手机号码模糊处理
function phone(mobile, len = 3){
    return String(mobile).slice(0, len * -1) + "*".repeat(len);
}
console.log(phone(13456670000, 6))
// 结果显示为:13456******

字符串字面量

JavaScript 中的字面量字符串是一种方便的字符串语法,允许你在字符串中嵌入表达式和变量

模板字面量是用反引号(`)分隔的字面量,允许多行字符串、带嵌入表达式的字符串插值和一种叫带标签的模板的特殊结构

模板字符串中可以同时使用单引号和双引号:

js
let text = `He's often called "Runoob"`;
// 显示的结果为:He's often called "Runoob"

模板字符串还支持多行文本,而无需使用特殊的转义字符,``中的内容形式是怎么样的,在控制台中就怎么样:

js
const multiLineText = `
  This is
  a multi-line
  text.
`;

通过字面量连接字符串:

js
let name = 'jlc';
let age = '24';
console.log(`我的名字是${name},年龄为${age}岁`)

字面量${}中不仅可以调用变量,也可以调用函数,只要这个函数有返回值即可

字面量中可以在添加字面量

字符串插值

模板字面量提供了一种将变量和表达式插入字符串的简单方法,该方法称为字符串插值

具体语法为:${...},用真实值自动替换变量称为字符串插值

js
let firstName = "Bill";
let lastName = "Gates";
let text = `Welcome ${firstName}, ${lastName}!`;
//结果为:Welcome Bill, Gates!

字符串插值在HTML中的使用:

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>`;

标签模板

可以将标签中的变量拿出来,进行二次处理

js
let name = "jlc";
let age = "24";
function tag(strings, ...vars){
    console.log(vars);
    console.log(strings);
}
console.log(tag`我的名字是${name},年龄为${age}。`);
// 结果显示为
{"jlc", "24"}
{"我的名字是", ",年龄为", "。"}  // 如果没有默认字符串,其前后也会加上空:{"","",""},字符串的数量一定是大于变量数量的

字符串类型转换

字符串转换为数值
js
let text = "9"
// 隐式转换:
console.log(text * 1)  // 结果显示9
// 通过构造函数进行转换
console.log(Number(text))  //结果显示9
数值转换为字符串
js
const num = 66
// 隐式转换
const srt = num + "";
// 通过构造函数进行转换
String(num)
字符串转换为数组
js
const web = "baidu"
// 通过字符串方法进行转换
console.log(web.split(""));    // ['b', 'a', 'i', 'd', 'u']
数组转换为字符串
js
const array = ["jlc", "24"];
// 通过join方法将数组转换为字符串
console.log(array.join(""));     // 'jlc24'
// 通过构造函数进行转换
console.log(array.toString());  // 默认是通过使用逗号进行连接的   'jlc,24'

对象

为什么要进行面向对象的学习:使用函数编程会导致出现复杂的代码块,函数编程会暴露到全局,可能带来一系列问题,函数编程需要改良成面向对象的编程,将函数作为对象中的一个方法,将关联性的业务封装起来,变成一个对象

对象(Object)字面量定义一个对象,对象是一个引用类型,赋值的时候给的是内存地址(共用一个内存区域)

对象也是变量,但是对象可以包含很多值,在JavaScript中,几乎“所有事物”都是对象,所有JavaScript值,除了原始值(原始值指的是没有属性或方法的值),都是对象

JavaScript对象中的名称:值对被称为属性,属性:属性值

对象方法:方法是在对象上执行的动作,方法以函数定义被存储在属性中

js
var person = {
    firstName: "Bill",
    lastName : "Gates",
    id       : 678,
    fullName : function() {
    	return this.firstName + " " + this.lastName;
    }
};

在函数定义中,this 引用该函数的“拥有者”

在上面的例子中,this 指的是“拥有” fullName 函数的 person 对象,即this.firstName 的意思是 person 对象的 firstName 属性

访问对象的属性

有两种方式可以访问对象的属性:

js
// 方式一:objectName.propertyName,如上面的对象所示:
person.lastName;   // 推荐使用

// 方式二:objectName["propertyName"],如上面的对象所示:
person["lastName"];

// 一般是在循环遍历的时候会经常使用方式二:
for(const key in person){
    console.log(person[key]);  // key是一个字符串,将所有对象中的值打印出来:Bill等
}

访问对象的方法

可以通过以下方式访问对象中的方法:

js
// objectName.methodName()
name = person.fullName();
// 如果您不加()访问 fullName 方法,则将返回整个函数定义内容的字符串

通过关键词new来声明JavaScript变量,该变量会被创建为对象,但是不建议把字符串、数值和布尔值声明为对象,他们会增加代码的复杂性并降低执行速度

JavaScript 对象是无法进行对比的,比较两个 JavaScript 对象(不管是==比较还是===比较),将始终返回 false

动态管理对象的属性

对象的属性和方法的动态添加
js
// 给person对象添加一个age属性,其属性值为18
person.age = 18;  
person["age"] = 18;

// 给person对象添加一个get方法
person.get = function(){  
    return `${this.lastName}的年龄是${this.age}`
}
对象的属性和方法的动态删除
js
delete person.age;   // 删除person对象的age属性

属性的检测

判断对象中是否有某个属性:

方法描述
hd.hasOwnProperty("age")判断hd对象中是否存在age属性(只看自己是否有该属性)

原型链属性:对象的原型,可以理解为对象的父级,对象可以拿(继承)父级的属性进行使用的

对于数组和对象来说,length属性是存在数组对象中的,还有一个参数__proto__表示原型(理解为该数组的父级,在父级里面会有很多的属性)

hasOwnProperty方法是不能看到父级的属性的,如果需要看到当前自己和父级的属性,可以通过"属性" in arr方式进行查找,该属性在数组自己或父级中,则返回true,反之,返回false

判断对象的属性是否存在:

js
// 将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

对象属性参与计算

对象的属性是通过计算之后产生的,有时候需要对数据结构进行处理,来改变其属性

js
let hd = {};
let title = "name";
hd[title] = "jlc";
console.log(hd.name);   // jlc

有时候为了改变从后台返回的数据结构的形式,我们需要对对象属性进行计算,动态的改变属性:

js
// 后台拿到的数据的形式:
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()方法用于将两个对象进行合并

js
let hd = Object.assign({ a; 1 }, { b: 2 });
console.log(hd);  // {a: 1, b: 2}

获取对象属性和值的方法

js
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]]  得到的是二维数组的结构

对象的循环遍历操作

js
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
}

展开语法在对象中的使用

展开语法是对数组和对象的批量处理

js
let user = {name: "jlc", age: "24"};
let hd = { ...user, lang: "zh"};
console.log(hd);  // {name: "jlc", age: "24", lang: "zh"}

通过展开语法进行内容的修改:

js
function upload(params){
    let config = {
        type: "*.jpeg,*.png",
        size: 10000
    };
    config = { ...config, ...params};  // 属性一样,后面的属性值会覆盖前面的属性值
    console.log(config);
}
// 不传,就使用默认的;传元素,就使用你传递的内容,也可以传入新的属性和属性值
upload({size: 99});   // {type: "*.jpeg,*.png", size: 99}

解构语法特性

解构语法特性是对元素的结构进行分解处理,将结构打散后赋值给变量

js
let user = {name: "jlc", age: "24"};
let {name: n, age: age} = user;  // 对象的解构,解构的变量不用获取全部,可以只获取一部分
console.log(n);  // jlc
// 解构后的名字可以和对象的属性值一样,那么可以进行简写
let {name, age} = user;  // 对象解构的简写
console.log(name);       // jlc

解构的思路在模块化编程中的使用非常多

只要数据是一个对象,都可以进行解构处理,如果函数的返回值是对象,也可以进行解构处理:

js
function hd(){
    return {name: "jlc", age: "24"};
}
let {name, age} = hd();
console.log(name);  // jlc

在函数传递参数的时候,也可以使用解构特性:一般是传递数据库中拿过来的数据

js
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进行声明,不然会报错:

js
let {name, age} = {name: "jlc", age: "24"};
// 非严格模式下可以简写为:
({name, age} = {name: "jlc", age: "24"});
多层对象的结构

如果对象中包含对象,那么这个对象被称为多层对象,对于多层对象可以进行解构

js
let hd = {
    name: "jlc",
    lesson: {
        title: "js"
    }
};
let {name, lesson: {title}} = hd;
console.log(name, title);  // jlc js
解构的默认值

当解构后的变量,其值不存在时,那么如果设置了默认值,就会显示默认值,如果值存在,就会显示其值

js
let user = {name: "jlc", age: "24"};
let {name, age, sex="男"} = user;
console.log(name, age, sex);   // jlc 24 男   如果没有加默认值,那个sex显示undefined

对象的复制

传统情况的对象赋值,传递的是内存地址

js
let hd = { name: "jlc" };
let cm = hd;  // 将对象hd的内存地址给cm
cm.name = "jlc123";
console.log(hd);  // { name: "jlc123" }  如果改变cm中的属性值,同时会改变原对象hd中的值,公用地址
浅拷贝

不能将原始对象中深层次(嵌套)的对象进行复制给另外一个新的对象

那么正常情况下对象要怎么进行复制呢?

js
let hd = { name: "jlc" };
let cm = {
    name: hd.name   // 将hd中的name属性的值取出来,作为cm对象中name属性的值
};
// 这样hd和cm对象就是两个不同的内存数据,开辟了两块不同的内存空间,对象cm的属性值改变,不会影响hd的属性值

如果被复制对象的属性特别多,我们需要用循环进行复制:(一般用于复制的时候想要内容发生一些改变时使用的,如将复制的内容都加上一个后缀)

js
let hd = { name: "jlc", age: 24};
let obj = {};
for(const key in hd){
    obj[key] = hd[key];
}
// 此时开辟的是两块内存地址,但是内容是一样的,修改obj内部的值对hd是没有影响的

除了循环的方式进行复制,也可以通过提供的assign()方法进行复制:

js
let hd = { name: "jlc", age: 24};
let obj = Object.assign({}, hd);  // 加对象hd中的值压到新对象中

通过展开语法进行复制:

js
let hd = { name: "jlc", age: 24};
let obj = {...hd};  // 将hd对象的结构拿过来,放到obj对象新开辟的空间中

如果想要复制的时候,新对象发生一些改变,可以通过使用for循环进行复制,否则建议使用后面两种方法

深拷贝

可以将原始对象中深层次(嵌套)的对象进行复制给另外一个新的对象

通过浅拷贝,对于对象中的对象,如果采用浅拷贝,新对象和原对象对于对象中的对象元素还是共用一个内存地址的

我们可以通过深层次的复制:深拷贝就是一层一层的处理,通过递归算法

js
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);

创建对象的封装和抽象

在一类对象中,其往往处理的内容都是一样的,这类对象中可能都有类似的方法,我们没有必要每次创建这类对象的时候反复的复制这些方法代码,我们可以通过以下的方法进行简化

工厂函数

工厂是生成我们的对象的,将方法动作统一生成我们的对象

js
// 工厂函数
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-姓名
  • 对于工厂函数中对象的方法进行修改后,同时会影响所有使用该工厂函数创建的对象

  • 使用工厂创建对象,可以使我们的代码更加规范,可以对我们的统一动作进行定制

构造函数

定义构造函数要求首字母大写,构造函数可以在创建的时候传递参数,来为我们的对象做初始值

js
// 构造函数,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中,所有的数据类型都是可以通过构造函数进行创建的(但是对于大多数的类型都不建议通过构造函数进行创建,一般是直接创建即可)

js
// 通过构造函数创建对象类型
let obj = new Object();

// 通过构造函数创建数值类型
let num = new Number(1);
// 此时num是一个对象,如果想要取到值可以通过:num.valueOf()为数值类型
// 当然直接通过运算后,会直接变成数值:num + 3    结果为4

// 通过构造函数创建字符串
let str = new String('jlc');
// 此时str是一个对象,如果想要取到值可以通过:str.valueOf()为字符串类型

所以说,我们为什么可以去使用字符串的方法,就是通过构造函数创建后,其实质上就是一个对象,构造函数中的一些方法可以去使用,当然有些方法可能不在构造函数中,可能在原型的父级中

抽象

被封装的对象,其属性还是可以被外部进行访问到的,还没有实现到抽象

抽象可以理解为一个手机的内部有很多复杂的逻辑,复杂逻辑的功能实现都被封装到手机的内部了,只给外部提供了一些案件的操作(这个逻辑称为抽象:把一些属性和方法封装到内部,不让外部进行访问)

js
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也是改变不了的

对象的属性特征

我们可以对一个对象属性的一些操作进行控制,比如添加属性,删除属性等等,但是更加深度的控制还不能实现,如,不能设置让一个属性不能被修改,不能被删除等等

js
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))

更改对象的的属性,如果属性存在,就更改这个属性,如果这个属性不存在,那么就创建这个属性(相当于新增属性)

js
// 对象还是之前的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用于对对象属性的设置

js
// 禁止向对象中添加属性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)

访问器

一个对象的属性值在默认情况下是可以在外界进行修改的,这就导致这个对象会很不稳定,外界访问对象需要有一个把关的环节,这个环节就是访问器,访问器用于检测修改内容的质量(数据的安全过滤)

js
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
使用访问器伪造属性

使用访问器伪造属性,这样我们在获取的时候就会比较方便

js
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;  // 在严格模式下会报错,普通模式下不生效
使用访问器批量设置属性
js
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,令牌的处理可以通过访问器来进行控制,一般获取到令牌后,需要存储到本地

js
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  同时在网络当中也能看到这些数据
// 这些数据没有放到对象中,只是在我们的本地存储中
冲突

访问器控制属性和使用原始方法控制属性的冲突

js
const user = {
    name: "jlc",
    age: 24,
    set name(value){
        console.log(value + "姓名");
    }
};
user.name = "JLC";  // 访问器的优先级高,结果显示:jlc姓名
console.log(user);  // 同时打印变量丢失了name的属性:{age: 24}

普通属性的优先级比访问器的优先级低

如果想要将name进行保存,可以将其定义为一个私有的属性

js
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
使用类定义访问器

类可以理解为构造函数的一个简写的形式,通过类的形式(语法糖的形式)来定义访问器:

js
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框架的数据绑定等都会使用到对象代理的概念

代理的基本语法
js
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"}  获取到整个对象的属性

代理不仅可以对对象进行代理,也可以对函数进行代理:

js
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)打印的就是{},括号中前半部分传递的是什么,打印的就是什么

通过代理来查看函数阶乘执行的时间:

js
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')
    }
})

把函数通过代理,来作为中间桥梁进行工作

代理对数组进行的拦截操作:我们可以通过代理对获取的数组元素的某个内容的长度进行截断处理操作

js
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双向数据绑定中使用代理拦截的例子:

html
<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>

对于绑定同一个模型的文本展示区域,其内容会同步的发生变化,在其底层逻辑中,我们可以理解为:当任何一个表单更改了数据之后,实际上是更改数据容器中的数据,数据容器中的数据发生更改后,会要求页面进行重新的渲染,同步的更改其他的两个容器的数据,这个数据容器就是我们所说的代理

使用代理来进行表单验证:

一般是定义一个类,在类中写一些方法来进行提供一些功能的验证:

html
<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 提供一种布尔数据类型,它只接受值truefalse

所有具有“真实”值的即为 True

所有不具有“真实”值的即为 False (0,-0," ",undefinednullNaNfalse)的布尔值为false

js
let bool = true;
console.log(typeof bool)  // 结果打印boolean

空数组/对象转换成布尔类型,其结果为真console.log(Boolean([])) // 结果为true

总结:

  • 数值类型,除了0之外都是真,0是假
  • 字符串类型,除了空字符串类型都为真,空字符串为假
  • 对于引用类型,任何数组和对象都为真

布尔类型的显示转换

js
let num = 0;
// 通过!进行转换,!有两部分的功能:1,将其转换成布尔类型;2.将布尔类型取反
num = !!num;
console.log(num);  // 结果为false  num为布尔类型
// 通过构造函数进行转换
Boolean(num)

字符串和数组,对象转换为布尔类型同理

js
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 语法规则和相关函数

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格式是字符串类型)转化为其所需要的格式)

js
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格式

js
let arr = ["jlc", "111"]
let json = JSON.stringify(arr);
console.log(json);   // 结构显示为:["0": "jlc", "1": "111"]

自定义返回JSON格式

我们可以在对象中自定义编写要返回的Json格式:

js
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格式

Released under the MIT License.