【JavaScript】数组,对象,数组方法,arguments,字符串方法,深度对比

本文深入讲解JavaScript中的数组和对象操作、字符串方法、垃圾回收机制等内容,并提供了丰富的代码示例。

❤️ Author: 老九
☕️ 个人博客:老九的优快云博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

数组

创建空数组
var a = []
创建有初始内容的数组
var a = [1, 2, 3 + 2, 4]
创建长不为0的数组
var a = Array(5)
读取数组的长度
a.length
读取数组中的第i项
a[i]//此处i可以为任意求出整数的表达式
读出数组中的最后一项
a[a.length - 1]
在数组末尾增加一项
a.push(value)
在删除数组末尾的项并返回这一项的值
var value = a.pop()
在数组的前面增加一项
a.unshift(value)
删除数组第一项并返回这一项的值
var value = a.shift() 
将数组填满
a.fill(value)

练习

<script>
  function min(a,b){
    return a < b ? a : b
  }
//-1就是-129 == 127 
  function isEven(n){
    if(n == 0){
      return true
    }
    if(n == 1){
      return false
    }
    return isEven(n - 2)
  }

  function countBs(str){
    var count = 0
    for(var i = 0;i<str.length;i++){
      if(str[i] == 'B'){
        count++
      }
    }
    return count
  }
</script>

对象

  • 补充:字符串.toUpperCase():全变成大写,.toLowerCase():全变小写,这两个方法都是基于原来的字符串返回一个新的字符串,原来的字符串不改变
  • str.charCodeAt(i):由符号(i是字符串的下标位置)得到编号;String.fromCharCode(code+32):由编号得到符号
  • 对象的key式字符串或者式symbol,value可以是任意类型
  • 删除对象的属性,用delete操作符
  • 中括号和点语法的区别:中括号使用范围广
<script>
  debugger;
  var str = 'jlkfjdskfJKLJKLJ'
  function toLowerCase(str) {
    var result = ''
    for (var i = 0; i < str.length; i++) {
      var code = str.charCodeAt(i)
      if (code >= 65 && code <= 90) {
        result += String.fromCharCode(code + 32)
      } else {
        result += str[i]
      }
    }
    return result
  }
  toLowerCase(str)
</script>

<script>

  function push(ary, val) {
    ary[ary.length] = val
    return ary.length
  }

  function unshift(ary, val) {
    for (var i = ary.length; i > 0; i--) {
      ary[i] = ary[i - 1]
    }
    a[0] = val
    return ary.length
  }

  function join(ary, str) {
  	str = String(str) 
    var result = ''
    for (var i = 0; i < ary.length - 1; i++) {
      result += ary[i] + str
    }
    return result + ary[i]
  }
</script>

在这里插入图片描述

  • 对象是属性的集合,对象是任意数量的(属性与值对应关系)的集合
var obj = {
	name : '张三',
	age : 18,
	weight : '60kg',
	events : ['work','touched trees','eat peanut']
	’the bug‘ : 132132,
	//如果用for in遍历属性的话,数字是按照自然数的顺序遍历的
	5 : 15,
	1: 222,
}
取值方式有两个
obj['name']  obj.name
  • a=[1,2,3],b = a,b和a指向得是同一个数组,a改变之后b也会改变,而不是b对象指向a对象,而是b对象指向a对象指向得数组,js都是这样得。===对比的就是是不是同一个对象。
  • 如果要删除一个对象中的一个属性,就用delete obj.c,如果要删的属性是一个字符串,可以用delete obj[‘the bug’]
  • 一个属性的值是undefined与这个对象有这个属性是有区别的
  • 添加属性就是直接obj.xxx = 8888即可,相当于给属性赋值,或者是obj[c] = 8888
  • obj = {a : 1,b:2,c:undefined},‘c’ in obj的值为true(注意c要打引号),in可以判断这个属性在不在这个对象中
    在这里插入图片描述
  • indexOf方法是在数组中找到对应值的下标,不存在返回-1
    在这里插入图片描述
  • in是一个运算符,看这个对象有没有这个属性,event in result,判断result对象中有没有有event这个属性
  • in运算符配合for循环,可以循环出对象的所有属性
    在这里插入图片描述
  • 下面这段代码,在JavaScript中,对象和数组等复合类型都是通过引用传递的,而不是通过值传递。函数f并没有修改val所指向的对象的任何属性值,而是将val重新赋值为一个新的空对象{}。这个新对象与原始对象没有任何关系,它只是一个新的对象,与原始对象不同。
    在这里插入图片描述

数组方法

  • at(可以为负数),push,pop,unshift,shift,fill(给的数组都是同一个,改其中一个,其他的也会跟着改变),join(将一个数组的所有元素连接成一个字符串并返回这个字符串。arr.join(“-”))
  • 查找元素的索引,indexOf(2,3)(从下标为3的位置开始找值为2的下标),lastIndexOf
<script>
  function indexOf(ary, val, startIndex = 0) {
    for (var i = startIndex; i < ary.length; i++) {
      if (ary[i] == val) {
        return i
      }
    }
    return -1
  }
</script>
  • slice(开始下标,结束下标),这个函数返回从开始下标到结束下标范围的数组的值,开始下标是包含的,结束下标是不包含的
  • slice出来的数组是浅拷贝,concat和slice都是相当于把原数组做了一次浅复制,在JavaScript中,复制一个对象分为浅拷贝和深拷贝两种方式。浅拷贝是指复制对象的引用,而不是对象本身,因此对原始对象的更改也会反映在新的对象中。深拷贝则是创建一个新的对象,其内容与原始对象相同,但它们是独立的。
  • 还有浅对比和深对比,浅对比就是只对比第一层的对象看是否相同,但是深对比对比的就是具体的内容是否一样,对比的是更深层的
<script>
  function slice(ary, start = 0, end = ary.length) {
    var result = []
    if(start < 0) start += ary.length
    if(end < 0) end += ary.length
    for (var i = start; i < end; i++) {
      result.push(ary[i])
    }
    return result
  }
</script>
  • 还可以是负值,这样就是从后往前了

在这里插入图片描述

  • concat函数,可以将字符串拼接起来,[1,2,3].concat([4,5,6]),结果是得到了一个新的数组
<script>
  function concat(a, b) {
    var result = []
    for (var i = 0; i < a.length; i++) {
      result.push(a[i])
    }
    for (var i = 0; i < b.length; i++) {
      result.push(b[i])
    }
    return result
  }
</script>

  • 面试题:深度克隆代码实现
<script>
  function cloneDeep(obj) {
    var copy = {}
    for (var key in obj) {
      var val = obj[key]
      if (typeof val === 'object') {
        copy[key] = cloneDeep(val)
      } else {
        copy[key] = val
      }
    }
    return copy
  }
</script>

  • includes方法,判断是否包含,返回true和false,相当于indexOf>=0
    在这里插入图片描述
    在这里插入图片描述
<script>
  function includes(ary, val) {
    if (Number.isNaN(val)) {
      for (var i = 0; i < ary.length; i++) {
        if (ary[i] !== ary[i]) {
          return true
        }
      }
      return false
    } else {
      for (var i = 0; i < ary.length; i++) {
        if (ary[i] === val) {
          return true
        }
      }
      return false
    }
  }
</script>

  • reverse方法,是将数组值的顺序调转,返回的是数组自身,而不是产生一个新的数组
  • 数组的toString方法就是将数组用逗号拼接,变成一个字符串
  • splice方法,也是修改数组自身的内容,下面这个意思是从下标为1开始,删除两项,然后将两项替换为’a‘’b‘’c’
    在这里插入图片描述
  • 清空数组最快的方法,arr.length = 0
  • 判断数组是否包含某个元素,arr.includes方法
  • find和findIndex,是高阶函数,传入函数参数,传入的函数有三个参数,item是当前的对象,index是第几个,arr是整个数组
var students = [
  {name: 'lmp',id:1},
  {name: 'tony',id:2},
]
var stu = students.findIndex(function(item,index,arr){
  return item.id == 1
})
console.log(stu)

数组的遍历

for in遍历的是索引值,for of遍历的是每一个元素
在这里插入图片描述

字符串方法

注意字符串在定义之后就不可以修改了,所以我们改变字符串的操作中,都是生成了一个新的字符串

  • length,toUpperCase,toLowerCase,charAt,charCodeAt,concat,indexOf(string,number),lastIndexOf,includes(string,number),startsWith(string),endsWith(string),replace(string,string),slice(number,number),split()
  • trim方法:把前面和后面的空白和回车都去掉,还有trimEnd()和trimStart()
<script>
  function trimStart(str){
    for(var i = 0;i<str.length;i++){
      if(str[i]!==' '){
        break
      }
    }
    return str.slice(i)
  }
</script>

  • endsWith方法,判断是不是以什么字符串结尾,还有startsWith,和endsWith相反
    在这里插入图片描述
  • padEnd方法,参数是在最后补20个单位的空格,padStart相反
    在这里插入图片描述
  • repeat方法,字符串重复若干次
    在这里插入图片描述
  • substring截取字符串,和数组的slice一样
  • substr也是截取字符串,s.substr(1,3)这个意思是将字符串s从下标为1的位置开始截取长度为3的字符串
    在这里插入图片描述
  • toString就是转成字符串,toString()括号里面写16就是转成16进制,写2就是转成2进制
    在这里插入图片描述
  • 获取子字符串的三种方法
    在这里插入图片描述
  • 字符串分割split
    在这里插入图片描述

arguments

  • 当函数在运行的时候,名叫arguments的特殊变量也会被添加到函数内部,函数的参数变成数组就是arguments,下面的例子所示
    在这里插入图片描述
<script>
  function sum() {
    var result = 0
    for (var i = 0; i < arguments.length; i++) {
      result += arguments[i]
    }
    return result
  }

  var a = sum(1, 1, 2, 2, 2, 3, 3, 3, 4)
</script>

  • 通过arguments数组实现concat函数,让concat([1,2,3],4,5,[6,7],[8,[9]])和[1,2,3].concat(4,5,[6,7],[8,[9]])一样
  • 重点:函数的实参只能传递以值或者数组或者对象为单位的第一个逗号前的值,后面的值只能通过arguments或者…的办法取得。
  • ary.slice(),函数可以用于复制一份数组,并生成一个新的数组
<script>
  function concat(ary) {
    debugger
    var result = ary.slice()
    for (var i = 1; i < arguments.length; i++) {
      if (Array.isArray(arguments[i])) {
        for (var j = 0; j < arguments[i].length; j++) {
          result.push(arguments[i][j])
        }
      } else {
        result.push(arguments[i])
      }
    }
    return result
  }

  concat([1, 2, 3], 4, 5, [6, 7], [8, [9]])
  //[1, 2, 3, 1, 2, 3, 4, 5, 6, 7, 8, Array(1)]
</script>

注意,箭头函数是不绑定arguments的,所以我们在箭头函数中使用arguments会去上层作用域查找

arguments和…的区别

…必须放到最后一个位置,否则报错
…只包含那些没有对应形参的实参,而arguments对象包含了传给函数的所有实参
arguments对象不是一个真正的数组,而…是一个真正的数组,可以进行数组的所有操作

arguments 转 Array

方式一

遍历arguments,添加到一个新数组在这里插入图片描述

方式二

Array.from
[…arguments]
在这里插入图片描述

…语法

  • …就相当于一个数组接收,每个函数只能使用一次…,并且…只能写在最后,…语法是浅复制,只能复制一层
  • 第一种用法是写在形参里
  • 第二种用法是写在函数实参中,例如Math.max()括号里接受的不能是一个数组,而是一个一个数字,如果有一个数组a,想放在Math.max()的实参中,就写成Math.max(…a)

在这里插入图片描述

  • 第三种用法,当作展开运算符
    在这里插入图片描述

全局变量

  • 声明的全局变量,其实是window的一个属性,window是一个对象 ,只有var定义的变量是,let和const定义的变量没货
    在这里插入图片描述
  • 补充:面试题:
    有一个函数返回1的概率为p,返回0的概率为1-p,基于此函数实现一个等概率返回0,1的函数
    只有下面写的两种情况是55开,全0全1就重新来
<script>
  var a = g()
  var b = g()
  if (a == 0 && b == 1) {
    return 0
  }
  if (a == 1 && b == 0) {
    return 1
  }
</script>

深度对比

<script>
  //返回这个对象的属性数量
  function size(obj){ 
    var c = 0
    for(var key in obj){
      c++
    }
    return c
  }
  function deepEqual(a, b) {
    if (a === b) {
      return true
    }
    //判断NaN
    if (a !== a && b !== b) {
      return true
    }
    //两个都是数组
    if (Array.isArray(a) && Array.isArray(b)) {
      if (a.length !== b.length) {
        return false
      } else {
        for (var i = 0; i < a.length; i++) {
          if (!deepEqual(a[i],b[i])) {
            return false
          }
        }
        return true
      }
    }
    //两个都是对象
    if (a && b && typeof a == typeof b && !Array.isArray(a) && !Array.isArray(b)) {
      //对象的属性数量不同,直接返回假
      if(size(a) !== size(b)){
        return false
      }
      //否则,遍历其中一个对象
      //不用反过来遍历,因为如果属性数量相同,但属性集合不同,一定会遍历到一个b不存在的属性时返回
      for (var key in a) {
        if (!(key in b)) {
          return false
        }
        if (!deepEqual(a[key],b[key])) {
          return false
        }
      }
      return true
    }
    return a === b
  }
</script>
  • 给一个不存在的变量直接赋值会直接创建出全局变量,如果在函数体内第一行写’use strict’这行字符串,就会触发严格模式,这样就不会出现没有定义的变量赋值这种操作了,而是会报错

在这里插入图片描述

GC垃圾回收机制

引用计数

这个js引擎用的不是这个回收机制,当一个对象
但是这个可能会产生循环引用,导致无法释放内存,导致内存泄漏
在这里插入图片描述

标记清除

核心思想:可达性
首先在内存中设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象;
这个算法可以很好解决循环引用问题
在这里插入图片描述
这个是V8中使用的算法,在JS中根对象就是window,当然为了给V8引擎更好的优化,算法的细节上也会结合一些其他的算法

标记整理

标记整理和标记清除相似,不同的是,回收的期间会将保留的存储对象搬运汇集到连续的内存空间,从而整合空闲的空间,避免内存的碎片化

分代收集

对象被分为两组:“新生代”和“旧生代”
新生代就是一些对象,他们完成工作并很快的死去,他们可以很快的被清理;
老生代就是长期存活的对象,被检查的频次也会减少

新生代和旧生代的区分

新生代首先会分为两块内存,fromspace和tospace,然后fromspace释放之后,一段时间复制到tospace,然后tospace里面部分释放之后,如果从fromspace来的依旧存活,就会放在旧生代,然后fromspace和tospace交换

增量收集

如果有许多对象,并且我们试图一次遍历并标记整个对象集,则可能需要一些时间,并在执行过程中带来明显的延迟。
所以引擎试图将垃圾收集工作分成几部分来做,然后将这几部分会逐一进行处理,这样会有许多微小的延迟而不是一个大的延迟

闲时收集

垃圾收集器只会在 CPU 空闲时尝试运行,以减少可能对代码执行的影响。

V8引擎详细内存图

在这里插入图片描述

————————————————————————
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为优快云博主「亚太地区百大最帅面孔第101名」的原创文章

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李小浦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值