JavaScript面试

✨♻️ JavaScript Visualized: Event Loop
Oh boi the event loop. It’s one of those things that every JavaScript developer has to deal with in o...
https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif

Q1: const alice = new Person("Alice")做了什么?
1)alice={}
2)alice.__proto__ = Person.prototype
3) Person.call(alice, "Alice")
4) return alice

Q2: this指向问题
//普通函数的this在被调用时决定
1) foo()//this->window(browser)/global(node)/undefined(use strict)
2) obj.foo()//this->obj(实例对象调用方法,this指向实例,无论方法定义在实例上还是prototype上)
		//变式1
		Obj.prototype.foo()//this->Obj.prototype
		//变式2
		foo2=Obj.prototype.foo
		foo2//this->global
3)call(),apply(),bind()

//箭头函数的this在被定义时决定//被定义时=加载进内存时
4)在定义时由外层作用域(函数)的this决定,不会改变
->不能使用箭头函数作为构造函数,因为箭头函数的this不能动态绑定/和new关键字不兼容


setTimeout 是在 全局作用域(window / global) 执行的
setTimeout 不会创建新的作用域,只是延迟执行函数。
 -setTimeout(普通函数)会使作用域变成global
 -setTimeout(箭头函数)作用域还是定义时候决定

Q3: 继承写法

1)Animal.call(this, name) 继承 Animal 的实例属性。
2)Dog.prototype = Object.create(Animal.prototype) 继承了 Animal 的原型方法。
3)Dog.prototype.constructor = Dog 纠正 constructor 指向。

function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  Animal.call(this, name); // 1)继承 Animal 的属性
  this.breed = breed;
}

// 2)继承 Animal 的方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 3)修正 constructor 指向

Q4: 作用域和闭包
闭包:当一个函数访问其外部作用域的变量时,就形成了闭包。
作用域链:当前作用域找不到变量时,会沿着作用域链向上查找,直到找到变量或者报错 (ReferenceError)。

变量提升(Hoisting):
var 声明的变量 和 函数声明 移动到其作用域的顶部,
但let 和 const 不会被提升到可访问的状态。
let 和 const 变量 会被提升(Hoisting),但不会初始化,因此在赋值前访问会导致 ReferenceError(处于暂时性死区)。
Hoisting初始化前访问Scope
let/constyesreference errorblock
varyesundefinedfunction
函数声明yesyes


Var的function scope和hoisting,导致了一些意想不到的行为,尤其是在循环、闭包、异步操作等场景中。例如,
for 循环中的 var 变量可能在所有迭代中共享同一个变量,导致结果不如预期。

ES6+使用const/let,更加明了。


for...in vs for...of:适用对象 vs 适用数组

for(let idx in nums) : 遍历键 | 对象 (Object) | 坑:arr的非索引属性/obj的原型链上的可枚举属性

for(let num of nums) : 遍历值 | 可迭代对象 (Array, Map, Set, String)

异步:
- callback hell:回调嵌套层数太多,难以维护
- event loop:执行所有同步代码 -> (清空微任务 -> 执行一个宏任务)循环
	- micro task:Promise.then、process.nextTick
	- macro task:setTimeout、setImmediate、I/O
	- 创建Promise的代码是同步执行的
- await:
	- Promise的语法糖
	- const result = await Promise.resolve(val)
	- 等待Promise resolved,返回Promise的解析值
	- 使用try{}catch(error){} 处理rejected

Promise:pending->fulfilled/rejected
Promise实例的方法:
	then(),fulfilled执行
	catch(),rejected执行
	finally(),最后一定被执行
	then/catch 会返回 fulfilled 状态的 Promise(async的返回值也是一个Promise)
Promise类的方法:
	resolve(val),状态pending->fulfilled,并返回解析值
	reject(val),状态pending->rejected,并返回错误原因
	race(),接收一个Promise数组,返回状态最先发生变化的Promise对象的结果/原因。
	all(),接收一个Promise数组,生成一个新的Promise,所有参数Promise全部fulfilled才解决
	
Promise.resolve(val)创造一个立即解决的Promise

new Promise(executor):参数为executor函数
executor函数有resolve,reject两个参数,这两个参数由js引擎提供,需要手动传入
Const p = new Promise((resolve,reject)=>{ })

工厂模式 是一种封装对象创建逻辑的设计模式,用一个工厂函数来创建对象,而不是在代码中直接使用 new

单例模式 是一种 创建型设计模式,它确保一个类 只有一个实例,并提供一个全局访问点。(日志,全局配置管理)

代理模式的核心思想是在访问某个对象时,引入一个“代理”对象来控制、修改或延

观察者模式让多个observer“观察”一个对象(subject),当这个对象变化时,所有观察者都会收到通知。(addEventListener


防抖:在事件触发后
等待一段时间,如果在等待时间内事件又被触发,则重新计时。适用于用户输入框搜索等场景。

var debounce = function(fn, t) {
    let timer
    return function(...args) {
        clearTimeout(timer)
        timer = setTimeout(()=>fn.apply(this, args), t)
    }
};

节流:限制单位时间内函数的执行次数,适用于滚动监听、按钮点击等场景。

Other: