暂时性死区
JS判断类型
- typeof
检测不出null 和 数组,结果都为object,所以typeof常用于检测基本类型 - instanceof
不能检测出number、boolean、string、undefined、null、symbol类型
所以instancof常用于检测复杂类型以及级成关系 - constructor
null、undefined没有construstor方法,因此constructor不能判断undefined和null - Object.prototype.toString.call
可以判断任意类型
普通函数和箭头函数的区别
- 普通函数
可以通过bind、call、apply改变this指向
可以使用new关键字 - 箭头函数
自身没有this指向,this继承自外层
不能通过bind、call、apply改变this指向
不能使用new关键字,因为箭头函数没有constructor
没有arguments
栈和堆的区别
- 栈
原始数据类型保存到栈内存中
占据空间小、大小固定
属于被频繁使用数据,所以放入栈中存储 - 堆
引用数据类型保存到堆内存中
占据空间大、大小不固定
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。
当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
undefined 和 null 区别
- undefined
定义变量,但是没有赋值
undefined是从null派生出来的 - null
表示一个空对象引用 - 区别
typeof undefined // undefined
typeof null // null
undefined == null // true
undefined === null // false
eval()的作用
造成内存泄露的操作
- 闭包
- 意外的全局变量
- 被遗忘的定时器或回调
- 没有清理的dom元素(赋值一个dom给一个变量,又removeChild这个dom,变量依旧存在内存中)
- 事件监听: 没有正确销毁 (低版本浏览器可能出现)
垃圾回收机制方式及内存管理
垃圾回收机制
- 标记清除法
当变量进入环境时,将这个变量标记为'进入环境'。当标记离开环境时,标记为‘离开环境’。离开环境的变量会被回收 - 引用技计数法
跟踪记录每个值被引用的次数,如果没有被引用,就会回收
- 标记清除法
- 内存管理
分配内存 > 使用内存 > 回收内存
闭包
- 闭包就是能够读取其它函数内部变量的函数
- 使用方法:在一个函数内部创建另一个函数
- 最大用处有两个:读取其他函数的变量值,让这些变量始终保存在内存中
- 缺点:会引起内存泄漏(引用无法被销毁,一直存在)
new关键字执行的操作
- 创建一个新对象
- 空对象的__proto__指向构造函数的原型对象
- 构造函数this指向替换成新对象
- 返回新对象
下面的代码输出的内容
function Fn() { return 1 }
console.log(new Fn) // 没有参数时,括号可以省略
function Fn2() { return true }
console.log(new Fn2)
答案:
- Fn {}
- Fn2 {}
当一个构造函数不是返回对象时,再去new这个构造函数,会强行返回由这个构造函数创建的新对象
script延迟引入的方式
defer属性
<script src="" defer></script>
立即下载,但是会等到整个页面都解析完成之后再执行
async属性
<script src="" async></script>
不让页面等待脚本下载和执行(异步下载),但是无法控制加载的顺序
- 动态创建script标签
- 使用定时器延迟
- 将script标签放在页面的最后
同步任务与微任务及宏任务
- 同步任务按顺序执行
- 微任务和宏任务皆为异步任务
- 宏任务一般是:setTimeout,setInterval、setImmediate
- 微任务一般是:promise
- 执行顺序:同步任务 > 微任务 > 宏任务
for...in 和 object.keys的区别
- Object.keys不会遍历继承的原型属性
- for...in 会遍历继承的原型属性
["1", "2", "3"].map(parseInt) 结果
结果:[1, NaN, NaN]
原理:
// map方法接收一个参数,并会传递三个参数:el, index, arr
[].map((el, index, arr) => {})
// 因此标题中的表达式可以看成下方的
["1", "2", "3"].map((el, index, arr) => {
return parseInt(el, index)
})
// parseInt("1", 0) 1
// parseInt("2", 1) NaN
// parseInt("3", 2) NaN
创建对象的3种方式
- 字面量方式创建
const obj = {} - 通过new关键字创建
const obj = new Object() - 通过Object身上的方法创建
Object.create(null) // create创建的对象原型对象上没有继承Object的原型方法
创建数组的方式
- 字面量方式创建
const arr = [] - 通过new关键字创建
const arr = new Array(1, 2, 3) - 通过Array身上的方法创建
const arr = Array.of(1, 2, 3)
Number() 的存储空间
Number类型的最大值为Number.MAX_SAFE_INTEGER。即Math.pow(2, 53) - 1 // 9007199254740991
超过了这个大小,就会截取造成精度丢失
在项目中,后端有时候会返回一个id字段,这个id可能是数字,而这个数字就有可能超出js的最大值。
例如:
const id = 9007199254740992564
console.log(id) // 9007199254740993000 将超出精度值后面的数字全部转换成了0
解决方案:
- 服务端解决,不要返回超过精度大小的数字
- 通过json-bigint库解决
0.1 + 0.2 === 0.3 ?
symbol用在哪
- symbol可以用来部署接口,例如对象身上没有接口,可以通过symbol来实现接口
之前写的文章:https://78.al/post/106.html - symbol可以防止命名冲突
之前写的文章:https://78.al/post/152.html
typeof NaN ? NaN是否等于NaN,判断一个NaN是否等于NaN
- NaN的意思是非数值,本质还是数字类型,因此 typeof NaN = 'number'
并且NaN.constructor === Number - 据我所知NaN是不等于NaN的,原因不知道
- 如果想强行判断NaN等于NaN的话,可以通过Object.is方法
Object.is(NaN, NaN) // true
判断一个对象是不是空对象
通过JSON方法
const obj = { name: '1' } console.log(JSON.stringify(obj) === '{}');
通过Object.keys方法
const obj = { name: '1' } console.log(Object.keys(obj).length === 0);
通过Object.getOwnPropertyNames这个和上面的差不多
const obj = { name: '1' } // getOwnPropertyNames 用于获取对象自身的属性 console.log(Object.getOwnPropertyNames(obj).length);
原型
将伪数组转换成真实数组的方法
- 通过...展开运算符
- 通过Array.from()方法
- 通过Array.prototype.slice.apply(arguments)
手撸一个扁平化数组的方法
const arr = [1, 2, [3, [4, [5]]]]
function flat(arr) {
let result = []
arr.forEach(item => {
if (Array.isArray(item)) {
result = [...result, ...flat(item)]
} else {
result = [...result, item]
}
});
return result
}
console.log(flat(arr));
获取页面上一共使用了多少种元素
console.log([...new Set([...document.querySelectorAll('*')].map(el => el.tagName))]);
手动实现 instanceof 方法
function myInstanceof(left, right) {
/* 定义右侧的原型,例如 Object.prototype */
const rightPrototype = right.prototype
/* 定义初始时左侧的实例的原型 */
let leftProto = left.__proto__
while (1) {
/* 如果左侧的原型找到了顶层null,表示没有匹配到与右侧对应的原型 */
if (!leftProto) return false
/* 如果左侧的原型等于了右侧构造函数的原型,则说明是右侧构造函数创建了左侧的实例 */
if (leftProto === rightPrototype) return true
/* 将左侧的原型继续往上层原型赋值,最终会赋值成null */
leftProto = leftProto.__proto__
}
}
/* 判断obj是否是对象类型 */
console.log(myInstanceof({}, Object));
简单实现节流和防抖
(1)节流
let _last = +new Date()
document.querySelector("input").addEventListener('input', () => {
const _now = +new Date()
if (_now - _last > 100) console.log(1);
_last = _now
})
(2)防抖
let _time = null
document.querySelector("input").addEventListener('input', () => {
clearTimeout(_time)
_time = setTimeout(() => {
console.log(1);
}, 300);
})
从输⼊ url 到展示的过程
模块化
- 解决命名冲突
- 提供复⽤性
- 提⾼代码可维护性
模块化有以下类型:
- iife:立即执行函数类型
- amd:requirejs
- cmd:seajs
- es:es6模块化
- umd:jquery源码开头就是umd规范
- system:Symtem.js
- commonjs:nodejs
1 instanceof Number ?
jQuery 链式调用怎么实现的 ?
这里使用原生js来模拟这样的效果
let fun = {
fun1: function() {
console.log("fun1");
return this;
},
fun2: function() {
console.log("fun2");
return this;
},
fun3: function() {
console.log("fun3");
return this;
}
}
fun.fun1().fun2().fun3();
这样就可以连续的输出字符串的fun1、fun2、fun3 了,原因是在每个方法后面加了一个return this
评论 (0)