本文详细介绍了JavaScript的基础知识和高级概念,涵盖了变量与数据类型、运算符与表达式、函数与作用域、DOM操作与事件处理等内容。此外,文章还深入讲解了异步编程、Promise、async/await等重要主题。本文还包括了JavaScript设计模式的入门知识,并通过实战演练和技巧帮助读者准备JavaScript面试题。JavaScript面试题贯穿全文,提供了全面的知识覆盖和实用的面试技巧。
JavaScript基础知识回顾
变量与数据类型
在JavaScript中,变量用于存储数据值。JavaScript支持多种数据类型,包括但不限于数字、字符串、布尔值、未定义和其他复杂类型如对象、函数等。
变量声明:
JavaScript中声明变量可以使用 var
, let
, 和 const
。这些关键字的选择取决于变量的作用域和是否需要改变变量的值。
var
:可以在任何位置重新声明变量,并且会提升到它们声明的位置。并且可以重新赋值。let
:声明的变量可以重新赋值,但在声明之前不能引用。const
:声明的变量不能重新赋值,但可以引用和修改不可变对象的内容。
示例代码:
var x = 5; // 使用 var 声明变量
console.log(x); // 输出 5
let y = 10;
console.log(y); // 输出 10
y = 20;
console.log(y); // 输出 20
const z = 100;
console.log(z); // 输出 100
// z = 200; // 会导致错误,因为 const 声明的变量不能重新赋值
数据类型:
Number
:表示数字,包括整数和浮点数。String
:表示文本,包括引号包围的字符序列。Boolean
:表示布尔值,只包含true
和false
。undefined
:表示未定义的变量。null
:表示空值,表示对象的不存在。Object
:表示复杂的数据结构。Symbol
:表示唯一的、不可变的数据类型,常用于对象属性的标识。Array
:表示数组,是对象的一种特殊类型,用于存储多个值。
示例代码:
let number = 3.14;
let string = "Hello, world!";
let boolean = true;
let undefinedType = undefined;
let nullType = null;
console.log(typeof number); // 输出 "number"
console.log(typeof string); // 输出 "string"
console.log(typeof boolean); // 输出 "boolean"
console.log(typeof undefinedType); // 输出 "undefined"
console.log(typeof nullType); // 输出 "object" (在 ES6 之前,null 类型是 "object")
let symbol = Symbol("symbol");
console.log(symbol); // 输出 Symbol(symbol)
运算符与表达式
运算符是JavaScript中用来执行基本计算操作的符号。运算符可以分为算术运算符、比较运算符、逻辑运算符、位运算符等。
算术运算符:
+
:加法-
:减法*
:乘法/
:除法%
:取模(余数)
示例代码:
let x = 10;
let y = 5;
console.log(x + y); // 输出 15
console.log(x - y); // 输出 5
console.log(x * y); // 输出 50
console.log(x / y); // 输出 2
console.log(x % y); // 输出 0
位运算符:
&
:按位与|
:按位或^
:按位异或~
:按位取反<<
:左移位>>
:右移位
示例代码:
let a = 5; // 二进制:101
let b = 3; // 二进制:011
console.log(a & b); // 输出 1 (二进制:001)
console.log(a | b); // 输出 7 (二进制:111)
console.log(a ^ b); // 输出 6 (二进制:110)
console.log(~a); // 输出 -6 (二进制:补码:11111111111111111111111111110101)
console.log(a << 1); // 输出 10 (二进制:1010)
console.log(a >> 1); // 输出 2 (二进制:0010)
比较运算符:
==
:等于===
:严格等于(检查类型和值)!=
:不等于!==
:严格不等于>
:大于<
:小于>=
:大于等于<=
:小于等于
示例代码:
let a = 5;
let b = 10;
console.log(a == b); // 输出 false
console.log(a === b); // 输出 false
console.log(a != b); // 输出 true
console.log(a !== b); // 输出 true
console.log(a > b); // 输出 false
console.log(a < b); // 输出 true
console.log(a >= b); // 输出 false
console.log(a <= b); // 输出 true
逻辑运算符:
&&
:逻辑与(短路)||
:逻辑或(短路)!
:逻辑非
示例代码:
let x = true;
let y = false;
console.log(x && y); // 输出 false
console.log(x || y); // 输出 true
console.log(!x); // 输出 false
流程控制语句
流程控制语句用于控制程序的执行顺序。
条件语句:
if
:条件为真时执行代码块。else if
:用于多个条件的检查。else
:条件为假时执行代码块。
示例代码:
let score = 85;
if (score >= 90) {
console.log("优秀"); // 分数大于等于90输出优秀
} else if (score >= 70) {
console.log("良好"); // 分数大于等于70输出良好
} else {
console.log("及格"); // 否则输出及格
}
循环语句:
for
:用于循环执行代码块,直到满足特定条件。while
:执行循环语句,直到条件为假。do...while
:与while循环类似,但是先执行一次循环再判断条件。
示例代码:
for (let i = 0; i < 5; i++) {
console.log(i); // 输出 0 到 4
}
let j = 0;
while (j < 3) {
console.log(j); // 输出 0 到 2
j++;
}
do {
console.log(j); // 输出 0
j++;
} while (j < 1);
switch 语句:
switch
:用于执行多个分支之一。
示例代码:
let number = 2;
switch (number) {
case 1:
console.log("数字是1");
break;
case 2:
console.log("数字是2");
break;
default:
console.log("数字不是1或2");
}
函数与作用域
函数定义与调用
在JavaScript中,函数用于执行特定的操作。函数可以有参数和返回值。
函数定义:
- 使用
function
关键字定义函数。 - 使用箭头函数
=>
定义函数。
示例代码:
function add(a, b) {
return a + b;
}
let result = add(3, 4);
console.log(result); // 输出 7
let multiply = (x, y) => x * y;
console.log(multiply(3, 4)); // 输出 12
作用域与闭包
作用域:
作用域决定了变量的可见性。JavaScript有两种类型的作用域:全局作用域和函数作用域。
- 全局作用域:在任何地方都可以访问的变量。
- 函数作用域:只在定义函数的地方可以访问的变量。
示例代码:
let globalVar = "全局变量";
function outerFunction() {
let localVar = "局部变量";
function innerFunction() {
console.log(globalVar); // 输出 "全局变量"
console.log(localVar); // 输出 "局部变量"
}
innerFunction();
}
outerFunction();
console.log(localVar); // 报错,localVar 未定义
闭包:
闭包是函数和对其周围状态的引用组成的结构。这种结构使得函数可以记住并访问函数内部声明的变量,即使函数已经执行完毕。
示例代码:
function createCounter() {
let count = 0;
function increment() {
count++;
console.log(count);
}
return increment;
}
let counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
arguments
对象的理解与应用
arguments
对象在函数内部作为特殊数组存在,可以访问传递给函数的所有参数。
示例代码:
function sum() {
let result = 0;
for (let i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
console.log(sum(1, 2, 3)); // 输出 6
this 关键字的理解与应用
this
关键字在JavaScript中用于表示函数执行时的对象。根据函数的定义和调用方式,this
的值可以不同。
- 普通函数:
this
指向全局对象(在浏览器中是window
)。 - 对象方法:
this
指向调用该方法的对象。 - 箭头函数:
this
继承自外部执行上下文。
示例代码:
let obj = {
name: "对象",
getName: function() {
console.log(this.name); // 输出 "对象"
}
};
obj.getName();
let func = function() {
console.log(this); // 在全局作用域下输出 Window
};
func();
let arrowFunc = () => {
console.log(this); // 输出 Window,因为在全局作用域下
};
arrowFunc();
let anotherObj = {
name: "另一个对象",
func: function() {
console.log(this.name); // 输出 "另一个对象"
}
};
anotherObj.func();
DOM操作与事件处理
基本的DOM操作方法
DOM(文档对象模型)是用于处理HTML文档的API。JavaScript通过DOM API可以操作HTML元素。
获取元素:
getElementById
:通过ID获取元素。getElementsByClassName
:通过类名获取元素。getElementsByTagName
:通过标签名获取元素。querySelector
:通过CSS选择器选择元素。querySelectorAll
:通过CSS选择器选择多个元素。
示例代码:
<!DOCTYPE html>
<html>
<head>
<title>DOM操作示例</title>
</head>
<body>
<div id="myDiv" class="myClass">Hello, World!</div>
<script>
let elementById = document.getElementById("myDiv");
console.log(elementById); // 输出 div 元素
let elementsByClass = document.getElementsByClassName("myClass");
console.log(elementsByClass[0]); // 输出 div 元素
let elementsByTag = document.getElementsByTagName("div");
console.log(elementsByTag[0]); // 输出 div 元素
let elementByQuery = document.querySelector("#myDiv");
console.log(elementByQuery); // 输出 div 元素
let elementsByQuery = document.querySelectorAll(".myClass");
console.log(elementsByQuery[0]); // 输出 div 元素
</script>
</body>
</html>
获取和修改元素属性
获取属性:
let elementById = document.getElementById("myDiv");
console.log(elementById.getAttribute("id")); // 输出 "myDiv"
console.log(elementById.getAttribute("class")); // 输出 "myClass"
修改属性:
let elementById = document.getElementById("myDiv");
elementById.setAttribute("class", "newClass");
console.log(elementById.getAttribute("class")); // 输出 "newClass"
事件绑定与处理
事件处理是DOM操作的重要组成部分。JavaScript可以通过事件处理器来响应用户输入或其他事件。
绑定事件处理器:
addEventListener
:添加事件处理器。removeEventListener
:移除事件处理器。
示例代码:
<!DOCTYPE html>
<html>
<head>
<title>事件绑定示例</title>
</head>
<body>
<button id="myButton">点击我</button>
<script>
let button = document.getElementById("myButton");
function handleClick() {
console.log("按钮被点击了");
}
button.addEventListener("click", handleClick);
// 移除事件处理器
button.removeEventListener("click", handleClick);
</script>
</body>
</html>
事件冒泡与事件捕获:
<!DOCTYPE html>
<html>
<head>
<title>事件冒泡和捕获示例</title>
</head>
<body>
<div id="outerDiv">
<div id="innerDiv">
<button id="myButton">点击我</button>
</div>
</div>
<script>
let outerDiv = document.getElementById("outerDiv");
let innerDiv = document.getElementById("innerDiv");
let button = document.getElementById("myButton");
// 事件冒泡示例
button.addEventListener("click", function() {
console.log("按钮被点击了");
}, false);
// 事件捕获示例
outerDiv.addEventListener("click", function() {
console.log("外层div被点击了");
}, true);
</script>
</body>
</html>
防止事件重复绑定
为了避免事件重复绑定,可以在添加事件处理器时使用 once
选项,或者使用闭包来动态生成事件处理器。
示例代码:
<!DOCTYPE html>
<html>
<head>
<title>防止事件重复绑定示例</title>
</head>
<body>
<button id="myButton">点击我</button>
<script>
let button = document.getElementById("myButton");
function handleClick() {
console.log("按钮被点击了");
}
// 使用一次性的事件处理器
button.addEventListener("click", handleClick, { once: true });
// 使用闭包防止重复绑定
button.addEventListener("click", (function() {
button.removeEventListener("click", arguments.callee);
return function() {
console.log("按钮被点击了");
};
})());
</script>
</body>
</html>
常见面试题解析
异步编程与回调函数
异步编程是现代JavaScript开发中不可或缺的一部分。异步操作可以避免阻塞主线程,提高程序的响应速度和用户体验。
异步操作:
使用 setTimeout
或 setInterval
实现异步操作。回调函数是处理异步操作结果的函数。
示例代码:
function asyncOperation(callback) {
setTimeout(() => {
callback("异步操作结果");
}, 1000);
}
asyncOperation((result) => {
console.log(result); // 输出 "异步操作结果"
});
Promise与async/await
Promise是一种解决异步问题的更好方法。它提供了一种更清晰的异步操作流程,支持 then
和 catch
方法,可以链式调用。
Promise的基本用法:
new Promise
:创建一个Promise实例。then
:处理成功的回调。catch
:处理失败的回调。
示例代码:
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作结果");
}, 1000);
});
}
asyncOperation().then((result) => {
console.log(result); // 输出 "异步操作结果"
}).catch((error) => {
console.error(error);
});
// 使用 async/await
async function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作结果");
}, 1000);
});
}
async function run() {
try {
let result = await asyncOperation();
console.log(result); // 输出 "异步操作结果"
} catch (error) {
console.error(error);
}
}
run();
Promise.all 和 Promise.race:
Promise.all
:等待所有传入的Promise实例完成。Promise.race
:等待第一个传入的Promise实例完成。
示例代码:
let promise1 = new Promise((resolve) => setTimeout(resolve, 1000, '第一个'));
let promise2 = new Promise((resolve) => setTimeout(resolve, 2000, '第二个'));
let promise3 = new Promise((resolve) => setTimeout(resolve, 3000, '第三个'));
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); // 输出 ["第一个", "第二个", "第三个"]
});
Promise.race([promise1, promise2, promise3]).then((value) => {
console.log(value); // 输出 "第一个"
});
JSON的使用与序列化
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。JavaScript提供了内置的方法来处理JSON。
JSON的基本用法:
JSON.stringify
:将JavaScript对象序列化为JSON字符串。JSON.parse
:将JSON字符串解析为JavaScript对象。
示例代码:
let obj = {
name: "张三",
age: 20,
address: {
city: "北京",
country: "中国"
}
};
let jsonStr = JSON.stringify(obj);
console.log(jsonStr); // 输出 {"name":"张三","age":20,"address":{"city":"北京","country":"中国"}}
let parsedObj = JSON.parse(jsonStr);
console.log(parsedObj); // 输出 {name: "张三", age: 20, address: {city: "北京", country: "中国"}}
let jsonString = '{"name": "张三", "age": 20, "address": {"city": "北京", "country": "中国"}}';
let parsedJson = JSON.parse(jsonString);
console.log(parsedJson); // 输出 {name: "张三", age: 20, address: {city: "北京", country: "中国"}}
JavaScript设计模式入门
模块模式
模块模式是一种用于封装和组织代码的方式。通过立即执行函数表达式(IIFE)创建私有变量和方法,并返回公共的方法和属性。
示例代码:
let myModule = (function() {
let privateVar = "私有变量";
function privateFunction() {
console.log("私有函数");
}
return {
publicVar: "公共变量",
publicFunction: function() {
console.log("公共函数");
}
};
})();
console.log(myModule.publicVar); // 输出 "公共变量"
myModule.publicFunction(); // 输出 "公共函数"
构造函数模式
构造函数模式用于创建具有特定属性和方法的对象实例。构造函数可以重用,通过 new
关键字创建新对象。
示例代码:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log(`你好,我是${this.name},我${this.age}岁了`);
};
let person1 = new Person("张三", 20);
let person2 = new Person("李四", 25);
person1.greet(); // 输出 "你好,我是张三,我20岁了"
person2.greet(); // 输出 "你好,我是李四,我25岁了"
单例模式
单例模式确保某个类只有一个实例,并提供一个全局访问点。单例模式常用于需要全局唯一实例的情况,如数据库连接、日志记录器等。
示例代码:
let Singleton = (function() {
let instance = null;
function createInstance() {
let obj = new Object("我是唯一实例");
return obj;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
console.log(Singleton.getInstance()); // 输出 "我是唯一实例"
console.log(Singleton.getInstance() === Singleton.getInstance()); // 输出 true
观察者模式
观察者模式用于定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知并被自动更新。
示例代码:
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(observerFn => observerFn !== observer);
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
class Observer1 {
update(data) {
console.log(`观察者1收到数据: ${data}`);
}
}
class Observer2 {
update(data) {
console.log(`观察者2收到数据: ${data}`);
}
}
let subject = new Subject();
let observer1 = new Observer1();
let observer2 = new Observer2();
subject.subscribe(observer1.update.bind(observer1));
subject.subscribe(observer2.update.bind(observer2));
subject.notify("数据被更改了");
工厂模式
工厂模式用于创建对象,而不暴露创建逻辑,使程序更模块化、可维护。
示例代码:
function createProduct(name, price) {
return {
name: name,
price: price,
getName: function() {
return this.name;
},
getPrice: function() {
return this.price;
}
};
}
let product1 = createProduct("产品1", 100);
let product2 = createProduct("产品2", 200);
console.log(product1.getName()); // 输出 "产品1"
console.log(product2.getPrice()); // 输出 200
面试题实战演练与技巧
常见问题总结与解答
常见的面试题包括变量与作用域、异步编程、DOM操作、设计模式等。理解这些概念并能够灵活应用是面试成功的关键。
示例问题与解答:
问:解释JavaScript中的闭包。
答:闭包是函数和对其周围状态的引用组成的结构。这种结构使得函数可以记住并访问函数内部声明的变量,即使函数已经执行完毕。闭包可以用于创建私有变量和方法。
示例代码:
function createClosure() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
let closure = createClosure();
closure(); // 输出 1
closure(); // 输出 2
问:解释Promise和async/await之间的区别。
答:Promise是一个对象,用于表示异步操作的最终完成(或失败)及其结果值。async
和 await
是一种更简洁的异步编程语法,使得异步代码看起来像同步代码。
示例代码:
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作结果");
}, 1000);
});
}
asyncOperation().then((result) => {
console.log(result); // 输出 "异步操作结果"
}).catch((error) => {
console.error(error);
});
async function run() {
try {
let result = await asyncOperation();
console.log(result); // 输出 "异步操作结果"
} catch (error) {
console.error(error);
}
}
run();
如何准备面试
准备面试时,建议从以下几个方面入手:
- 基础知识:复习JavaScript的基础语法、数据类型、运算符、流程控制语句、函数、作用域等。
- 高级概念:深入学习异步编程、Promise、async/await、DOM操作、事件处理等高级概念。
- 设计模式:熟悉一些常见的设计模式,如模块模式、构造函数模式、单例模式等。
- 实践:通过编写代码来加深理解,尽量在实际项目中应用所学知识。
- 阅读面试题:多做面试题,尤其是常见的面试题,了解面试题的类型和解答方法。
- 模拟面试:可以找同学或朋友模拟面试,或者找在线资源进行模拟面试。
示例代码:
function sum(a, b) {
return a + b;
}
console.log(sum(3, 4)); // 输出 7
let arr = [1, 2, 3, 4, 5];
console.log(arr.filter((item) => item > 2)); // 输出 [3, 4, 5]
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作完成");
}, 1000);
});
promise.then((result) => {
console.log(result); // 输出 "异步操作完成"
}).catch((error) => {
console.error(error);
});
面试中常见陷阱及应对策略
面试中常见的陷阱包括一些常见错误或者误解。理解这些陷阱可以帮助你在面试中更好地表现。
示例陷阱与应对策略:
陷阱: this
的值与预期不符。
应对策略: 使用箭头函数或绑定 this
来确保正确性。
示例代码:
let obj = {
name: "对象",
getName: function() {
console.log(this.name); // 输出 "对象"
}
};
obj.getName();
let func = function() {
console.log(this); // 在全局作用域下输出 Window
};
func();
let arrowFunc = () => {
console.log(this); // 输出 Window,因为在全局作用域下
};
arrowFunc();
let anotherObj = {
name: "另一个对象",
func: function() {
console.log(this.name); // 输出 "另一个对象"
}
};
anotherObj.func();
陷阱: 使用 new
关键字创建对象时,没有正确使用构造函数。
应对策略: 使用构造函数,并确保返回正确的对象。
示例代码:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log(`你好,我是${this.name},我${this.age}岁了`);
};
let person1 = new Person("张三", 20);
let person2 = new Person("李四", 25);
person1.greet(); // 输出 "你好,我是张三,我20岁了"
person2.greet(); // 输出 "你好,我是李四,我25岁了"
共同学习,写下你的评论
评论加载中...
作者其他优质文章