文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
JS 基础
变量类型和计算
typeof 能判断哪些类型
- 识别所有值类型
- 识别函数
- 判断是否是引用类型(不可再细分)
何时使用 === 何时使用 ==
除了 == null(null == undefined) 外,其他都一律用 ===
值类型和引用类型的区别
值类型:栈
引用类型:栈 + 堆
值类型 / 引用类型的拷贝
手写深拷贝 ⭐️
- 判断值类型和引用类型
- 判断是数组还是对象
- 递归
function deepClone(obj = {}) {
if (typeof obj !== "object" || obj == null) {
return obj;
}
let result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// 自己的属性
if (obj.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(obj[key]);
}
}
return result;
}
原型和原型链
如何准确判断一个变量是不是数组
- a instanceof Array
- Array.isArray()
class 的原型本质,怎么理解
class 实际上是函数,语法糖
- 每个 class 都有显示原型 prototype
- 每个实例都有隐式原型 proto
- 实例的 proto 指向对应 class 的 prototype
手写一个简易的 jQuery,考虑插件和扩展性
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector);
const length = result.length;
for (let i = 0; i < length; i++) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index];
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i];
fn(elem);
}
}
on(type, fn) {
return this.each((elem) => {
elem.addEventListener(type, fn, false);
});
}
}
// 插件
jQuery.prototype.dialog = function (info) {
alert(info);
};
// "造轮子"
class MyJQuery extends jQuery {
constructor(selector) {
super(selector);
}
// 扩展自己的方法
addClass(className) {
// ...
}
}
作用域和闭包
this 的不同应用场景,如何取值
this 的取值是在函数执行时确定,不是在函数定义时确定!
- 当做普通函数被调用(window)
- 使用 call、apply、bind(传入的对象)
- 作为对象方法被调用(对象)
- 在 class 的方法中调用
- 箭头函数(定义时的上级作用域)
手写 bind 函数
Function.prototype.bind = function () {
// 将参数拆解为数组
const args = Array.prototype.slice.call(arguments);
// 获取 this(数组第一项)
const t = args.shift();
// fn.bind(...) 中的 fn
const self = this;
// 返回一个函数
return function () {
return self.apply(t, args);
};
};
实际开发中闭包的应用场景,举例说明
- 隐藏数据,只提供 API
function createCache() {
const data = {};
return {
set: function (key, val) {
data[key] = val;
},
get: function (key) {
return data[key];
},
};
}
闭包:所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方(静态作用域)!!!
答案:100, 100
异步
同步和异步的区别是什么?
- 基于 JS 是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
手写用 Promise 加载一张图片 ⭐️
function loadImg(src) {
return new Promise((resolve, reject) => {
const img = document.createElement("img");
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error("图片加载失败"));
};
img.src = src;
});
}
Callback Hell
前端使用异步的场景有哪些?
- 网络请求,如 ajax 图片加载
- 定时任务,如 setTimeout、setInterval
场景题
答案:1、3、5、4、2
异步进阶
请描述 event loop 的机制,可画图 ⭐️
- 同步代码,一行一行放在 Call Stack 中执行
- 遇到异步,会先 “记录” 下,等待时机(定时、网络请求等)
- 时机到了,就移动到 Callback Queue
- 如果 Call Stack 为空(即同步代码执行完),Event Loop 开始工作
- 轮询查找 Callback Queue,如有则移动到 Call Stack 执行
- 然后继续轮询查找(永动机)
Promise 有哪三种状态,如何变化
三种状态:
- pending、resolved、rejected
- pending → resolved 或 pending → rejected
- 变化不可逆
状态表现:
- pending 状态,不会触发 then 和 catch
- resolved 状态,会触发后续的 then 回调函数
- rejected 状态,会触发后续的 catch 回调函数
async/await 和 Promise 的关系
- 执行 async 函数,返回的是 Promise 对象
- await 相当于 Promise 的 then
- try…catch 可捕获异常,代替了 Promise 的 catch
Event Loop 和 DOM 渲染
- JS 是单线程,而且和 DOM 渲染共用一个线程
- JS 执行时,得留一些时机供 DOM 渲染
- 每次 Call Stack 清空,即同步任务执行完
- 都是 DOM 重新渲染的机会,DOM 结构如有改变则重新渲染
- 然后再去触发下一次 Event Loop
什么是宏任务和微任务,两者有什么区别
- 宏任务:setTimeout、setInterval、ajax、dom 事件
- 微任务:Promise、async/await
- 微任务执行时机比宏任务要早
- 宏任务在 DOM 渲染后触发,如 setTimeout
- 微任务在 DOM 渲染前触发,如 Promsie
手写 Promise⭐️
- 初始化 & 异步调用
- then catch 链式调用
- API .resolve .reject .all .race
class MyPromise {
state = "pending";
value = undefined;
reason = undefined;
resolveCallbacks = []; // pending 状态下,存储成功的回调
rejectCallbacks = []; // pending 状态下,存储失败的回调
constructor(fn) {
const resolveHandler = (value) => {
if (this.state === "pending") {
this.state = "fulfilled";
this.value = value;
this.resolveCallbacks.forEach((fn) => fn(this.value));
}
};
const rejectHandler = (reason) => {
if (this.state === "pending") {
this.state = "rejected";
this.reason = reason;
this.rejectCallbacks.forEach((fn) => fn(this.reason));
}
};
try {
fn(resolveHandler, rejectHandler);
} catch (err) {
rejectHandler(err);
}
}
then(fn1, fn2) {
// pending 状态下,fn1、fn2 被保存
fn1 = typeof fn1 === "function" ? fn1 : (v) => v;
fn2 = typeof fn2 === "function" ? fn2 : (e) => e;
if (this.state === "pending") {
return new MyPromise((resolve, reject) => {
this.resolveCallbacks.push(() => {
try {
const newValue = fn1(this.value);
resolve(newValue);
} catch (err) {
reject(err);
}
});
this.rejectCallbacks.push(() => {
try {
const newReason = fn2(this.reason);
reject(newReason);
} catch (err) {
reject(err);
}
});
});
}
if (this.state === "fulfilled") {
return new MyPromise((resolve, reject) => {
try {
const newValue = fn1(this.value);
resolve(newValue);
} catch (err) {
reject(err);
}
});
}
if (this.state === "rejected") {
return new MyPromise((resolve, reject) => {
try {
const newReason = fn2(this.reason);
reject(newReason);
} catch (err) {
reject(err);
}
});
}
}
catch(fn) {
return this.then(null, fn);
}
}
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => resolve(value));
};
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => reject(reason));
};
MyPromise.all = function (promiseList = []) {
return new MyPromise((resolve, reject) => {
const result = [];
const length = promiseList.length;
let resolveCount = 0;
promiseList.forEach((p) => {
p.then((data) => {
result.push(data);
// resolveCount 必须在 then 里面做 ++
resolveCount++;
if (resolveCount === length) {
resolve(result);
}
}).catch((err) => {
reject(err);
});
});
});
};
MyPromise.race = function (promiseList = []) {
let resolved = false;
return new MyPromise((resolve, reject) => {
promiseList.forEach((p) => {
p.then((data) => {
if (!resolved) {
resolve(data);
resolved = true;
}
}).catch((err) => {
reject(err);
});
});
});
};
场景题
then 和 catch 改变状态:
- then 正常返回 resolved promise,里面有报错则返回 rejected promise
- catch 正常返回 resolved promise,里面有报错则返回 rejected promise
第一题:1、3
第二题:1、2、3
第三题:1、2
第一题:a → Promise 对象、b → 100
第二题:‘start’、100、200、报错…
答案:100、400、300、200
答案:‘script start’ → ‘async1 start’ → ‘async2’ → ‘promise1’ → ‘script end’ → ‘async1 end’ → ‘promise2’ → ‘setTimeout’
执行时机: 同步代码执行完毕 → 执行微任务 → 触发 DOM 渲染 → 触发 Event Loop,执行宏任务。
要点: 初始化 promise 时,传入的函数会立刻被执行;await 后面的都作为回调内容(微任务)。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论