08_JavaScript的内置类

166 阅读32分钟

JavaScript的内置类

1.包装类

JavaScript的原始类型并非对象类型,所以从理论上来说,它们是没有办法获取属性或者调用方法的

但是在开发中,我们经常会通过基本数据类型去调用对应的属性和方法

    var name = "Hello World"
    var height = 1.8888888

    // function String(str) {
    //   this.str = str
    //   this.length = 11
    //   this.split = function() {
    //   }
    // }

    // 在调用原始类型的属性或者方法时, 内部的操作 name = new String(name)
    console.log(name.length)
    console.log(name.split(" "))
    console.log(height.toFixed(2))

    // var obj = {
    //   name: "kobe",
    //   running: function() {
        
    //   }
    // }
    // obj.running()

    // 原始类型默认也是可以手动的创建对象(没有必要这样来做)
    var name1 = new String("Hello World")
    console.log(typeof name, typeof name1)


    // Number/String

原始类型是简单的值,默认并不能调用属性和方法; 因此JavaScript为了可以使其可以获取属性和调用方法,对其封装了对应的包装类型

默认情况,当我们调用一个原始类型的属性或者方法时,会进行如下操作:

  • 根据原始值创建一个原始类型对应的包装类型对象
  • 调用对应的属性或者方法,返回一个新的值
  • 方法和属性使用完毕后,销毁创建的包装类对象
  • 通常JavaScript引擎会进行很多的优化,它可以跳过创建包装类的过程在内部直接完成属性的获取或者方法的调用

常见的包装类型有:String、Number、Boolean、Symbol、BigInt类型

  1. null、undefined没有任何的方法和属性,因为null和>undefined没有对应的包装类

  2. 虽然规范上要求调用基本数据类型的属性和方法的时候,需要先转换为对应的包装类

但是有些时候,JS引擎内部可以直接拿到对象的属性或操作>对象的方法,而不需要转换为对应的包装类

因此JS引擎处于优化的角度,其可以跳过创建包装类的过程>在内部直接完成属性的获取或者方法的调用

2. Number

Number类型有一个对应的数字包装类型 Number

  // Number构造函数 -> window.Number
    // var num = 123 // new Number(num)

    // 类属性
    // Number中本身是有自己的属性
    // 这种直接定义在构造函数上的方法和属性被称之为类方法和类属性(也可以被称之为静态方法和静态属性)
    // 最大的正Number值
    console.log(Number.MAX_VALUE)
    // 最小的正Number值
    console.log(Number.MIN_VALUE)
    // integer: 整数
    // 最大的不会失精的整数 --- 正数
    console.log(Number.MAX_SAFE_INTEGER)
    // 最小的不会失精的整数 --- 负数
    console.log(Number.MIN_SAFE_INTEGER)

    // 对象的方法 实例方法

    // toString方法也可以传递一个base参数,表示转换为base进制表示的数字所对应的字符串
    // 不传默认值为10,即按照十进制进行转换
    // base的取值范围为 [2, 36]
    // toString(base)

    var num = 1000
    // toString可以将数字转换为对应的字符串
    console.log(num.toString(), typeof num.toString()) //  => 1000 string
    // 注意: 因为转换后的结果为字符串,所以没有使用0b之类的进制前缀开头
    // 但是如果是以数字形式表示的话,需要在开头加上对应的进制前缀
    console.log(num.toString(2)) // => 1111101000
    console.log(num.toString(8)) // => 1750
    console.log(num.toString(16)) // => 3e8

    // 因为数字后边加上点 会被JS引擎解析为浮点数
    // 所以如果直接使用整数类型的数字字面量去调用toString方法的时候
    // 需要使用.. 或 使用括号将数字字面量进行包裹
    // 以便于告诉 JS引擎之前的数字是整数 不是浮点数
    console.log(123..toString(2)) //1111011

    // toFixed的使用(重要)

    // toFixed(digits),格式化一个数字,保留digits位的小数
    // digits的范围是0到20(包含)之间,默认值为0

    var pi = 3.1415926

    // toFixed方法默认是保留成整数
    console.log(pi.toFixed()) // => 3
    // toFixed方法会四舍五入
    console.log(pi.toFixed(3)) // => 3.142
    // toFixed转换后的结果为字符串类型
    console.log(typeof pi.toFixed(2)) // => string

    // 类的方法
    // parseInt
    // parseFloat
    // 整数: 123
    // 浮点数: 小数 123.321
    var num1 = "123.521"
    console.log(Number(num1).toFixed(0)) // =>124
    // parseInt --- 将字符串转换为整数形式表示的Number --- 转换后结果为Number类型 --- 不会四舍五入
    console.log(Number.parseInt(num1)) // =>123
    // parseFloat --- 将字符串转换为浮点数形式表示的Number --- 转换后结果为Number类型 --- 不会四舍五入
    console.log(Number.parseFloat(num1)) // =>123.521

    // window对象上面
    console.log(parseInt(num1)) // =>123
    console.log(parseFloat(num1)) //  =>123.521

    // function HYNumber() {

    // }
    // HYNumber.parseInt2 = function() {

    // }
    // window.parseInt2 = HYNumber.parseInt2
    // console.log(window.parseInt2 === HYNumber.parseInt2)
    // parseInt方法 和 parseFloat方法 不仅存在于Number对象上,同时存在于window对象上
    console.log(parseInt === Number.parseInt)// => true
    console.log(parseFloat === Number.parseFloat) // => true
    // 以下转换方式以parseInt为例,parseFloat同理
    console.log(parseInt(23)) // => 如果参数是Number类型,不进行转换,直接返回
    console.log(parseInt(false)) // => 如果参数不是Number类型或String类型值,直接返回NaN

    // 如果参数类型为数字开头字符串,会先从头开始尽可能的截取成合法的数字后再进行换行
    // 这是parseInt和parseFloat于Number方法最大的区别
    console.log(parseInt('123a234')) // => 123
    console.log(parseInt('123.')) // => 123
    console.log(parseFloat('123.223a24213')) // => 123.223
    console.log(parseFloat('123.')) // => 123

    // 如果参数不是以数字开头的字符串,直接返回NaN
    console.log(parseInt('a23')) // => NaN

3. Math

除了Number类可以对数字进行处理之外,JavaScript还提供了一个Math对象

Math是一个内置对象(不是一个构造函数),它拥有一些数学常数属性和数学函数方法

常用属性说明示例结果
PI圆周率,约等于 3.14159Math.PI3.141582653589796
常用方法说明
Math.floor向下舍入取整
Math.ceil向上舍入取整
Math.round四舍五入取整
Math.random生成0~1的随机数(包含0,不包含1) 生成的随机数可以是整数,也可以是小数
Math.pow(x, y)返回x的y次幂
    // console.log(typeof Number) // function
    // var num = new Number()

    // Math -> 对象
    // window/obj/console
    // console.log(typeof Math)
    // var math = new Math()

    // Math对象的属性
    // console.log(Math.PI)

    // Math对象的方法
    // var num = 3.55
    // console.log(Math.floor(num)) // 3
    // console.log(Math.ceil(num)) // 4
    // console.log(Math.round(num)) // 4

    // 另外方法
    // random: 随机生成 [0, 1)
    console.log(Math.random())
    // 需求: [5~50)的随机数
    // [a, b)
    // y = a
    // x = b - a
    // Math.floor(Math.random() * x) + y
    for (var i = 0; i < 1000; i++) {
      var randomNum = Math.floor(Math.random() * 45) + 5
      console.log(randomNum)
    }

    // Math.pow(x, y)
    console.log(Math.pow(2, 4))

4. String

4.1 String类的补充(一)- 基本使用

在开发中,我们经常需要对字符串进行各种各样的操作,String类提供给了我们对应的属性和方法

常见属性说明
length获取字符串的长度 虽然JS没有字符类型,但是字符串在内存中进行解析的时候,会作为字符数组进行解析
 var message = "Hello World"
    // 1.属性: length
    // console.log(message.length)

    // 2.访问字符串中某个位置的字符
    // console.log(message[4])
    // console.log(message.charAt(4))
    // console.log(message[20])
    // console.log(message.charAt(20))
    // 3.字符串的遍历
    // for普通遍历
    for (var i = 0; i < message.length; i++) {
       console.log(message[i])
    }
    // for..of的遍历 -> 迭代器
    // 目前可迭代对象: 字符串/数组
    // 对象是不支持for..of
    // String对象内部是将字符串变成了一个可迭代对象
    for (var char of message) {
       console.log(char)
    }
    // for-in遍历
    for (var char in message) {
      // 因为for-in遍历在遍历的时候,在遍历自身属性和方法的时候,还会遍历原型上的属性和方法
      // 所以需要使用hasOwnProperty方法来判断某一个属性key是不是被遍历对象自身上的属性和方法后再进行打印
      if (message.hasOwnProperty(char)) {
         console.log(message[char])
      }
    }
    // 三种for循环遍历的区别
    var user = { 
      name: 'Klaus', 
      running() {
      } 
    }
    var proto = Object.getPrototypeOf(user)
    proto.age = 24
    proto.studying = () => {}

    // Object.keys --- 只遍历自身属性和方法, 不遍历原型上的属性和方法
    // console.log(Object.keys(user)) // => ['name', 'running']

    var keys = Object.keys(user);
    for (var i = 0; i < keys.length; i++ ) {
      console.log(keys[i]);
    }

    // for-in 遍历即会遍历自身的属性和方法,也会遍历原型上的属性和方法
    for (var key in user) {
      // console.log(key)
      /*
        =>
          name
          running
          age
          studying
      */
    }

    // ------------------------------------------

    const arr = ['Alex', 'Steven', 'Klaus']

    // 数组也重写了Symbol.iterable属性,所以数组也可以使用for-of遍历
    // 遍历出来的属性有哪些,取决于Symbol.iterable接口的实现
    // 在数组中,就是每一个数组元素值
    for (var value of arr) {
      // console.log(value)
      /*
        =>
        Alex
        Steven
        Klaus
      */
    }

4.2 String类的补充(二) - 修改字符串

JavaScript中的字符串具有不可变性,也就是说字符串在定义后是不可以修改的

所以下面的操作是没有任何意义的

    var message = "Hello World"

    // 1.严格的修改字符串, 之前的字符串内部修改掉
    // message[2] = "a"
    // console.log(message)

所以在JS中很多改变字符串的操作都是生成了一个新的字符串,而不会修改原本的那个字符串

    // String两个方法:(重要)
    // toUpperCase: 将所有的字符变成大写
    // toLowerCase: 将所有的字符变成小写
    
    // var message1 = message.toUpperCase()
    // console.log(message) // => Hello World
    // console.log("message1:", message1) // => message1: HELLO WORLD
    // message = message.toUpperCase() 
    
    var message2 = message.toLowerCase()
    console.log(message2) // => hello world

4.3 String类的补充(三) - 查找字符串

在开发中我们经常会在一个字符串中查找或者获取另外一个字符串,String提供了如下方法:

    var message = "my name is wjl."
    var name = "wjl"

    // 判断一个字符串中是否有另外一个字符串
    // 1.indexOf(searchString, fromIndex)
    //从searchString开始,查找fromIndex的索引;
    // fromIndex的默认值为0
    /*
      index:
        情况一: 搜索到, 搜索字符串所在索引位置
        情况二: 没有搜索到, 返回-1
    */
     var index = message.indexOf(name)
     if (message.indexOf(name) !== -1) {
       console.log("message中包含name")
     } else {
       console.log("message不包含name")
     }
     
    // lastIndexOf(message, startIndex) --- 多个符合条件的值 之后找到最后一个
    // lastIndexOf的使用方式和indexOf是完全一致的
    // 唯一的区别是,lastIndexOf的索引值是从后往前的
    // 也就是意味着lastIndexOf的索引值是从后往前计算的 
    console.log(message.lastIndexOf('wjl')) // => 11 
    console.log(message.lastIndexOf('wjl', 11)) // => 11
    console.log(message.lastIndexOf('wjl', 10)) // => -1

4.4 String类的补充(四)- 开头和结尾

但比较indexOf和lastIndexOf的语义化并不是十分明显,所以在ES6中提供了更为与优化的查找方式

      var message = "my name is wjl."
      var name = "wjl"

    // 2.includes: ES6中新增一个方法, 就是用来判断包含关系
    if (message.includes(name)) {
      console.log("message中包含name") // =>message中包含name
    }
   
    // 3.startsWith: 是否以xxx开头
    if (message.startsWith("my")) {
      console.log("message以my开头") // => message以my开头
    }

    // 4.endsWith: 是否以xxx结束
    if (message.endsWith("why")) {
      console.log("message以why结尾")  // => message以wjl结尾
    }
    
    // 5.replace 替换字符串
    var newMessage = message.replace("wjl", "kobe")
    console.log(message) // => my name is wjl
    console.log(newMessage)  // => my name is kobe
    var newName = "kobe"
    var newMessage = message.replace("wjl", function() {
      return newName.toUpperCase()
    })
    console.log(newMessage) // => my name is KOBE

4.5 String类的补充(五) - 获取子字符串

开发中推荐使用slice方法。

Snipaste_2022-11-28_09-33-27.png

 var message = "Hello World"

    // 获取子字符串
    // slice(start, end) => 截取[start, index)对应的子串
    // 因为字符串的不可变性,slice会返回一个新的字符串,而不会去修改原本的字符串,所以slice方法是一个纯函数
    // 如果不写end表示 从start开始直接截取后面全部字符串
    // end支持负数,-1表示最后一个字符,-2表示倒数第二个字符,。。。依次类推
    console.log(message.slice(3, 7)) //lo W
    console.log(message.slice(3, -1)) //lo Worl
    console.log(message.slice(3)) //lo World

    // substr
    // substr 是标准附录中的方法,浏览器可以支持 也可以不进行支持
    // 虽然绝大多数浏览器都支持这个方法,但是并不推荐使用
    // substr(start, length)
    console.log(message.substr(3, 7)) //lo Worl
    // substring(start, end) => 截取索引为[start, end)的子串
    console.log(message.substring(3, 7)) //lo W

    // substring第二个参数不传,表示截取到字符串的末尾
    console.log(message.substring(3)) //lo World

    // substring第二个参数不支持负值
    // substring(start, end) 如果end为负值 为自动进行容错处理
    // 转换为 substring(0, start + 1)
    console.log(message.substring(3, -1)) //Hel

String类的补充(六) - 其他方法

    var str1 = "Hello"
    var str2 = "World"
    var str3 = "kobe"

    // 1.字符串拼接
    // +
    // var newString = str1 + str2 + str3
    // console.log(newString) // => HelloWorldkobe
    // concat方法: 链式调用
    // concat方法会将拼接后的字符串作为函数的返回值进行返回
    var newString2 = str1.concat(str2).concat(str3)
    var newString3 = str1.concat(str2, str3, "abc", "cba")
    console.log(newString2) // => HelloWorldkobe
    console.log(newString3) // =>  HelloWorldkobeabccba

    // 2.删除收尾的空格
    console.log("    why      abc   ".trim())// =>why      abc

    // 3.字符串切割split
    var message = "abc-cba-nba-mba"
    var items = message.split("-")
    var newMessage = items.join("*")
    console.log(newMessage) // => abc*cba*nba*mba
    
    const str = '1'

    // 对于padStart和padEnd方法,如果原本的长度大于其需要的长度(也就是第一个参数所设置的参数),那么对应方法会静默失效

    // padStart() 方法用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充
    // 参数1 -- 字符串所需要达到的个数
    // 参数2 -- 使用什么进行填充, 对应的参数会被转换为字符串后在进行填充
    console.log(str.padStart(2, 0)) // => 01
    console.log(typeof str.padStart(2, 0)) // => string

    // padEnd()  方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充
    // padEnd方法的参数和padStart方法的参数完全一致
    console.log(str.padEnd(2, 0)) // => 10
    console.log(typeof str.padEnd(2, 0)) // => string

6. 数组

对象允许存储键值集合,但是在某些情况下使用键值对来访问并不方便

这个时候我们需要一种有序的集合,里面的元素是按照某一个顺序来排列的

这个数据类型需要是有序的集合,以便于我们可以通过索引来获取到其中每一个元素

数组是一种特殊的对象类型

这个结构就是数组(Array)

Snipaste_2022-11-28_10-07-22.png

6.1 数组的创建方式

// 1.创建数组的方式
// 字面量形式创建数组
var names = ["wjl", "kobe", "james", "curry"]

var product1 = { name: "苹果", price: 10 }
var products = [
  { name: "鼠标", price: 98 },
  { name: "键盘", price: 100 },
  { name: "西瓜", price: 20 },
  product1
]

// 2.创建方式二: 类Array
// 构造函数方式创建数组
// 所以数组本身也是一种特殊的对象
// 那也就意味着 Array extends Object

// 在使用Array构造函数的时候 new可以省略 但是并不被推荐
var arr1 = new Array()
var arr2 = new Array("abc", "cba", "nba")
console.log(arr1, arr2) // ["abc", "cba", "nba"]

// 传入了一个数字, 它默认会当成我们要创建一个对应长度的数组
var arr3 = new Array(5) 
console.log(arr3, arr3[0])// [empty*5]
var arr4 = [5]

// 3.通过索引访问元素
console.log(names[0]) // 第一个元素
console.log(names[names.length-1]) // 最后一个元素

6.2 数组的基本操作

    // 对于某一个结构的操作: 增删改查(数据库)

    var names = ["abc", "cba", "nba"]

    // 1.访问数组中的元素
    console.log(names[0]) // => abc
    console.log(names.at(0)) // =>abc
    
    console.log(names[-1]) // => undefined
    console.log(names.at(-1))// => nba

    // 2.修改数组中的元素
    names[0] = "wjl"
    console.log(names)  // = > ['wjl', 'cba', 'nba']

    // 3.新增数组中的元素(了解)
    names[3] = "kobe"
    names[10] = "james"
    console.log(names) // =>  ['wjl', 'cba', 'nba', 'kobe', 空属性 × 6, 'james']


    // 4.删除数组中的元素(了解)
    delete names[1]
    console.log(names) // => ['wjl', 空白, 'nba', 'kobe', 空属性 × 6, 'james']
    console.log(names[1])// => undefined

6.3 数组的添加、删除方法

    var names = ["abc", "cba", "nba", "mba", "abcd"]

    // 1.在数组的尾部添加和删除元素
    // push方法
    // 在数组末尾添加元素
    // 添加元素 可以一次添加一个 也可以一次添加多个
    // push方法的返回值为修改后数组的个数
    // push方法会修改原数组
    names.push("wjl", "kobe")
    console.log(names) // => ['abc', 'cba', 'nba', 'mba', 'abcd', 'wjl', 'kobe']
    // pop方法
    // 在数组末尾移除元素
    // 一次只能移除一个
    // pop方法的返回值为移除的那个元素
    // pop方法会修改原数组
    names.pop()
    names.pop()
    console.log(names)  // =>  ['abc', 'cba', 'nba', 'mba', 'abcd']
    
    // 2.在数组的头部添加和删除元素
    // unshift方法
    // 在数组头部添加元素
    // 添加元素 可以一次添加一个 也可以一次添加多个
    // unshift方法的返回值为修改后数组的个数
    // unshift方法会修改原数组
    names.unshift("wjl", "kobe")
    console.log(names) // => ['wjl', 'kobe', 'abc', 'cba', 'nba', 'mba', 'abcd']
    // shift方法
    // 在数组头部移除元素
    // 一次只能移除一个
    // shift方法的返回值为移除的那个元素
    // shift方法会修改原数组
    names.shift()
    console.log(names) // =>  ['kobe', 'abc', 'cba', 'nba', 'mba', 'abcd']
    
    

shiftunshift是在数组头部进行元素的增删,所以其在添加或移除元素后,需要进行数组的整体移动

poppush方法是直接在数组尾部进行元素的增删,其不需要进行数组的整体移动

所以poppush的性能和执行速度相比shiftunshift更优,也更快

Snipaste_2022-11-28_13-37-46.png

如果我们希望在中间某个位置添加,删除或者替换元素,我们可以使用splice方法

arr.splice 方法可以说是处理数组的利器,它可以做所有事情:添加,删除和替换元素

    // 3. 在任意位置添加/删除/替换元素
    var names = ["abc", "cba", "nba", "mba", "abcd"]
      // splice方法不是纯函数,会修改原数组
    // 参数一: start, 从什么位置开始操作元素
    // 参数二: deleteCount, 删除元素的个数如果为0或者负数表示不删除
    // 参数3 - item1, item2, ...:在添加元素时,需要添加的元素
    // 返回值 - 被删除元素组成的数组,如果没有删除元素,就返回空数组

    // 3.1.删除元素
    names.splice(1, 2)
    console.log(names) // => ['abc', 'mba', 'abcd']

    // 3.2.新增元素
    // deleteCount: 0, 后面可以添加新的元素
    names.splice(1, 0, "wjl", "kobe")
    console.log(names) // => ['abc', 'wjl', 'kobe', 'mba', 'abcd']


    // 3.3.替换元素
    names.splice(1, 2, "wjl", "kobe", "james")
    console.log(names)  // => ['abc', 'wjl', 'kobe', 'james', 'mba', 'abcd']

6.4 length属性

length属性用于获取或设置数组的长度

当我们修改数组的时候,length 属性会自动更新

也就是说数组的length属性是可写的

  • 如果我们手动增加一个大于默认length的数值,那么数组会自动扩容,即增加数组的长度
  • 如果我们设置一个比原本length小的值,数组就会被截断
    var names = ["abc", "cba", "nba", "mba"]

    // 1.属性length
    // 获取数组的长度length
    // console.log(names.length) // 4

    // // length属性可写的(扩容)
    // names.length = 10
    // console.log(names) // =>  ['abc', 'cba', 'nba', 'mba', 空属性 × 6]

    // // 设置的length小于原来的元素个数
    // names.length = 0
    // console.log(names)  // => []

    // 2.数组的遍历
    // 2.1. 普通的for循环
    for (var i = 0; i < names.length; i++) {
      console.log(names[i])
    }

    // 2.2. for..in
    for (var index in names) {
      // for-in 对应的变量(即这里的i) 类型是string
      console.log(index, names[index])
    }

    // 2.3. for..of
    for (var item of names) {
      // item表示数组中每一项的元素值
      // item的类型取决于遍历出来的元素的类型
      console.log(item)
    }
    // 2.4. forEach方法可以用于遍历数组
    // forEach方法的第二个参数用于修正callback内部的this指向
    arr.forEach(function (item, index, arr) {
      // 如果需要使用修正的this值,那么callback不可以是箭头函数
      console.log(item, index, arr, this)
    }, { name: 'Klaus' })

6.5 数组方法 – slice、cancat、 join

    var names = ["abc", "cba", "nba", "mba", "wjl", "kobe"]

    // 1.slice方法: 不会修改原数组
    // splice有区别: splice修改原有的数组
    // start 从什么位置开始
    // end 结束位置, 不包含end本身
    // 数组slice方法的使用 和 字符串的slice方法的使用方式完全一致
    // slice方法是纯函数,不会修改原数组
    // slice方法的返回值是截取后的新数组
    var newNames = names.slice(2, 4)
    console.log(newNames) // ['nba', 'mba']

    // 2.concat方法: 将多个数组拼接在一起
    var names1 = ["abc", "cba"]
    var names2 = ["nba", "mba"]
    var names3 = ["wjl", "kobe"]
    
    // push方法添加新数组
    // 1. push方法不是纯函数
    // 2. push方法会将参数作为作为一个整体进行插入
    //    所以如果参数的类型是数组类型的时候,其值会作为一个整体进行插入,默认并不会自动展开
    // names1.push(arr2)  // => [ 'abc', 'cba', [ 'nba', 'mba' ] ]
    
    var newNames2 = names1.concat(names2, names3)
    console.log(newNames2) //=> ['abc', 'cba', 'nba', 'mba', 'wjl', 'kobe']

    // 3.join方法: 字符串split
    console.log(names.join("-"))// => abc-cba-nba-mba-wjl-kobe

6.6 数组方法 – 查找元素

    /*
      indexOf方式.
      手动for循环
      数组的find方法
    */

    // 1.数组中存放的是原始类型
    var names = ["abc", "cba", "nba", "mba"]

    // 1.1. indexOf
    // 可以找到, 返回对应的索引
    // 没有找到, 返回-1
    // indexOf lastIndexOf includes 方法的使用和字符串中对应方法的使用方式是完全一致的
    // indexOf lastIndexOf includes 方法只适合于基本数据类型的查找,如果数组中的元素类型是复杂数据类型
    // indexOf lastIndexOf includes将会无能为力
    console.log(names.indexOf("nbb")) // => -1
    console.log(names.lastIndexOf('nba')) // => 2
    console.log(names.includes('nba')) // => true

    // 2.数组中存放的是对象类型
    var students = [
      { id: 100, name: "wjl", age: 18 },
      { id: 101, name: "kobe", age: 30 },
      { id: 102, name: "james", age: 25 },
      { id: 103, name: "wjl", age: 22 }
    ]

    // 查找的是id为101的学生信息
    // 2.1. 自己写一个for循环
    var stu = null
    for (var i = 0; i < students.length; i++) {
      if (students[i].id === 101) {
        stu = students[i]
        break
      }
    }

    // // 判断上面的算法有没有找到对应的学生
    if (stu) {
      console.log("找到了对应的101学生", stu) // => 找到了对应的101学生 {id: 101, name: 'kobe', age: 30}
    } else {
      console.log("没有找到对应的101学生")
    }

    // 2.2. find方法: 高阶函数
    var students = [
      { id: 100, name: "why", age: 18 },
      { id: 101, name: "kobe", age: 30 },
      { id: 102, name: "james", age: 25 },
      { id: 103, name: "why", age: 22 }
    ]
    
    var stu = students.find(function(item) {
      if (item.id === 101) return true
    })
    // find 找到符合条件元素 --- 找到返回对应元素,找不到返回undefined
    console.log(stu) // => {id: 101, name: 'kobe', age: 30}
    
    var names = ["abc", "cba", "nba"]

    // indexOf/lastIndexOf
    // find: 查找元素

    // includes
    // console.log(names.includes("nba"))

    // findIndex: 查找元素的索引
    var findIndex = names.findIndex(function(item, index, arr) {
      return item === "nba"
    })
    // var findIndex = names.findIndex(item => item === "nba")
    
    // findIndex 找符合条件的元素的索引值 -- 找到返回对应的索引值,找不到返回-1
    console.log(findIndex) // => 2
    
    // find 和 findIndex 是ES6中提供的在数组中进行元素项查找的高阶函数
    // find 和 findIndex 的参数是一个回调函数
    // 数组中有几个元素,对应的回调函数就会被调用几次,并将每一个元素,索引,数组本身作为参数传递给回调函数
    // 也就是说find 和 findIndex方法中的回调函数的参数和forEach中的参数是一致的
    // 如果回调函数返回true, 那么find 和 findIndex 就会终止查找 并将对应结果返回
    

Snipaste_2022-11-29_09-04-24.png


     var nums = [20, 4, 10, 15, 100, 88]
    
    // sort: 排序
    // sort方法也是一个高阶函数,用于对数组进行排序,并返回一个排序后的新数组
    // sort方法不是一个纯函数,会改变原数组
    // 如果callback返回的结果大于0,那么就交换,如果callback返回结果小于等于0,就不交换
    nums.sort(function(item1, item2) {
        return item2 - item1
      // item1和item2进行比较
      // 返回是 整数
      // 谁小谁在前
      // return item1 - item2
    
    })

    console.log(nums)  // =>  [100, 88, 20, 15, 10, 4]
    // reverse() 方法将数组中元素的位置颠倒,并返回该数组
    // reverse方法是一个非纯函数,会改变原数组
    console.log(nums.reverse()) // =>  [4, 10, 15, 20, 88, 100]

    // 复杂类型的排序
    var students = [
      { id: 100, name: "wjl", age: 18 },
      { id: 101, name: "kobe", age: 30 },
      { id: 102, name: "james", age: 25 },
      { id: 103, name: "curry", age: 22 }
    ]

    students.sort(function(item1, item2) {
      return item1.age - item2.age
    })

    console.log(students) // =>[    { id: 100, name: "wjl", age: 18 },  { id: 103, name: "curry", age: 22 } ,  { id: 102, name: "james", age: 25 }, { id: 101, name: "kobe", age: 30 }  ] 
    const arr = new Array(3)

    // fill 方法将数组中的每一个元素项全部填充为对应的参数值
    // fill方法是非纯函数
    arr.fill(3)
    console.log(arr) // => [ 3, 3, 3 ]
 
     // 1.forEach函数
    var names = ["abc", "cba", "nba", "mba"]

    // 三种方式, 新增一种方式
    // 数组的绝对多数 高阶函数的参数都是 item,index,arr
    // 绝大多数数组的高阶函数,除了第一个callback外,都存在第二个参数thisArg,用于修正callback内部的this指向
    
    names.forEach(function(item, index,arr ) {
      console.log(item ,index, arr)
    }, { name: "wjl" })
    
    names.forEach(function(item) {
      console.log(item, this )
    }, { name: "wjl" })
    
    // 2.filter函数: 过滤
    // filter() 方法创建一个新数组 - filter方法是一个纯函数
    // 新数组中只包含每个元素调用函数返回为true的元素
    var nums = [11, 20, 55, 100, 88, 32]
    // 2.1. for循环实现
    // var newNums = []
    // for (var item of nums) {
    //   if (item % 2 === 0) {
    //     newNums.push(item)
    //   }
    // }
    
    var newNums = nums.filter(function(item) {
      return item % 2 === 0
    })
    console.log(newNums) // =>  [20, 100, 88, 32]

    // 3.map函数: 映射
    var nums = [11, 20, 55, 100, 88, 32]
    // map() 方法创建一个新数组 --- map方法是纯函数
    // 这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成,也就是说map方法是用来进行数组映射的
    var newNums = nums.map(function(item) {
      return item * item
    }) 
    console.log(newNums) // = > [121, 400, 3025, 10000, 7744, 1024]

    
    // 4.reduce 累加
    var nums = [11, 20, 55, 100, 88, 32]
    var result = 0
    for (var item of nums) {
      result += item
    }
    console.log(result) // => 306

    // 第一次执行: preValue->0 item->11
    // 第二次执行: preValue->11 item->20
    // 第三次执行: preValue->31 item->55
    // 第四次执行: preValue->86 item->100
    // 第五次执行: preValue->186 item->88
    // 第六次执行: preValue->274 item->32
    // 最后一次执行的时候 preValue + item, 它会作为reduce的返回值

    // initialValue: 初始化值, 第一次执行的时候, 对应的preValue
    // 如果initialValue没有传呢?
    // reduce的第一个参数为callback, 第二个参数为initalValue
    // 1. 如果设置了initalValue,那么第一次执行的preValue的值即为initalValue,item为nums[0]
    //                      第二次执行的时候,preValue为上一次callback的返回值,item为nums[1]
    //                      依次类推,如果全部遍历完毕,那么就直接将callback的返回值作为reduce方法的返回值进行返回

    // 2. 如果没有设置initalValue,那么第一个执行的preValue的值为nums[0], item的值为nums[1]
    //                       第二次执行的preValue的值为上一次callback的返回值,item的值为nums[2]
    //                       依次类推,如果全部遍历完毕,那么就直接将callback的返回值作为reduce方法的返回值进行返回

    // 所以reduce方法一般可以用来做数组的累加操作 --- reduce是一个纯函数
    var result = nums.reduce(function(preValue, item) {
      console.log(`preValue:${preValue} item:${item}`)
      return preValue + item
    }, 0)
    console.log(result) // => 306

    // reduce练习
    var products = [
      { name: "鼠标", price: 88, count: 3 },
      { name: "键盘", price: 200, count: 2 },
      { name: "耳机", price: 9.9, count: 10 },
    ]
    var totalPrice = products.reduce(function(preValue, item) {
      return preValue + item.price * item.count
    }, 0)
    console.log(totalPrice) // => 763
    
    
    
  

    

计算一个数组中所有偶数的平方的和

    var nums = [11, 20, 55, 100, 88, 32]

    // 过滤所有的偶数, 映射所有偶数的平方, 并且计算他们的和
    var total = nums.filter(function(item) {
      return item % 2 === 0
    }).map(function(item) {
      return item * item
    }).reduce(function(preValue, item) {
      return preValue + item
    }, 0)
    console.log(total) // => 19168
    
    // every方法 数组中的每一项都符合条件的时候 才会返回true
    console.log(nums.every(num => num % 2 === 0)) // => false

    // some方法 数组中只要有一个元素符合对应的条件 就会返回true
    console.log(nums.some(num => num % 2 === 0)) // => true
    
    // var total = nums.filter(item => item % 2 === 0)
    //                 .map(item => item * item)
    //                 .reduce((preValue, item) => preValue + item, 0)
    // console.log(total) // => 19168

7. Date

国际标准时间是英国伦敦的皇家格林威治( Greenwich )天文台的标准时间(刚好在本初子午线经过的地方),这个时间也称之为GMT(Greenwich Mean Time)

  • 其他时区根据标准时间来确定自己的时间,往东的时区(GMT+hh:mm),往西的时区(GMT-hh:mm)
  • 例如: 中国位于东8区,假设GMT的零时区时间为00:00 那么此时中国的时间为08:00

但是,公转有一定的误差,因此会造成GMT时间有一定的误差,于是就提出了根据原子钟计算的标准时间UTC(Coordinated Universal Time)

在浏览器中,GMT时间表示,是按照UTC格式时间再加上对应的时区值进行计算得到

在JavaScript中我们使用Date来表示和处理时间

    // 创建Date对象的方式
    // 1.没有传入任何的参数, 获取到当前时间
    var date1 = new Date()
    /*
      在浏览器中,默认的时间输出格式为 RFC 2822 标准 => Wed Nov 30 2022 09:45:02 GMT+0800 (中国标准时间)
      在node中,默认的时间输出格式为 ISO 8601 标准 => 2022-11-30T01:46:51.302Z
    */
    
    console.log(date1)
    
    /*
      在创建时间对象的时候,new关键字可以省略,表现形式和加上new是基本一致的 --- 不推荐
      在浏览器中,输出格式和new Date()的输出格式一致
      在node中,默认的输出格式不在使用ISO格式,而是使用RFC格式

      ps: 后边Date构造函数有参数的时候,如果省略了new关键字,其表示效果和这里是一致的

      > 之所以JS的大部分内置类在创建的时候,既可以使用new关键字,也可以不使用new关键字
      > 是因为ES5中,类就是构造函数,而其本身本质上就是一个普通函数
      > 所以其既可以通过new来进行调用,也可以作为普通函数进行调用
      > 所以JS为了避免在调用的时候,调用者直接将内置类当做普通函数进行调用
      > 所以也对其进行了实现,但是不使用new关键字来调用构造函数是不规范的
      > 所以这种调用方式并不被推荐
    */
    const date2 = Date()
    console.log(date2)
    

    // 2.传入参数: 时间字符串
    
    // 根据具体时间生成对应的日期对象
    // 参数为日期格式字符串 除了年份外 其余都可以省略
    // 如果月份和日期省略 默认就是 01 即为1月1日
    // 如果时分秒省略 默认就是0:0:0
    //  ---  如果是RFC表示 浏览器会自动修复时区,所以默认的时分秒在中国是08:00:00
    //  ---  如果是ISO表示,node并不会自动修复时区,所以默认的时分秒表示即为00:00:00
    
    var date2 = new Date("2022-08-08")
    console.log(date2)  // => Mon Aug 08 2022 08:00:00 GMT+0800 (中国标准时间)

    // 3.传入具体的年月日时分秒毫秒
    
    // 传入多个参数,分别对应 年,月,日,时,分,秒,毫秒
    // 至少传入2个参数,也就是年和月,其余参数可以省略
    // 不可以单单传入年,否则年份会被当做时间戳进行解析
    // 日期不传 默认值为01, 其余不传默认值为00
    // 和直接传入时间字符串不同的时候,分别传入的值会被认为是当前时区值,所以浏览器不会对时间的时区进行修正
    
    // 传入一个时间戳
    var date3 = new Date(2033, 10, 10, 09, 08, 07, 333)
    console.log(date3) // =>  Thu Nov 10 2033 09:08:07 GMT+0800 (中国标准时间)

    // 4.传入一个Unix时间戳
    // 1s -> 1000ms
    var date4 = new Date(10004343433)
    console.log(date4)// => Mon Apr 27 1970 02:59:03 GMT+0800 (中国标准时间)

7.1 时间戳

计算机中的时间戳分为两种

种类说明
时间戳它是一个整数值,表示自1970年1月1日00:00:00 UTC以来的毫秒数 时间戳的长度为13位 JavaScript采用的就是这种毫秒为单位的时间戳
Unix 时间戳它是一个整数值,表示自1970年1月1日00:00:00 UTC以来的秒数 时间戳的长度为10位 Go语言采用的就是Unix时间戳

7.2 dateString表示形式

日期的表示方式有两种: RFC 2822 标准 或者 ISO 8601 标准

标准示例说明
RFC 2822Mon May 16 2022 17:24:22 GMT+0800 (中国标准时间)1. 浏览器中日期默认的输出格式 2. 默认输出值会自动转换为当前时区
ISO 8601 标准2022-05-16T09:24:22.113Z 2022-05-03T17:30:08+08:001. node中日期默认的输出格式 2. 默认输出值不会自动转换为当前时区

其它的一些时间表示格式,例如‘2022-02-12 12:03:25.333’ 为自定义的时间表示格式,并不是国际标准的时间表示格式

ISO转换标准

标识说明
YYYY年份,0000 ~ 9999 YYYY表示四位年份 YY表示两位年份
MM月份,01 ~ 12 月份使用MM表示,以区分 分钟
DD日,01 ~ 31
T分隔日期和时间,没有特殊含义,可以省略
HH小时,00 ~ 24 HH表示24小时制 hh表示12小时制
mm分钟,00 ~ 59 分钟使用mm表示,以区分 月份
ss秒,00 ~ 59
.sss毫秒
Z时区 Z表示零时区
    // 日期格式之间的相互转换
    var date = new Date()

    console.log(date) // => Wed Nov 30 2022 10:07:40 GMT+0800 (中国标准时间)
    console.log(date.toDateString())  // => Wed Nov 30 2022
    // RFC -> ISO
    console.log(date.toISOString()) // => 2022-11-30T02:07:40.146Z
    // ISO -> RFC
    console.log(date.toString()) // => Wed Nov 30 2022 10:07:40 GMT+0800 (中国标准时间)
    
    // 转换为GMT格式时间,这两个方法返回的时候,不会自动校对时区
    // 又因为GMT是UTC加上对应的时区,所以以下两个方法的返回值是一样的
    console.log(date.toUTCString()) // => Wed, 30 Nov 2022 02:08:32 GMT
    console.log(date.toGMTString())// => Wed, 30 Nov 2022 02:08:32 GMT

但是ISO和RFC格式表示的时间,并不是我们日常生活中,常用的时间

所以多数情况下,我们需要获取到对应的时间值后进行自定义转换

7.3 获取和设置时间信息

我们可以从Date对象中获取各种详细的信息,同样我们也可以设置Date中各种各样的信息

我们在对日期进行设置的时候,如果设置的值超出了对应数值的范围,JS会进行自动校准

获取方法说明设置方法
getFullYear()获取年份(4 位数)setFullYear()
getMonth()获取月份,从 0 到 11 注意: 月份是从0开始计算的setMonth()
getDate()获取当月的具体日期,从 1 到 具体月份的最大值setDate()
getHours()获取小时setHours()
getMinutes()获取分钟setMinutes()
getSeconds()获取秒钟setSeconds()
getMilliseconds()获取毫秒setMilliseconds()
getDay()获取一周中的第几天,从 0(星期日)到 6(星期六) 注意: 1. 在浏览器中 一周开始的日期为周日 2. 周几的计算也是从0开始计算的setTime()
    var date = new Date()

    console.log(date)
    console.log(date.toISOString())

    // 1.获取想要的时间信息
    var year = date.getFullYear()
    var month = date.getMonth() + 1
    var day = date.getDate()
    var hour = date.getHours()
    var minute = date.getMinutes()
    var second = date.getSeconds()
    console.log(year, month, day, hour, minute, second)
    console.log(`${year}/${month}/${day} ${hour}:${minute}:${second}`)

    var weekday = date.getDay() // 一周中的第几天
    console.log(weekday)


    // 2.也可以给date设置时间(了解)
    // date.setFullYear(2033)
    // 自动校验
    date.setDate(32)
    console.log(date)  // Fri Dec 02 2033 10:41:14 GMT+0800 (中国标准时间)

7.4 获取时间戳

    // var date = new Date(1132324234242)
    // console.log(date)

    // Date对象, 转成时间戳
    var date = new Date()
    // 根据某一个具体的时间,转换为对应的时间戳
    var date2 = new Date("2033-03-03")

    // 方法一: 当前时间的时间戳
    var timestamp1 = Date.now()
    console.log(timestamp1)

    // 方法二/三将一个date对象转成时间戳
    // 使用实例方法getTime或valueOf
    // getTime 和 valueOf 方法的功能是一样的
    var timestamp2 = date.getTime()
    var timestamp3 = date2.valueOf()
    console.log(timestamp2, timestamp3)

    // 方法四: 了解
    
    
    // date对象有重写toString方法和valueOf方法
    // 也就是说如果date对象转字符串的时候,会转换成RFC标准或ISO标准的日期格式字符串
    // 如果date对象转换为数字类型的时候,会转换为对应的时间戳
    // 因此在date对象前面加上加号,减号,叹号的时候,就会形成对应的表达式
    // 此时如果在date对象前边加上正号,date对象就会被转换为数值类型
    // 也就是我们所需要的时间戳
    
    console.log(+date)
    
    // Date.parse(str) 方法可以从一个字符串中读取日期,并且输出对应的时间戳
    // Date.parse(str) === new Date(dateString).getTime()
    // Date.parse方法的参数必须是符合 RFC2822 或 ISO 8601 日期格式的字符串
    // 其他格式的日期字符串作为参数的时候,该方法可能可以正常解析,但是不保证结果一定正确
    // 如果输入的格式不能被解析,那么会返回NaN
    console.log(Date.parse('Mon May 16 2022 19:32:45 GMT+0800 (中国标准时间)')) // => 1652700765000

    // 计算这个操作所花费的时间
    var startTime = Date.now()
    for (var i = 0; i < 100000; i++) {
      console.log(i)
    }
    var endTime = Date.now()
    console.log("执行100000次for循环的打印所消耗的时间:", endTime - startTime)


    // 封装一个简单函数
    function testPerformance(fn) {
      var startTime = Date.now()
      fn()
      var endTime = Date.now()
    }
    
    

Date.parse方法

Date.parse(str) 方法可以从一个字符串中读取日期,并且输出对应的Unix时间戳。

作用等同于 new Date(dateString).getTime() 操作

需要符合 RFC2822 或 ISO 8601 日期格式的字符串

比如YYYY-MM-DDTHH:mm:ss.sssZ

其他格式也许也支持,但结果不能保证一定正常

如果输入的格式不能被解析,那么会返回NaN

    var timeString = "03/23/2033"

    // 1.方式一:
    // var date = new Date(timeString)
    // var timestamp = date.getTime()

    // 2.方式二:
    var timestamp = Date.parse(timeString)
    console.log(timestamp)

7.5 通过时间戳计算时分秒

如果我们有一个三位数,我们希望分别取出其百位,十位,个位上的值,我们可以使用如下方式

const num = 234

console.log(Math.floor(num / 100)) // => 2 - 百位
console.log(Math.floor(num / 10) % 10) // => 3 - 十位
console.log(num % 10) // => 4 - 个位

如果我们有一个时间戳,想要获取其对应的时,分,秒,也可以使用类似的方式

    const now = new Date()

    console.log(now) // => 2022-05-21T04:57:40.413Z

    // 计算当前时间的时分秒,需要先计算出当前时间和单日零点之间的时间戳
    // 日期是一个对象,所以需要重新新建一个新的日期对象
    const yesterday = new Date()
    // 将新的日期对象的时间设置为当日零点
    yesterday.setHours(0, 0, 0)

    // 计算时间戳的差值,并将毫秒制转换为秒制
    const intervalTime = Math.floor((now - yesterday) / 1000)

    // 根据时间戳的差值,计算时分秒
    console.log(Math.floor(intervalTime / (60 * 60))) // => 12 - 时
    console.log(Math.floor(intervalTime / 60) % 60) // => 57 - 分
    console.log(intervalTime % 60) // => 39 - 秒