从0开始的前端复习生活

1,650 阅读5分钟

最近笔者在复习前端的内容,感受到了前端知识体量的庞大,于是将一些最近看的知识点汇总分享,也方便以后查看。

JS 类型

原始类型

特点:原始类型的的值保存在本地,且赋值给其他变量后,其他变量值的改变并不影响原先的值

  1. Boolean(保存真或假的类型)
  2. Number(保存数字类型)
  3. String(保存字符串类型)
  4. null(不指向任何地址,用于手动赋值,清空内容)
  5. undefined(变量未赋值时的默认值)
  6. symbol(作为唯一标识符,也可作为对象的唯一属性名)
  7. BigInt(最新的原始类型,它提供了一种方法来表示大于 253 - 1 的整数)

symbol 和 Bigint

symbol 和 Bigint 是es6和es7新增的原始类型,这里就记录的更详细一点

symbol

  1. 我们常用 const a =Symbol() 来创建一个symbol类型的值 但要注意的是不能使用new操作符,因为通过new实例化的是一个对象,而不是symbol

  2. Symbol方法接收一个参数,即使传入的参数相同,生成的symbol值也不相等,因为Symbol的独一无二

const foo = Symbol('foo'); const bar = Symbol('foo'); console.log(foo === bar); // false

bigint

  1. 用来描述整数的类型,填补了整数范围的空缺
  2. 在整数后加n 或者调用BigInt函数
  3. BigInt大多数情况下和常规数字一样使用但不可以和常规数字混合使用

引用类型

特点:引用类型的值是保存堆内存中的对象,栈中存储的变量,只是用来查找堆中的引用地址。

  1. Object类型
  2. Array类型
  3. Data类型
  4. RegExp类型
  5. Function类型
  6. ....... 引用类型的知识并不少,这里推荐一篇掘金好文给想要深入了解的小伙伴 你必须理解的 JavaScript 知识 —— 引用类型 (juejin.cn)

对象转原始类型

  • 如果需要转字符串就调用 x.toString(), 转换为基础类型的话就直接返回。不是字符串类型的话就先调用 valueof,结果不是基础类型的话再调用 toString
  • 调用 x.valueof(),如果转换为基础类型,就直接返回
  • 如果没有返回原始类型,就会报错

字符串与数字的转换

  • 数字转字符串 tostring ()
  • 字符串转数字 parseInt() ,number() ,字符串(数字)前加"+"

函数参数是对象会发生什么

  • 会造成数据污染,函数传参如果是对象,传的是对象的引用地址,如果函数内对对象进行修改,函数外的对象也会发生改变,函数内原先的对象,会开辟一个新的指针,返回新的对象。

判断JS类型

typeof

作用:能够判断除null以外的原始类型,还能判断Function以及除Function外的Object类型

实现原理

JS在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息如000:对象 010:浮点数 但对于 null 和 undefined来说,null 表示的所有机器码均为0undefined用 −2^30 整数来表示。所以 typeof 会将 null 判断为 Object ,这也是 js 的历史遗留 bug

instanceof

作用:能够判断一个实例是否属于某种类型,也可以判断一个实例是否是其父类型或者祖先类型的实例

实现原理

instanceof 的主要实现原理就是遍历左边变量的原型链,直到找到右边变量的 prototype 在左边变量的原型链上即可,简单来说就是实例对象的隐式原型=构造函数的显示原型,这里我们手动实现一个instanceof

function _instanceof(left, right) {
    let L = left.__proto__
    //定义L 为左边的隐式原型
    let R = right.prototype
    //定义R 为右边的显示原型
    while (L !== null) {
        if (L === R) return true
        // 当左边的隐式原型等于右边的显示原型时返回 true
        L = L.__proto__
        // 两边不等时 在原型链向上找
    }
    return false
}

其他方法

Object.prototype.toString.Call()

在使用上,Object的原型方法,其他对象通过call/apply调用 -> [Object xxx] ,能够判断所有的数据类型

constructor

通过构造函数new出来的对象都有一个constructor属性,来指向它的构造函数如: new String('1').constructor === String,并且除了对字符串、数组、window、document可以直接进行判断,对其他的数据类型,必须要是被new出来的实例对象才能使用该方法判断类型,该方法不能判断 null 和 instaceof 类型

Array.isArray()

用于判断对象是不是数组

经典题目

for (var i = 0; i <= 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, i * 1000)
}

这是一道很经典的题目,相信很多人清楚它的输出结果 6个6 这里可以将var改为let或者在setTimeout中加入第三个参数解决,这里主要是运用闭包解决。首先了解一些前置知识

闭包

产生原因

函数内部返回另一个函数

函数内部引用外部函数

定义

在 js 中,根据词法作用域的规则,内部函数总是可以访问外部函数中申明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束,但内部函数引用的变量依然保存在内存中,我们把这些变量的集合称为闭包。

setTimeout的第三个参数

setTimeout许多小伙伴都知道怎么使用,这里主要是讲setTimeout的参数

在平时的使用时我们一般只用到前两个参数

setTimeout ( code , ms )

  • code:延迟时间到期之后执行的代码块
  • ms:延迟时间

事实上在setTimeout后面可以加无数个参数,这些参数会作为前面回调函数的附加参数,比如

setTimeout((a,b,c) => { console.log(a,b,c) }, 2000, 1, 2, 3); // 1 2 3

解决

首先我们要产生一个闭包,如果我们用一个具名函数,我们就必须去调用它。所以我们自然而然的会想到自执行函数,先创建一个自执行函数,然后将setTimeout函数放入。这时我们回头看闭包的产生条件,发现它并没有返回一个函数啊,这里要从setTimeout函数的特殊性说起,setTimeout作为一个典型异步函数,无论是否return,setTimeout函数都会放入任务队列中,在外面执行。这里附下代码

for (var i = 0; i <= 5; i++) {
    (function (j) {
        setTimeout(function () {
            console.log(j);
        }, j * 1000)
    })(i)
}

另外两种解决方式

for (var i = 0; i <= 5; i++) {
    setTimeout(function (j) {
        console.log(j);
    }, i * 1000,i)
}
for (let i = 0; i <= 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, i * 1000)
}