当前位置: 首页 > news >正文

rangechecks 检测代码检测到超出范围的数组访问。_夯实基础系列(一)数据类型及其检测及进阶...

1.系列介绍

第一节,讲解数据类型,及其常规的检测方法,数据存储形式,垃圾回收,包括面试中常常要求的手写的一些方法。

2.JS中数据类型

JS数据类型分为两大类:基本类型和引用类型。基本类型包含:

string, number. boolean, undefined, null, symbol六种;引用类型有:对象一种。

面试官:“那么什么是基本类型什么是引用类型呢?”

小明:“基本类型是简单的数据段,而引用类型是可能由多个值构成。

面试官:“那么简单类型和引用类型是如何存储的呢?”

小红:”简单类型按值访问,存储在内存栈中,引用类型,按引用访问,地址存储在栈中,内容存储在堆中。所以但进行简单类型复制的时候,复制的是一个值,而引用类型复制的时候复制的其实是一个引用地址,所以就有了深拷贝和浅拷贝一说。“

面试官:“回答的不错,下一题。”

5c10ef052e077f5520e7f9b31312111a.png

3.检测数据类型

typeof

使用typeof能够检测出数据的类型,当检测基本类型的时候很好用,但是无法检测出对象的具体类型,例如Date和RegExp.看下面的示例。

typeof '1' === 'string'
typeof 1 === 'number'
typeof {} === 'object'
typeof undefined === 'undefined'
typeof null === 'object' // null被认为是一个空对象
typeof Symbol(1) === 'symbol'
typeof NaN === 'number'
typeof Function === 'function'
typeof true === 'boolean'

typeof能够返回的类型有以上几种:string,number,object,function,undefined,symbol,boolean 七种,但是无法知道是不是数组等。

检测数组

检测数组提供了两个操作符,isArray和instanceof。

isArray

Array.isArray() 用于确定传递的值是否是一个 Array

Array.isArray([1,2,3])  // true 

instanceof

用于检测 某个对象是否是某个构造函数的实例

[] instanceof Array // true

var a = {}
a instanceof Object // true

但是检测数组的时候还是建议用isArray,因为某些场景下 instanceof并不能很好的起作用。

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]

Array.isArray(arr);  // true
arr instanceof Array; // false

万金油

Object.protptype.toString.call(obj)

使用这个方法能够准确的检测出所有的类型。

Object.protptype.toString.call({}) === "[object Object]"
Object.protptype.toString.call([]) === "[object Array]"

为什么这样子能够准确的检测类型呢?因为Object的原型对象上有一个toString的方法,所有的其他类型的数值都继承了来自对象的toString方法。但是由于每个类型都各自改写了toString的方法,所以我们必须要调用Ojeact.prototype上的toString方法。看下面?:

[].toString()   // 结果是 “”
Array.prototype.hasOwnProperty('toString') // true
Number.prototype.hasOwnProperty('toString') // true
String.prototype.hasOwnProperty('toString')  // true
我们再删掉数组原型上的toString方法

delete Array.prototype.toString

[].toString()  // [object Array]
Object.protptype.toString.call([]) === "[object Array]"

结果一致,因为删掉了,数组原型上的toString他会根据原型链去查找对象上的toString方法。

延伸

实现一个检测类型的方法:

function detectType (obj) {
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}

js中有三个特殊对象。String,Boolean, Number,通过new关键字生成的都是属于对象类型,如果用typeof检测的话。平时开发的时候需要注意一下。

4.Number

Number 类型应该是 ECMAScript 中最令人关注的数据类型了,这种类型使用 IEEE754 格式来表示 整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)。

什么是IEEE754格式呢?

说实话,我看不太懂,专业术语太多了,记住js浮点型数值采用的双精度,即64位(8个字节)

0.1 + 0.2 !== 0.3的问题

由于0.1和0.2转换成二进制分别是:0.00011001100110011001100(1100无限循环)  0.001100110011001100110011(0011无限循环)。 

js采用的是64位(8字节),所以会舍弃掉后面一些循环的部分,就导致了这类问题的出现。tips: 不要用浮点数进行运算,除非你对精度没有要求。

那么如何解决浮点数计算的问题呢?大家应该都能想到,先转换成整数,然后运算好以后再转换成浮点数。话不多说,上代码。

function transform (num1, num2, type) {
    const typeList = ['add', 'min', 'multi', 'divide']
    if (!num1 || !num2 || !type) {
        throw Error('num1 & num2 & type are required')
    }

    if (!typeList.includes(type)) {
        throw Error("There are four types can be accepted 'add', 'min', 'multi', 'divide'")
    }

    let maxLen = 0
    let copyNum1 = 0
    let copyNum2 = 0
    let l = num1.toString().split('.').length > 2 ? num1.toString().split('.')[1].length : 0
    let r = num2.toString().split('.').length > 2 ? num2.toString().split('.')[1].length : 0
    maxLen = l     copyNum1 = floatToInt(num1, maxLen)
    copyNum2 = floatToInt(num2, maxLen)

    if (type === 'add') {
        return (copyNum2 + copyNum1) / Math.pow(10, maxLen)
    } else if (type === 'min') {
        return (copyNum1 - copyNum2) / Math.pow(10, maxLen)
    } else if (type === 'divide') {
        return copyNum1 / copyNum2
    } else if (type === 'multi') {
        return (copyNum1 * copyNum2) / Math.pow(Math.pow(10, maxLen), 2)
    }
}

function floatToInt (num, len) {
    return num * Math.pow(10, len)
}

5.垃圾收集

JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。而在 C 和 C++之类的语言中,开发人员的一项基本任务就是手工跟踪内存的使用情况,这是造成许多问 题的一个根源。在编写 JavaScript 程序时,开发人员不用再关心内存使用问题,所需内存的分配以及无 用内存的回收完全实现了自动管理。这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变 量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间), 周期性地执行这一操作。下面我们来分析一下函数中局部变量的正常生命周期。局部变量只在函数执行的过程中存在。而在 这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。然后在函数中使 用这些变量,直至函数执行结束。此时,局部变量就没有存在的必要了,因此可以释放它们的内存以供 将来使用。在这种情况下,很容易判断变量是否还有存在的必要;但并非所有情况下都这么容易就能得 出结论。垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来收 回其占用的内存。用于标识无用变量的策略可能会因实现而异,但具体到浏览器中的实现,则通常有两 个策略。

5.1 引用计数

概念就是一旦没有被引用就会被回收。但是存在一种特例就是循环引用的问题。这种情况下就会出现无法回收。导致内存泄漏。

var a = {
    b: 1
}

var c = {
    d: 2
}
a.c = c
c.a = a

此时a和c将得不到回收。最开始js的回收机制就是如此。此时只能手动清除


a = null
c = null

设置为null并不意味着马上会清除。只会为了将其移除执行环境,等待下次收集器回收。

5.2 标记清除

标记清除算法是一种垃圾回收算法,它是第一个可以回收被循环引用的数据结构的垃圾回收算法.现在仍旧有许多常用的垃圾回收技术使用各种各样的标记清除算法的变体.

在使用标记清除算法时,未引用对象并不会被立即回收.取而代之的做法是,垃圾对象将一直累计到内存耗尽为止.当内存耗尽时,程序将会被挂起,垃圾回收开始执行.当所有的未引用对象被清理完毕时,程序才会继续执行.

标记清除算法又被叫做追踪式垃圾收集器,这是因为这种算法追踪被程序所引用的所有对象.在程序中可以直接访问的对象是指通过堆栈上的本地变量或者任意静态变量说引用的对象.从垃圾回收的角度来看,这种对象,叫做根(roots).如果一个对象是被另外的可(直接或者间接)访问的对象中的域引用,则这个对象可以被间接访问.可访问的对象被称为可用的(live),其他的对象被称为垃圾(garbage).

这里讲几个概念

5.2.1 可达性

简单地说,“可达性” 值就是那些以某种方式可访问或可用的值,它们被保证存储在内存中。

5.2.2 根

  1. 全局变量

  2. 内部的

  3. 当前作用域链上的其他变量或者函数

  4. 本地函数的局部变量和参数 这些称之为根

如果引用或者引用链能够在根中找到,那么就说明这个值是可达的。

例如:

var a = {
    b: 'xx'
}
var c= 1
a.b = c

window => a => b => a.b = c a在全局变量中,a中有个b属性的值为变量c,说明c在能够在根中找到,说明c是可达的。如果我们让 a = null,那么在全局中a的引用就丢失了。此时变量c就是不可达的,会被移除执行环境等待被回收。上图:d8a624c86aec49baeea32d115c7055fb.png

切断引用 a = null:700c75ac09962c6e9acc00d09eb936d9.png

当切断的时候,a对b的引用就丢失了,圆圈部分就变得不可达,就会被标记,等待被清除。

常见手写系列

1.instanceof

instanceof的原理是查询是否在原型上出现过,出现过就返回true,否则为false,思路很简单,不断获取左边的__proto__直到等于右边的prototype,则返回true否则等于null的时候为false

function myInstanceOf (left, right) {
    const rightProto = right.prototype
    let leftVaule = left.__proto__

    while (true) {
        if (leftVaule === null) {
            return false
        } else if (leftVaule === rightProto) {
            return true
        }

        leftVaule = leftVaule.__proto__
    }
}

2.tyopeof 原理

不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息。

000: 对象
010: 浮点数
100:字符串
110:布尔
1:整数

typeof null 为"object", 原因是因为 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object",当对象有"[[Call]]"方法的时候会返回function,否则就是object.

3.浅拷贝和深拷贝

3.1 浅拷贝

Object.assign()
Object.create()
// 都是浅拷贝

3.2 深拷贝

1.常见的有JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj))

但是这里有弊端,无法解决循环引用的问题,以及时间类型的拷贝,会丢失掉时区。

2.第二种手写一下,这里进攻仅供参考。需要特殊处理的地方还有很多,比如ArrayBuffer,多纬数组,循环引用等问题。

function detectType (obj) {
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}

function deepClone (parent, obj = {}) {
    const newObj = Array.isArray(obj) ? [] : {}
    for (let key in parent) {
        if (detectType(parent[key]) === 'array') {
            newObj[key] = parent[key].concat([])
        } else if (detectType(parent[key]) === 'regexp') {
            newObj[key] = new parent[key].constructor(obj)
        } else if (detectType(parent[key]) === 'date') {
            newObj[key] = new parent[key].constructor(parent[key].getTime())
        } else if (detectType(parent[key]) === 'object') {
            const temp = {}
            newObj[key] = temp
            deepClone(parent[key], temp)
        } else {
            newObj[key] = parent[key]
        }
    }
    return newObj
}
3.使用lodash等工具库,不过还是要了解原理哦

参考文章

JavaScript垃圾收集——标记清除和引用计数

前端面试:谈谈 JS 垃圾回收机制

关注我和我一起夯实基础

下期主要讲一下对象和函数68af10572aa35068bf0194e0338bbbb7.png

相关文章:

  • python示例程序演示_以Python代码实例展示kNN算法的实际运用
  • python 自动下载脚本_Python脚本自动下载小说
  • jdbc封装工具类代码_[22]-JDBC 工具类优化
  • jpg转dwg格式转换器_如何将PDF或者JPG转CAD格式(dwg格式)?
  • python多线程实现生产者消费者_使用Python多线程实现生产者与消费者模型
  • layui框架和vue哪个好_目前流行的9大前端框架
  • python xlwings筛选_使用Python和xlwings在Excel中查找活动/选定单元格的范围
  • stream distinct去重_会了这些 Stream 操作,再学 Flink 真的简单的不得了
  • ubuntu vscode安装_Visual Studio Code软件的安装和开发环境搭建
  • 只显示小方格_展位设计中的小空间如何运用使空间看上去更大(上)
  • oa系统登录后几分钟自动退出_你知道系统登录有多少种方式吗?
  • python 怕网页_Python解救论文拖延狗
  • xilinx bd修改后sdk如何刷新bsp_如何使用Theos对iOS应用程序进行注入
  • mysql 统计查询总数_详解慢查询日志分析工具mysqlsla--概念、选型、安装及实例说明...
  • python去除注释语句_Python文件去除注释的方法
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • 【React系列】如何构建React应用程序
  • 【个人向】《HTTP图解》阅后小结
  • angular2开源库收集
  • fetch 从初识到应用
  • golang 发送GET和POST示例
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Java深入 - 深入理解Java集合
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Spring Boot快速入门(一):Hello Spring Boot
  • VuePress 静态网站生成
  • 编写符合Python风格的对象
  • 分享几个不错的工具
  • 关于Flux,Vuex,Redux的思考
  • 关于使用markdown的方法(引自CSDN教程)
  • 后端_ThinkPHP5
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 说说动画卡顿的解决方案
  • 写给高年级小学生看的《Bash 指南》
  • 异常机制详解
  • gunicorn工作原理
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • (11)MATLAB PCA+SVM 人脸识别
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (Forward) Music Player: From UI Proposal to Code
  • (Java数据结构)ArrayList
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (汇总)os模块以及shutil模块对文件的操作
  • (三)终结任务
  • (算法二)滑动窗口
  • (一)基于IDEA的JAVA基础12
  • (转)Windows2003安全设置/维护
  • (转)我也是一只IT小小鸟
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • *2 echo、printf、mkdir命令的应用
  • .cfg\.dat\.mak(持续补充)
  • .NET Reactor简单使用教程