最近笔者在复习前端的内容,感受到了前端知识体量的庞大,于是将一些最近看的知识点汇总分享,也方便以后查看。
JS 类型
原始类型
特点:原始类型的的值保存在本地,且赋值给其他变量后,其他变量值的改变并不影响原先的值
- Boolean(保存真或假的类型)
- Number(保存数字类型)
- String(保存字符串类型)
- null(不指向任何地址,用于手动赋值,清空内容)
- undefined(变量未赋值时的默认值)
- symbol(作为唯一标识符,也可作为对象的唯一属性名)
- BigInt(最新的原始类型,它提供了一种方法来表示大于
253 - 1
的整数)
symbol 和 Bigint
symbol 和 Bigint 是es6和es7新增的原始类型,这里就记录的更详细一点
symbol
-
我们常用
const a =Symbol()
来创建一个symbol类型的值 但要注意的是不能使用new操作符,因为通过new实例化的是一个对象,而不是symbol -
Symbol方法接收一个参数,即使传入的参数相同,生成的symbol值也不相等,因为Symbol的独一无二
const foo = Symbol('foo'); const bar = Symbol('foo'); console.log(foo === bar); // false
bigint
- 用来描述整数的类型,填补了整数范围的空缺
- 在整数后加n 或者调用BigInt函数
- BigInt大多数情况下和常规数字一样使用但不可以和常规数字混合使用
引用类型
特点:引用类型的值是保存堆内存中的对象,栈中存储的变量,只是用来查找堆中的引用地址。
- Object类型
- Array类型
- Data类型
- RegExp类型
- Function类型
- … 引用类型的知识并不少,这里推荐一篇掘金好文给想要深入了解的小伙伴 你必须理解的 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 表示的所有机器码均为0
,undefined用 −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)
}