119期
1. 什么是浏览器的同源策略?解决什么问题?如何避免?
2. 什么是git cherry-pick?通常什么场景下使用?
3. iconfont的原理是什么?
上面问题的答案会在第二天的公众号(程序员每日三问)推文中公布
也可以小程序刷题,已收录500+面试题及答案
118期问题及答案
1. js中[]==[]的结果是什么?为什么
在JavaScript中,[] == []
的结果是 false
。它是由JavaScript的比较规则决定的。
JavaScript中的 ==
运算符用于比较两个值是否相等。当使用 ==
进行比较时,它首先会检查操作数的类型,然后再比较它们的值。对于 [] == []
,以下是发生的情况:
两个空数组被创建。尽管它们的内容是一样的(都是空数组),但它们是两个不同的对象实例。
==
运算符首先比较它们的类型,它们都是对象。接下来,它比较它们的引用。由于这两个数组是不同的实例,它们具有不同的引用。
因此,尽管它们的内容相同,但由于引用不同,
[] == []
的结果是false
。
要检查两个数组是否具有相同的内容,你应该使用严格相等运算符 ===
,或者使用比较它们的内容,而不是引用的方法。例如,你可以使用 JSON.stringify
将数组转换为字符串,然后进行比较:
JSON.stringify([]) === JSON.stringify([]); // true
这将比较数组的内容而不是它们的引用。
2. 什么是观察者设计模式?通常有哪些应用场景?
观察者设计模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,使一个对象(主题或被观察者)的状态发生变化时,所有依赖它的对象(观察者)都会得到通知并自动更新。观察者模式通常包括以下几个角色:
主题(Subject):也称为被观察者,它维护一组观察者对象,并提供方法来添加、删除和通知观察者。当主题的状态发生变化时,它通知所有注册的观察者。
观察者(Observer):观察主题的对象,它定义了一个更新接口,用于在主题状态发生变化时接收通知和执行相应操作。
具体主题(Concrete Subject):主题的具体实现,它维护了一组观察者,状态发生变化时通知观察者。
具体观察者(Concrete Observer):观察主题的具体实现,实现了观察者接口,并在接收到通知时执行具体的操作。
观察者设计模式的主要目标是实现对象之间的松耦合,使主题和观察者之间的关系动态且可扩展。这种模式在很多应用场景中都非常有用,其中一些典型的应用场景包括:
事件处理:观察者模式常用于事件处理系统,其中事件源(主题)可以触发事件,而事件处理程序(观察者)订阅并响应事件。
用户界面:在GUI应用程序中,按钮、文本框等用户界面元素可以作为主题,当它们的状态发生变化时,会通知订阅的UI组件(观察者)更新显示。
发布-订阅模式:观察者模式是发布-订阅模式的基础,用于构建事件总线或消息队列系统,允许组件订阅和发布消息。
数据更新:在数据驱动的应用中,当数据模型发生变化时,视图(观察者)需要自动更新以反映最新的数据状态。
消息通知:在通知系统中,观察者模式可以用来订阅和接收特定类型的消息。
股票市场:股票市场中的股票价格变化可以作为主题,各种投资者可以作为观察者,订阅股价变化并采取相应的投资行动。
多语言支持:在多语言应用中,语言选择可以作为主题,UI元素可以作为观察者,以便在语言更改时刷新UI。
观察者设计模式使得系统更具弹性,能够轻松扩展新的观察者,同时保持主题和观察者之间的松耦合。这有助于分离关注点,提高代码的可维护性和可扩展性。 下面我将通过一个示例来说明观察者设计模式的应用场景。假设我们有一个新闻发布系统,新闻编辑器(主题)发布新闻,而订阅者(观察者)是不同的新闻订阅者。当新闻编辑器发布新闻时,所有订阅者都会收到通知并获取最新的新闻。
// 主题(新闻编辑器)
class NewsEditor {
constructor() {
this.subscribers = []; // 订阅者列表
}
// 添加订阅者
subscribe(subscriber) {
this.subscribers.push(subscriber);
}
// 移除订阅者
unsubscribe(subscriber) {
this.subscribers = this.subscribers.filter(sub => sub !== subscriber);
}
// 发布新闻
publishNews(news) {
console.log(`发布新闻: ${news}`);
this.notifySubscribers(news);
}
// 通知所有订阅者
notifySubscribers(news) {
this.subscribers.forEach(subscriber => {
subscriber.update(news);
});
}
}
// 观察者(新闻订阅者)
class NewsSubscriber {
constructor(name) {
this.name = name;
}
// 更新方法,用于接收新闻通知
update(news) {
console.log(`${this.name} 收到新闻: ${news}`);
}
}
// 创建新闻编辑器和订阅者
const editor = new NewsEditor();
const subscriber1 = new NewsSubscriber("Subscriber 1");
const subscriber2 = new NewsSubscriber("Subscriber 2");
// 订阅者订阅新闻编辑器
editor.subscribe(subscriber1);
editor.subscribe(subscriber2);
// 发布新闻
editor.publishNews("重要新闻: 今天的头条新闻");
// 输出结果:
// 发布新闻: 重要新闻: 今天的头条新闻
// Subscriber 1 收到新闻: 重要新闻: 今天的头条新闻
// Subscriber 2 收到新闻: 重要新闻: 今天的头条新闻
在上面的示例中,NewsEditor
充当主题,它可以添加订阅者、移除订阅者、发布新闻以及通知订阅者。NewsSubscriber
充当观察者,它通过 update
方法接收新闻通知。当新闻编辑器发布新闻时,所有订阅者都会得到通知并接收最新的新闻。
这个示例演示了观察者设计模式在实际应用中的用途,其中主题和观察者之间的关系是松耦合的,允许轻松地扩展和维护系统。
3. 堆与栈有什么区别?请用代码模拟实现一个?
堆和栈是计算机内存中的两种不同区域,它们有不同的分配方式和使用方式。下面是它们的主要区别:
栈 (Stack):
分配方式:栈是一种自动分配和释放内存的数据结构,它以一种线性的方式组织数据。
存储:栈用于存储函数调用的上下文(局部变量、函数参数、返回地址等),以便在函数返回时恢复状态。
速度:栈操作非常快,因为它是线性的,通常是固定大小。
生存期:栈上的数据的生存期通常与包含它的函数的生存期相同。
大小限制:栈的大小通常有限,因此适用于相对小的数据。
堆 (Heap):
分配方式:堆是一种手动分配和释放内存的数据结构,程序员负责分配和释放内存。
存储:堆用于存储动态分配的数据,如对象、数组等。
速度:堆操作相对较慢,因为它的内存分配和释放需要一定的时间。
生存期:堆上的数据的生存期不与特定函数相关,需要手动释放,否则会造成内存泄漏。
大小限制:堆的大小通常受系统资源的限制,可以动态分配较大的数据。
下面是一个简单的JavaScript示例,模拟栈和堆的区别:
// 模拟栈
function stackExample() {
let x = 10; // 栈上的局部变量
let y = 20;
const result = x + y;
return result;
}
// 模拟堆
function heapExample() {
const obj = { name: "John", age: 30 }; // 在堆上分配对象
obj.age = 31; // 修改堆上的对象
return obj;
}
const stackResult = stackExample();
const heapResult = heapExample();
console.log("栈示例结果:", stackResult);
console.log("堆示例结果:", heapResult);
在上面的示例中,stackExample
函数使用栈存储局部变量 x
和 y
,以及计算它们的结果。而 heapExample
函数使用堆分配了一个对象,并在堆上修改了对象的属性。结果,stackResult
存储了一个数字,而 heapResult
存储了一个对象。
需要注意的是,JavaScript中的内存管理通常由JavaScript引擎和垃圾回收器来处理,开发者不需要手动分配和释放内存。上述示例仅用于说明栈和堆的概念。
学习不打烊,充电加油只为遇到更好的自己,每天早上9点纯手工发布面试题,每天坚持花20分钟来学习与思考,在千变万化,类库层出不穷的今天,不要等到找工作时才狂刷题,提倡每日学习。