1、instanceof的原理是什么?
概念:instanceof 运算符 用于检测 构造函数的 prototype 属性 是否出现在某个 实例对象 的 原型链 上。
基本概念
- 函数的
prototype属性:函数的原型对象,用于定义实例共享属性和方法。 - 构造函数:用于创建和初始化对象。
- 构造函数的实例对象:通过 new 一个 构造函数 得到的 对象。
构造函数 VS 构造函数的 prototype 属性
- 分离职责:构造函数负责初始化实例属性,
prototype负责存储共享方法。 - 原型链机制:通过
__proto__连接实例和prototype,实现属性和方法的继承。 - 内存优化:共享方法只需存储一次,避免重复定义。
速记
- 可以把
instanceof理解为 “是否是…… 的实例”。 arr instanceof Array就可以理解为 “数组arr是否是Array这个构造函数创建的实例”。
缺点:
- 由于instanceof是基于原型链的检查,因此如果某个对象的原型链比较深,那么检查的效率会比较低。
- 只能用于检查对象是否是某个构造函数的实例,不能用于基本类型(如字符串、数字等)的检查。
手写
// let arr = [1,2,3]; arr instanceof Array; => true
function myInstanceof(obj, constructor) {
let proto = Object.getPrototypeOf(obj)
while (proto) {
if(proto === constructor.prototype) return true
proto = Object.getPrototypeOf(proto)
}
return false
}
2、new 及 对象实例创建的三种方式
概念:运算符,用于创建 构造函数或类的实例。其核心流程如下:
- 创建空对象:在内存中分配一个新对象。
- 设置原型链:将新对象的
__proto__指向构造函数的prototype。 - 绑定
this:将构造函数中的this绑定到新对象。 - 执行构造函数:初始化实例属性。
- 返回实例:默认返回新对象(除非构造函数显式地返回其他对象)。
手写:
function myNew (Constructor, ...args) {
var obj = Object.create(Constructor.prototype); // 1,2
var result = Constructor.apply(obj, args); // 3,4
return (typeof result === 'object' && result !== null) ? result : obj; // 5
}
拓展: 创建对象一共有三种方式:
- new操作符:用于创建构造函数或类的实例,原型链固定为构造函数的
prototype。 - 对象字面量:隐式调用
Object构造函数,原型链固定为Object.prototype。 Object.create():显式指定原型,灵活性更高,不依赖构造函数。
const prototype = { a: 1 };
const obj = Object.create(prototype, { b: { value: 2 } }); // b 默认为不可枚举属性,如果想要能够被枚举,需要设置b 的 enumerable属性 为 true。
// 原型链:obj.__proto__ === prototype
三者的创建原理不同,new 和 {} 基于构造函数,而 Object.create() 直接控制原型链。
易错点加深:
apply:应用的意思,把一个函数应用到某个对象上,并以数组的形式传递参数。
3. defer 和 async
概念
- defer(延迟的顺序执行,执行被延迟到 HTML 解析完成后)。
- async(异步的无序执行,加载完成立即执行,执行顺序取决于网络加载速度)。
实际应用建议
- 优先使用
async:若脚本无依赖且无需按顺序执行,可提升首屏渲染速度。 - 使用
defer:若脚本需按顺序执行或依赖 DOM 结构(如 jQuery 插件)。 - 避免同时使用:两者不可叠加,否则以
async为准。 - 内联脚本无效:
defer和async仅对外部脚本(src属性)生效。
4、手写防抖
function debounce(func, delay, immediate = false) {
let timer = null
return function(...arg) {
const execNow = immediate && !timer
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
if(!immediate) {
func.apply(this ,arg)
}
timer = null
}, delay)
if(execNow) {
func.apply(this ,arg)
}
}
}
5、手写去重
function unique(arr) {
const result = []
const seen = new Map()
for (let item of arr) {
if(!seen.has(item)) {
seen.set(item, true)
result.push(item)
}
}
return result
}
6、实现EventBus
class EventBus {
constructor() {
this.events = {}
}
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(callback)
}
emit(eventName, ...args) {
if(this.events[eventName]){
[...this.events[eventName]].forEach(fn => fn(...args))
}
}
off(eventName, callback) {
if(this.events[eventName]){
this.events[eventName].filter(cb => cb != callback)
}
if(!this.events[eventName].length) {
delete this.events[eventName]
}
}
once(eventName. callback) {
}
}