本文详细解析了JS基础知识和常见面试题,涵盖变量、数据类型、函数、作用域以及ES6新特性等内容,帮助读者全面了解和准备JS面试真题。
在JavaScript中,变量用于存储数据,变量的类型决定了它可以存储的数据类型。JavaScript是一种动态类型语言,变量可以在声明时直接赋值,也可以在声明后赋值。
基本数据类型
JavaScript的基本数据类型包括Number
、String
、Boolean
、null
、undefined
和Symbol
(ES6引入)。这些类型的数据直接存储在栈内存中。
let number = 123; // Number类型 let string = "Hello, world!"; // String类型 let boolean = true; // Boolean类型 let nullValue = null; // null类型 let undefinedValue; // undefined类型 let symbol = Symbol(); // Symbol类型
undefined
类型表示未定义的值,通常用于未初始化或未赋值的变量。Symbol
类型是ES6引入的一种新的基本数据类型,用于生成唯一的标识符。
复杂数据类型
除了基本数据类型,JavaScript还有复杂数据类型,如Object
、Array
和Function
。复杂数据类型的数据存储在堆内存中。
let object = { name: "John", age: 30 }; // Object类型 let array = [1, 2, 3]; // Array类型 let functionType = function() { console.log("This is a function"); }; // Function类型
数据类型的转换
JavaScript提供了多种方法来转换数据类型,例如Number()
、String()
、Boolean()
等。
let num = "123"; let number = Number(num); // number为123 let str = 123; let string = String(str); // string为"123" let bool = "false"; let boolean = Boolean(bool); // boolean为true
函数是执行特定任务的代码块。在JavaScript中,函数可以定义函数参数、返回值,并可以嵌套使用。
function add(x, y) { return x + y; } let result = add(5, 3); // result为8
作用域
变量的作用域决定了变量在程序中的可访问性。JavaScript中的作用域主要有全局作用域和块作用域两种。
// 全局作用域 let globalVar = "I'm global"; console.log(globalVar); // 输出"I'm global" function checkScope() { // 块作用域 let blockVar = "I'm block"; console.log(blockVar); // 输出"I'm block" } checkScope(); console.log(blockVar); // 报错,因为blockVar在全局作用域中不可访问
这里checkScope
函数内部的blockVar
仅在其作用域内有效,当尝试在函数外部访问时会报错。
ES6(ECMAScript 2015)引入了许多新特性,使JavaScript更加强大和灵活。
箭头函数
箭头函数简化了函数的定义,特别是对于单行操作。箭头函数与普通函数的一个重要区别是,箭头函数没有自己的this
,它使用外层函数或全局作用域的this
。
let add = (x, y) => x + y; // 箭头函数 console.log(add(5, 3)); // 输出8
模板字符串
模板字符串允许在字符串中嵌入变量和表达式。模板字符串中可以使用${}
来插入变量或表达式。
let name = "John"; let age = 30; let greeting = `Hello, ${name}. You are ${age} years old.`; console.log(greeting); // 输出 "Hello, John. You are 30 years old."
类
ES6引入了类的概念,使面向对象编程更加直观。类的定义使用class
关键字,成员方法通过constructor
初始化。
class Person { constructor(name, age) { this.name = name; this.age = age; } introduce() { return `My name is ${this.name}, and I am ${this.age} years old.`; } } let person = new Person("John", 30); console.log(person.introduce()); // 输出"My name is John, and I am 30 years old."
数组是JavaScript中常用的数据结构之一,提供了许多内置方法来操作数组。
数组的增删查改
let arr = [1, 2, 3, 4]; // 添加元素 arr.push(5); // arr为[1, 2, 3, 4, 5] arr.unshift(0); // arr为[0, 1, 2, 3, 4, 5] // 删除元素 arr.pop(); // arr为[0, 1, 2, 3, 4] arr.shift(); // arr为[1, 2, 3, 4] // 查找元素 let index = arr.indexOf(3); // index为2 let value = arr[2]; // value为3 // 修改元素 arr[2] = "three"; // arr为[1, 2, "three", 4]
数组的排序与遍历
let arr = [4, 2, 3, 1]; // 排序 arr.sort((a, b) => a - b); // arr为[1, 2, 3, 4] // 遍历 arr.forEach((item, index) => { console.log(`Index: ${index}, Value: ${item}`); });
字符串是JavaScript中最基本的数据类型之一,提供了许多内置方法来处理字符串。
字符串的拼接与分割
let str = "Hello, world."; // 拼接 str += " How are you?"; console.log(str); // 输出 "Hello, world. How are you?" // 分割 let parts = str.split(" "); console.log(parts); // 输出 ["Hello,", "world.", "How", "are", "you?"]
字符串的查找与替换
let str = "Hello, world."; // 查找 let index = str.indexOf("world"); // index为7 let substring = str.substring(7, 13); // substring为"world." let startsWith = str.startsWith("Hello"); // startsWith为true let endsWith = str.endsWith("."); // endsWith为true // 替换 let replaced = str.replace("world", "everyone"); // replaced为"Hello, everyone."
数值运算是JavaScript中最基本的操作之一,提供了各种运算符来执行数值运算。
基本运算
let x = 5; let y = 3; let sum = x + y; // sum为8 let difference = x - y; // difference为2 let product = x * y; // product为15 let quotient = x / y; // quotient为1.6666666666666667 let remainder = x % y; // remainder为2
进制转换
let binary = 0b101010; // 二进制 let octal = 0o755; // 八进制 let decimal = 123; // 十进制 let hexadecimal = 0xFF; // 十六进制 console.log(binary); // 输出 42 console.log(octal); // 输出 493 console.log(decimal); // 输出 123 console.log(hexadecimal); // 输出 255
语法错误是最常见的错误类型之一。当代码不符合JavaScript语法规则时,会抛出语法错误。
let message; let message = "Hello"; // 报错,变量已声明
正确的写法:
let message = "Hello";
Chrome DevTools是调试JavaScript代码最常用的工具之一,提供了丰富的功能来帮助开发者调试代码。
JavaScript提供了多种错误处理机制,如try
、catch
、finally
等。
function divide(x, y) { try { if (y === 0) { throw new Error("Divide by zero error."); } return x / y; } catch (error) { console.error(error.message); return null; } finally { console.log("Divide operation completed."); } } console.log(divide(10, 0)); // 输出 "Divide by zero error." 和 "Divide operation completed."
单例模式确保一个类只有一个实例,并提供一个全局访问点。单例模式常用于需要全局共享状态的场景。
class Singleton { constructor() { if (Singleton.instance) { return Singleton.instance; } this.data = {}; Singleton.instance = this; } } let instance = new Singleton(); Singleton.instance = new Singleton(); // 返回之前创建的实例 console.log(instance === Singleton.instance); // 输出 true
工厂模式用于创建对象实例,但通过工厂方法隐藏创建细节。工厂模式常用于需要创建多种类型对象的场景。
function createAnimal(type) { switch (type) { case "dog": return new Dog(); case "cat": return new Cat(); default: return null; } } class Dog { bark() { console.log("Woof!"); } } class Cat { meow() { console.log("Meow!"); } } let dog = createAnimal("dog"); let cat = createAnimal("cat"); dog.bark(); // 输出 "Woof!" cat.meow(); // 输出 "Meow!"
模块模式用于封装对象,使对象的内部状态私有化,提供公共接口访问。模块模式常用于需要封装对象的场景。
let module = (function() { let privateVar = 0; function privateMethod() { console.log("This is a private method."); } return { publicMethod: function() { privateVar++; console.log(`Public method called ${privateVar} times.`); } }; })(); module.publicMethod(); // 输出 "Public method called 1 times." module.privateMethod(); // 报错,因为privateMethod是私有的
在JavaScript中,每个函数都有一个prototype
属性,指向一个对象。这个对象称为原型对象,它包含该函数实例的共享属性和方法。每个对象都有一个内部属性[[Prototype]]
,指向其原型对象。当访问对象的属性或方法时,如果对象本身没有定义,则会从其原型对象中查找。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}.`); }; let person = new Person("John", 30); person.sayHello(); // 输出 "Hello, my name is John."
原型链的查找过程如下:
null
)。this
关键字在JavaScript中用于访问当前对象的属性和方法。this
的值取决于函数的调用方式。
function sayHello() { console.log(`Hello, my name is ${this.name}.`); } let person = { name: "John", sayHello: sayHello }; person.sayHello(); // 输出 "Hello, my name is John."
sayHello
函数在person
对象上调用,因此this
指向person
对象。
异步编程主要用于处理长时间运行的任务,例如网络请求和文件读写。JavaScript提供了多种异步编程模型,如回调、Promises、async/await等。
回调
回调是一种简单的异步编程方式,将回调函数作为参数传递给异步操作。
function asyncOperation(callback) { setTimeout(function() { callback("Data from async operation."); }, 1000); } asyncOperation(function(data) { console.log(data); // 输出 "Data from async operation." });
Promises
Promises是JavaScript中的一种异步编程模型,返回一个表示异步操作最终完成或失败的对象。
function asyncOperation() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve("Data from async operation."); }, 1000); }); } asyncOperation().then(function(data) { console.log(data); // 输出 "Data from async operation." });
async/await
async/await是基于Promises的语法糖,使异步代码更接近同步代码。
async function asyncOperation() { return "Data from async operation."; } async function main() { let data = await asyncOperation(); console.log(data); // 输出 "Data from async operation." } main();
面试时,面试官通常会考察候选人的基本知识、解决问题的能力和代码质量。以下是一个模拟面试场景:
面试官:请解释一下JavaScript中的原型链。
候选人:在JavaScript中,每个函数都有一个prototype
属性,指向一个对象。这个对象称为原型对象,它包含该函数实例的共享属性和方法。每个对象都有一个内部属性[[Prototype]]
,指向其原型对象。当访问对象的属性或方法时,如果对象本身没有定义,则会从其原型对象中查找。原型链的查找过程如下:首先查找对象自身的属性;如果对象自身没有定义,则查找对象的原型对象的属性;依次类推,直到原型链的末端(null
)。
面试官:好的。请编写一个函数,实现数组的去重功能。
候选人:好的,这是一个去重函数的实现:
function uniqueArray(arr) { let result = []; let seen = new Set(); for (let item of arr) { if (!seen.has(item)) { result.push(item); seen.add(item); } } return result; } let arr = [1, 2, 2, 3, 4, 4, 5]; console.log(uniqueArray(arr)); // 输出 [1, 2, 3, 4, 5]
面试时,面试官可能会提出各种技术问题,以下是一些常见的面试题及解答:
问题:解释一下JavaScript中的闭包。
回答:闭包是JavaScript中的一个概念,指的是一个函数及其作用域对象的结合。闭包可以访问函数内部的变量,即使该函数已经执行完毕。闭包常用于创建私有变量和函数。
function createCounter() { let count = 0; return function() { count++; return count; }; } let counter = createCounter(); console.log(counter()); // 输出 1 console.log(counter()); // 输出 2
问题:解释一下JavaScript中的作用域和作用域链。
回答:作用域指的是变量的可访问范围。在JavaScript中,变量的作用域主要有全局作用域和块作用域两种。全局作用域中的变量在整个程序中都可以访问,块作用域中的变量仅在块内可访问。作用域链是一种链式结构,用于查找变量的值。当访问一个变量时,JavaScript会从当前作用域开始查找,直到找到该变量或到达全局作用域。
function checkScope() { let blockVar = "I'm block"; // 块作用域 console.log(blockVar); // 输出 "I'm block" } checkScope(); console.log(blockVar); // 报错,因为blockVar在全局作用域中不可访问
面试时,除了准备技术问题外,还需要注意以下几点: