24道基础八股
24道基础八股
1. 解释JavaScript是什么?主要用途是什么?
JavaScript是一种高级的、解释型的语言,主要用于创建交互式的网页。他的主要用途包括在网页上添加动态交互效果,处理表单验证、创建复杂的单页应用(SPA)等。
- 高级解释型语言:这意味着JavaScript代码不需要编译,可以直接在浏览器中解释执行。这使得开发和调试过程更加快速和灵活。
- 动态交互效果:例如,当用户点击按钮时改变页面内容,或者实现拖拽功能等。这些效果可以大大提升用户体验。
- 表单验证:在用户提交表单之前,JavaScript可以检查输入是否符合要求,如邮箱格式是否正确,密码是否足够强等。这可以减少服务器端的压力,提高响应速度。
- 单页应用(SPA):这是一种现代web应用架构,整个应用只有一个HTML页面,通过JavaScript动态更新内容。这种方式可以提供类似于桌面应用的用户体验。
- 其他用途:JavaScript还可以用于服务器端编程(如Node.js),移动应用开发,游戏开发等。
2. JavaScript中有哪些基本数据类型
Number, String, Boolean, Null, Undefined, Symbol以及BigInt。
- Number:表示整数和浮点数。例如:let age = 25; let price = 19.99;
- String:表示文本数据。例如:let name = “John Doe”;
- Boolean:表示true或false。例如:let isLoggedIn = true;
- Null:表示一个故意的空值。例如:let emptyValue = null;
- Undefined:表示一个未定义的值。例如:let notAssigned;
- Symbol:表示一个唯一的标识符。例如:let id = Symbol(“id”);
- BigInt:用于表示大于2^53 - 1的整数。例如:let bigNumber = 1234567890123456789012345678901234567890n;
3. Null和Undefined在JavaScript中有什么区别
null表示有意不存在任何对象值,是一个空值或占位符,通常用于表示对象预期存在但当前为空的情况。
undefined表示不存在值或未初始化的变量,如果一个变量声明了但没有赋值,则它的值为undefined。
1 | let obj = null; // 明确表示对象为空 |
4. 在JavaScript中如何声明对象
在JavaScript中,可以使用var、let或const关键字来声明变量。其中,var声明的变量存在变量提升(Hoisting)现象,let和const声明的变量具有块级作用域(Block Scope)。
1 | var a = 1; // 函数作用域 |
5. 请解释JavaScript中的Hoisting是什么?
Hoisting是JavaScript的一种行为。其中变量和函数声明在编译阶段被移动到各自范围的顶部。这意味着函数或全局作用域的任何地方声明变量或函数,它们都会被视为在作用域的顶部声明。
- 变量提升:
- 使用var声明的变量会被提升到其作用域的顶部
- 只有声明被提升,赋值不会被提升
- let和const声明的变量不会被提升
- 函数提升:
- 函数声明会被完整地提升到其作用域的顶部
- 函数表达式不会被提升
1 | console.log(x); // 输出: undefined |
函数声明:是通过使用
function
关键字后面跟着函数名的方式进行声明,例如:function myFunction() {}
。这种方式的函数名可以在函数内部使用该函数名进行递归调用等。函数声明在执行代码之前,JavaScript引擎会将函数声明提升到当前作用域的顶部,因此可以在函数声明之前调用函数。函数表达式:则是将函数赋值给一个变量,这个变量可以是一个匿名函数或具名函数,例如:
const myFunction = function() {}
或const myFunction = function myFunc() {}
。函数表达式可以是匿名函数(没有函数名),或者有一个函数名,但该名称只在函数内部有效,无法在函数外部访问。函数表达式不会被提升,只有在代码执行到定义的地方后才能调用。
6. JavaScript中的this关键字的作用是什么?
this关键字在JavaScript中用于引用当前对象。它的值取决于函数的调用方式,可以是全局对象(在浏览器是Window)、调用函数的对象、新创建的对象(在构造函数中)等。
- 全局上下文:
- 在全局执行上下文中,this指向全局对象(浏览器中是window,Node.js中是global)
- 函数上下文:
- 在普通函数调用中,this指向全局对象
- 在方法调用中,this指向调用该方法的对象
- 在构造函数中,this指向新创建的对象实例
- 箭头函数:
- 箭头函数没有自己的this,它继承自外层作用域的this
- 显式设置this:
- 可以使用call()、apply()或bind()方法显式设置函数调用时的this值
1 | // 1. 全局上下文 |
7. ==和===运算符在JavaScript中有什么区别
== 运算符比较值,允许类型强转换,而===运算符严格比较值和类型。因此,当使用===时,如果两个值的类型和值都相等,则结果为true;而使用==时,如果两个值的值相等但类型不同,则会进行类型转换后再比较。
1 | // 使用 == |
8. 请解释JavaScript中的事件委托(Event Delegation)是什么?
事件委托是一种技术,其中父元素处理由子元素触发的事件。通过将事件处理程序添加到父元素上,并检查事件的目标元素,可以优化性能并减少事件侦听器的数量。这通常用于动态添加子元素的场景。
- 利用事件冒泡机制
- 将事件监听器添加到父元素而不是每个子元素
- 可以处理动态添加的元素
- 提高性能,尤其是在处理大量子元素时
- 减少内存使用和代码复杂度
1 | <ul id="todo-list"> |
1 | document.getElementById('todo-list').addEventListener('click', function(event) { |
这种方法的优势在于,即使我们动态地添加或删除列表项,事件处理也会正常工作,无需额外的代码。同时,它也提高了性能,因为我们只需要一个事件监听器来处理所有的删除操作。
9.在JavaScript中如何创建一个对象
在JavaScript中,可以使用对象字面量、构造函数或ECMAScript6中引入的类语法来创建对象。对象字面量是最简单的方法,如var obj = {key:'value'}
1 | // 1. 对象字面量 |
10. 请解释JavaScript中的闭包(Closure)是什么?
闭包是一个函数,即使在外部函数完成执行后,它仍保留从其外部范围访问变量的功能。这是因为在函数执行时,会创建一个执行上下文(Execution Context),其中包含该函数的变量对象(Variable Object)。当函数执行完毕后,这个上下文不会立即销毁,而是被保留起来,这就是闭包。
- 闭包可以访问外部函数的变量,即使外部函数已经返回
- 闭包可以用来创建私有变量和方法
- 闭包在JavaScript中广泛用于实现数据隐藏和模块化模式
- 过度使用闭包可能导致内存占用增加
1 | function outerFunction(x) { |
在这个例子中,innerFunction形成了一个闭包。它可以访问outerFunction的参数x和局部变量y,即使在outerFunction执行完毕后。当我们调用closure(20)时,它仍然能够访问x和y的值,并与传入的参数z一起进行计算。
[执行上下文和作用域链](【【JavaScript】执行上下文和作用域链!】 https://www.bilibili.com/video/BV1Ym411o7Cs/?share_source=copy_web&vd_source=c5226b5623222d58d700acb3cd12a496)
[视频地址](【【JavaScript】什么是原型】 https://www.bilibili.com/video/BV1pE421K7cx/?share_source=copy_web&vd_source=c5226b5623222d58d700acb3cd12a496)
要尽量避免闭包的使用
11. 请解释JavaScript中的bind()方法有什么用途?
bind()方法用于创建一个新函数,当这个新函数被调用时,bind()的第一个参数将作为它运行时的this,之后的一些列参数将会在传递的实参前传入作为他的参数。这提供了一种方式,使得在函数内部可以使用预定义的this值和其他参数值。
1 | function foo(a, b, c) { |
12. JavaScript中的split()和join()方法有什么区别?
spilt() 方法用于将一个字符串按照指定的分隔符分割成一个字符串数组。而join()方法则将一个数组(或一个类数组对象)的所有元素通过指定的分隔符连接成一个字符串。
- split()作用于字符串,返回数组
- join()作用于数组,返回字符串
- 两者都可以指定分隔符,如果不指定,split()默认以空格为分隔符,join()默认以逗号为分隔符
- split()可以限制返回的数组长度,而join()没有这个功能
1 | // split() 示例 |
13. JavaScript中的数组方法pop()、push()、unshift()、shift()分别有什么作用?
pop()方法用于删除数组的最后一个元素并返回该元素的值;
push()方法用于向数组的末尾添加一个或多个元素,并返回新的长度;
unshift()方法用于向数组的开头添加一个或多个元素,并返回新的长度;
shift()方法用于删除数组的第一个元素并返回该元素的值。
1 | let fruits = ['apple', 'banana']; |
14. 如何在JavaScript中处理错误?
在JavaScript中,可以使用try-catch块来捕获和处理异常。try块包含可能会抛出异常的代码,而catch块则包含当异常被抛出时执行的代码。此外,还可以使用throw语句来手动抛出。
- try-catch 用于捕获和处理可能发生的错误
- throw 用于手动抛出异常
- finally 块(可选)无论是否发生异常都会执行
- Error 对象可以用来创建自定义错误
1 | function divide(a, b) { |
15. 请解释JavaScript中的函数提升(Function Hoisting)和变量提升(Variable Hoisting)的区别。
- 函数提升: 在JavaScript中,函数声明会被提升到其所在作用域的顶部。这意味着你可以在函数声明之前调用该函数,而不会出错。
- 变量提升: 变量提升仅适用于
var
关键字申明的变量。这些变量会被提升到其所在作用域的顶部,但只会被赋予undefined
值。这意味着你可以在变量声明之前引用它,但会得到undefined
- 函数提升会将整个函数定义移到其所在作用域的顶部
- 变量提升只会将变量声明移到其所在作用域的顶部,而不会提升其初始化
- 函数提升优先于变量提升
- 只有使用function关键字声明的函数会被提升,函数表达式不会被提升
1 | // 变量提升示例 |
16. 在JavaScript中,如何判断一个变量是数组?
- 使用
Array.isArray()
方法: 这是判断一个变量是否为数组的最佳方法。例如:Array. isArray(myVariable)
- 使用
instanceof
运算符:例如:myVariable instanceof Array
。但这种方法在某些情况下可能会出错,尤其是当页面中存在多个Array
构造函数时
1 | // 创建测试用的变量 |
17. JavaScript中的回调函数(Callback)是什么?请给出一个简单的例子。
回调函数是一个函数,作为参数传递给另一个函数,并在那个函数内部被调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 定义一个接受名字和回调函数作为参数的函数
function greet(name, callback) {
// 打印问候语
console.log('Hello, ' + name);
// 执行作为参数传入的回调函数
callback();
}
// 定义一个将被用作回调函数的函数
function sayGoodbye() {
// 打印告别语
console.log('Goodbye!');
}
// 调用 greet 函数,传入 'World' 作为名字,sayGoodbye 作为回调函数
greet('World', sayGoodbye);
// 执行结果将会是:
// 输出:Hello, World
// 输出:Goodbye!
18. 请解释JavaScript中的严格比较(===)和抽象比较(==)的区别。
- 严格比较 === : 不仅比较值,还比较它们的类型。如果两个值的类型和值都相同,则返回true。
- 抽象比较 == :在比较前会进行类型转换, 如果两个值在类型转换后相等,则返回true。
19. 请解释JavaScript中的单线程模型和非阻塞I/O。
- JavaScript在浏览器环境中是单线程的,意味着它一次只能执行一个任务
- 为避免阻塞UI, JavaScript采用了非阻塞I/O模型,如事件循环和异步编程,允许在等待I/O操作(如网络请求)时执行其他任务。
[阻塞I/O和非阻塞I/O](【阻塞 IO vs 非阻塞 IO (为什么需要 IO 多路复用?)】 https://www.bilibili.com/video/BV1tN41127un/?share_source=copy_web&vd_source=c5226b5623222d58d700acb3cd12a496)
1 | const fs = require('fs'); |
在这个例子中,readFile
函数会异步地读取文件,而不会阻塞其他代码的执行。当文件读取完成后,会执行传入的回调函数。
20. 请解释JavaScript中的事件循环(Event Loop)是如何工作的。
- JavaScript的事件循环是一个处理异步事件和回调函数的机制
- 当JavaScript代码执行时,它会将同步代码添加到调用栈中并执行。如果遇到异步操作(如setTimeout、网络请求等),则将其添加到任务队列中。
- 当调用栈为空时,事件循环会查看任务队列中的第一个任务,并将其添加到调用栈中执行。这个过程会不断重复,形成事件循环。
【【前端八股文】事件循环-eventloop】 https://www.bilibili.com/video/BV1j14y1j7us/?share_source=copy_web&vd_source=c5226b5623222d58d700acb3cd12a496
1 | console.log('1'); // 同步操作,直接执行 |
21. 请解释JavaScript中的闭包(Closure)并给出一个例子。
闭包是一个有权访问其外部词法环境(lexical environment)函数。即使外部函数已经执行完毕,闭包仍然可以访问其外部函数的变量。
1 | function outerFunction(outerVariable){ |
22. 请解释JavaScript中的原型链(Prototype Chain)是什么。
【【前端八股文】原型和原型链】 https://www.bilibili.com/video/BV1LY411d7Yt/?share_source=copy_web&vd_source=c5226b5623222d58d700acb3cd12a496
23. 请解释JavaScript中的 this 关键字在箭头函数和普通函数中的不同。
- 在普通函数中,this的值取决于函数的调用方式。它可以指向全局对象(在浏览器中的window)、调用函数的对象、新创建的对象(在构造函数中)等
- 在箭头函数中,this不绑定自己的this, 而是捕获其所在上下文的this值作为自己的this值。这使得箭头函数在回调函数和事件处理器中特别有用,因为它们不会意外地改变this的值。
- 在普通函数中,this 的值取决于函数的调用方式
- 在箭头函数中,this 被词法绑定,继承自外围作用域
- 箭头函数不会创建自己的 this,它会捕获其所在上下文的 this 值
- 由于这个特性,箭头函数通常不适用于方法函数,但在回调中很有用