以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?
第10天要刷的面试题如下:
- 对比强/弱类型程序设计语言
- 对比解释/编译类型程序设计语言
- js代码是如何被执行的
- 封装一个XHR,可以被取消
- 谈谈js中的内置对象
下面是我自己的理解:
1. 对比强/弱类型程序设计语言
强类型语言和弱类型语言描述了编程语言在处理数据类型时的严格程度。
-
强类型语言(Strongly typed language):
- 在强类型语言中,变量的类型是静态检查 的,即在编译时或运行时会进行类型检查。
- 强类型语言要求变量在使用之前必须经过明确的类型声明 ,并且只能进行兼容的类型转换。
- 强类型语言严格限制了不同数据类型之间的混合操作,需要显式地进行类型转换。
- 例如,
Java、C#、Python(部分方面)、Go
等是强类型语言。
-
弱类型语言(Weakly typed language):
- 在弱类型语言中,变量的类型可以在运行时自动推断 ,无需明确的类型声明。
- 弱类型语言允许隐式的类型转换,也就是在不显示指定的情况下将一个数据类型转换为另一个数据类型。
- 弱类型语言相对灵活,在不同数据类型之间进行混合操作时,会自动进行类型转换。
- 例如,
JavaScript、PHP、Perl、Bash
等是弱类型语言。
-
强类型语言的优点包括:
- 更早地发现类型错误 ,提高代码的稳定性和可靠性。
- 提供更好的代码可读性和维护性,因为类型信息可以在代码中明确表达。
- 限制了隐式的类型转换,减少潜在的错误和意外行为。
-
弱类型语言的优点包括:
- 编写代码更加灵活和简便,省去了显式的类型声明。
- 更容易进行快速原型开发和迭代,因为数据类型可以自由变化。
-
强类型语言与静态类型语言、弱类型语言与动态类型语言并不完全等同。有些语言可能同时具备强类型和动态类型的特征,或者弱类型和静态类型的特征。
-
TypeScript(TS)是一种强类型语言。
2. 对比解释/编译类型程序设计语言
解释型语言和编译型语言是两种不同的程序设计语言类型,它们在代码执行方式和性能方面存在差异。
-
解释型语言(Interpreted Language):
- 解释型语言的代码通常是逐行被解释器解读和执行的。
- 在解释型语言中,源代码会通过解释器逐行解析并立即执行 ,无需先进行【显式的】编译过程。
- 解释型语言的代码可以直接在运行环境中执行,无需生成独立的可执行文件。
- 例如,
Python、JavaScript、Ruby 和 PHP
等都是解释型语言。
-
编译型语言(Compiled Language):
- 编译型语言的代码需要经过编译器 的处理,在执行之前转换为机器语言或字节码形式。
- 在编译型语言中,源代码通过编译器进行全局分析、优化和转换,生成与目标平台相关的可执行文件。
- 编译型语言的代码在运行之前需要预先编译,并且编译结果通常是与特定硬件和操作系统兼容的二进制文件。
- 例如,
C、C++、Java 和 Go
等都是编译型语言。
-
解释型语言在每次执行时都需要解释器对源代码进行解释和执行,这可能导致相对较慢的执行速度。
-
编译型语言在编译阶段可以进行全局优化和静态分析,生成高效的机器码或字节码,因此通常具有较快的执行速度。
总结:使用解释器还是编译器、要不要生成机器或者字节码、有没有产生可执行文件、效率的高低。
3. js代码是如何被执行的
执行过程包括以下步骤:
- 解析 :JavaScript 代码从上到下逐行被解析器 解析。解析器会将代码分解为抽象语法树(Abstract Syntax Tree,AST)的形式,以理解代码的结构和含义。
- 词法分析 :解析器将代码分解为多个标记(tokens),如变量名、运算符、关键字等。这是通过**词法分析器(Lexer)**完成的,它根据语法规则将源代码转换为一系列标记。
- 语法分析 :解析器使用语法分析器(Parser)将标记流转换为语法树(不是AST)。语法分析器根据语言的规范和语法规则,确定代码中的表达式、语句和结构。
- 执行 :在生成了语法树后,解释器或者即时编译器对语法树进行执行。执行过程中,JavaScript 引擎会遵循特定的执行模型,逐行执行代码并处理各种操作,例如变量赋值、函数调用、条件语句、循环等。
- 作用域和上下文 :JavaScript 中存在作用域和执行上下文的概念。作用域定义了变量和函数的可访问性和生命周期 ,而执行上下文表示了当前执行代码所处的环境。JavaScript 引擎在执行代码时会根据作用域和执行上下文来解析标识符、查找变量、创建闭包等。
- 内存管理:JavaScript 引擎负责内存管理,包括分配和释放内存。它使用垃圾回收机制自动检测不再使用的对象,并回收这些对象所占用的内存空间,以避免内存泄漏和过度消耗内存。
- 优化 :即时编译 (JIT Compilation)、字节码解释执行等,以提高执行效率。这些优化技术可以将代码转换为更高效的形式,例如机器码,以获得更快的执行速度。
一句话总结:javaScript 代码在被执行前会经历解析、词法分析、语法分析阶段,并由解释器或即时编译器逐行执行。整个过程涉及到作用域、执行上下文和内存管理等概念。
4. 封装一个XHR,可以被取消
- 取消请求的做法是:调用XHR对象上的abort方法。
- 为了保证abort方法之被调用一次,使用了锁
4.1 回调函数版本
js
class XHRWrapper {
constructor() {
this.xhr = new XMLHttpRequest();
// 锁,请求只能被取消一次
this.isCanceled = false;
}
// 构造XHR并发起请求
get(url, callback) {
this.xhr.open('GET', url, true); // 第三个参数为true表示的是异步请求
this.xhr.onreadystatechange = () => {
if (this.xhr.readyState === 4 && !this.isCanceled) {
callback(this.xhr.responseText);
}
};
this.xhr.send();
}
cancel() {
if (!this.isCanceled) {
this.xhr.abort();
this.isCanceled = true;
}
}
}
4.2 Promise版本
js
class XHRWrapper {
constructor() {
this.xhr = new XMLHttpRequest();
this.isCanceled = false;
}
get(url) {
// 回调和Promise做法的区别在于get方法的执行结果上
// 如果是回调函数方法,则get方法是不需要返回任何值的;而如果使用Promise方法,则get需要返回一个Promise对象
return new Promise((resolve, reject) => {
this.xhr.open('GET', url, true);
this.xhr.onreadystatechange = () => {
if (this.xhr.readyState === 4) {
if (this.isCanceled) {
reject(new Error('Request canceled'));
} else if (this.xhr.status >= 200 && this.xhr.status < 300) {
// resolve的内容为resolve(this.xhr.responseText)
resolve(this.xhr.responseText);
} else {
reject(new Error('Request failed with status ' + this.xhr.status));
}
}
};
this.xhr.send();
});
}
cancel() {
if (!this.isCanceled) {
this.xhr.abort();
this.isCanceled = true;
}
}
}
4.3 带缓存版本
在调用get函数的时候,首先对url进行判断,如果此url已经请求过了,则从缓存哈希表中直接返回结果;如果没有请求过再重新发起请求获取数据!
js
class XHRWrapper {
constructor() {
this.xhr = new XMLHttpRequest();
this.isCanceled = false;
this.cache = new Map();
}
get(url) {
let _rst = this.cache.get(url)
if(!_rst) {
_rst = new Promise((resolve, reject) => {
this.xhr.open('GET', url, true);
this.xhr.onreadystatechange = () => {
if (this.xhr.readyState === 4) {
if (this.isCanceled) {
reject(new Error('Request canceled'));
} else if (this.xhr.status >= 200 && this.xhr.status < 300) {
// resolve的内容为resolve(this.xhr.responseText)
resolve(this.xhr.responseText);
} else {
reject(new Error('Request failed with status ' + this.xhr.status));
}
}
};
this.xhr.send();
})
this.cache.set(url, _rst);
}
return _rst;
}
cancel() {
if (!this.isCanceled) {
this.xhr.abort();
this.isCanceled = true;
}
}
}
5. 谈谈js中的内置对象
请注意,JavaScript 中有一些全局对象和内置对象之间存在重叠,因为某些内置对象也可以作为全局对象使用。以下是根据常见的使用场景对 JavaScript 中的内置对象和全局对象进行分类:
-
数据类型相关对象:
- Object:JavaScript 的基本对象,用于创建和操作对象。
- Array:用于存储和操作有序的数据集合。
- String:用于处理字符串的操作和转换。
- Number:用于数字操作和转换。
- Boolean:表示布尔值的对象,用于逻辑操作和判断。
- Symbol:符号对象。
- Bigint:大数对象。
-
日期和时间相关对象:
- Date:用于处理日期和时间的对象,提供了日期计算、格式化、解析等方法。
-
数学相关对象:
- Math:提供了一组数学计算相关的静态方法和常量。
-
JSON 相关对象:
- JSON:用于解析和序列化 JSON 格式的对象。
-
正则表达式相关对象:
- RegExp:用于处理正则表达式匹配和替换。
-
错误处理相关对象:
- Error:用于表示错误的对象,包含错误的名称和消息等信息。
-
函数和执行上下文相关对象:
- Function:用于定义和调用函数对象。
-
浏览器环境相关对象(仅在浏览器中可用):
- Document:用于操作 HTML 文档的对象,提供了访问和修改文档结构的方法。
- Window:表示浏览器窗口,作为全局对象提供了全局变量和方法,以及与浏览器交互的方法和属性。
- XMLHttpRequest:用于发送 HTTP 请求和接收响应的对象。
-
其它对象:如
File、Map、Set、WeakMap、WeakSet、Promise、Generator、Reflect、Proxy、Intl、WebAssembly
等 -
内置函数:如
parseInt parseFloat eval
等