目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、作用域(可拓展)
作用域决定了变量、函数和对象在代码中的可访问性。JavaScript中的作用域分为以下几种:
- 全局作用域
- 函数作用域
- 块级作用域
- 词法作用域
- 模块作用域
- 动态作用域
- 作用域链
二、闭包
1.闭包(Closure)
是指一个函数能够访问并记住其词法作用域(Lexical Scope),即使这个函数在其词法作用域之外执行。闭包是 JavaScript 中非常强大的特性,它允许函数“记住”并访问定义时的环境,即使这个环境已经不再存在。
const foo = ()=>{ var arr=[]; var i; for (i=0;i<10;i++){ arr[i]=function In(){ console.log(i); } } return arr[0]; }在这个代码中,由于内部函数In调用外部变量i 即使foo函数执行完毕,var i 仍会被In记住,此时In就形成了一个闭包
2.闭包的使用场景
-当使用FP的时候基本就会使用闭包
-当一个函数执行,和上下文相关,就有闭包
-闭包是用来保存执行环境的,也包括环境内的变量
3.闭包的实际应用(可拓展)
3.1.1 数据封装
3.1.2 回调函数
3.1.3 函数柯里化
3.1.4 模块模式
4.闭包的缺陷
影响性能:过度使用闭包尤其是循环创建会降低性能
内存泄漏:闭包会导致其词法作用域中的变量无法被垃圾回收,从而可能导致内存泄漏
三.this
非严格模式this
普通函数被调用时,因为全局的方法和属性都是
window对象的方法和属性,所以this指向全局对象(浏览器中的window),严格模式下,this的值是undefined。函数调用对象this
当函数作为对象的方法调用时,this指向该对象。
当函数作为构造函数调用时,this指向新创建的对象。(函数通过new关键字调用,this指向新创建的实例对象)
箭头函数中的this
首先,箭头函数没有自己的this,全局作用域下,箭头函数的this指向全局对象,而在对象的方法中使用箭头函数时,this指向定义箭头函数时所在上下文的this值。
注意:
箭头函数没有自己的
arguments对象。不能用作构造函数(不能用
new调用)。没有
prototype属性。事件处理函数中的this
在DOM事件处理函数中,this默认指向触发事件的DOM元素。
显式绑定thiscall,apply,bind可以显式的绑定this指向特定对象。
四.面向过程和面向对象
1.JS对象的创建
//JS对象的创建 Object.create(); var bar = {}; new 关键字; //三者的区别Object.create()创建了一个对象 let p = Object.create(q) ; // ->p._proto_===q;旧的
var声明有作用域混乱、变量提升等问题。p的原型指向q; //原型链
当需要调用p对象的一个方法或属性时,如果p上没有则去q
5. 模块化 (import / export)
为什么需要?
将代码拆分成独立的模块,便于管理、复用和维护,避免全局污染。
-
导出(
export):
// module.js
// 1. 命名导出(可以有多个)
export const apiKey = '123abc';
export function sum(a, b) { return a + b; }
export class Person {}
// 或统一导出
const apiKey = '123abc';
function sum(a, b) { return a + b; }
export { apiKey, sum }; // 推荐此方式
// 2. 默认导出(只能有一个)
export default function() { console.log('默认导出'); }
// 或
const myFunc = () => {};
export default myFunc;
导入(import):
// app.js
// 1. 导入命名导出(必须用花括号,名字要匹配)
import { apiKey, sum } from './module.js';
// 重命名
import { apiKey as key, sum as add } from './module.js';
// 全部导入为一个对象
import * as module from './module.js';
console.log(module.apiKey);
// 2. 导入默认导出(不用花括号,名字任意)
import myFunction from './module.js';
// 3. 混合导入
import myFunction, { apiKey, sum } from './module.js';
6. Promise
为什么需要?
解决传统回调函数的“回调地狱”问题,用更优雅的方式处理异步操作。
-
三种状态:
pending(进行中)、fulfilled(已成功)、rejected(已失败)。 -
基本用法:
const myPromise = new Promise((resolve, reject) => {
// 执行异步操作,比如网络请求
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功!'); // 状态从 pending -> fulfilled
} else {
reject('操作失败!'); // 状态从 pending -> rejected
}
}, 1000);
});
// 使用 .then() 处理成功,.catch() 处理失败
myPromise
.then(result => {
console.log(result); // "操作成功!"
})
.catch(error => {
console.error(error); // "操作失败!"
})
.finally(() => {
console.log('无论如何都会执行'); // 清理工作
});
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`最终结果: ${finalResult}`))
.catch(error => console.error(`错误: ${error}`));
// 清晰、线性,避免了层层嵌套的回调
Promise.all([promise1, promise2, promise3]) // 全部成功才成功,一个失败就失败
.then(values => { /* values 是结果的数组 */ });
Promise.race([promise1, promise2]) // 哪个先完成就用哪个的结果
.then(value => { /* 第一个完成的结果 */ });
Promise.allSettled([promise1, promise2]) // 等全部完成,无论成功失败
.then(results => { /* 包含状态和结果的数组 */ });
7. async / await
为什么需要?
让异步代码的写法像同步代码一样直观,是基于 Promise 的语法糖。
-
基本规则:
-
async函数总是返回一个 Promise。 -
await只能在async函数内部使用,它会暂停函数的执行,等待 Promise 完成,并返回结果。
-
-
用法:
// 用 async/await 重写上面的 Promise 链
async function doTasks() {
try {
const result = await doSomething(); // 等待 doSomething() 完成
const newResult = await doSomethingElse(result); // 用上一个结果继续
const finalResult = await doThirdThing(newResult);
console.log(`最终结果: ${finalResult}`);
} catch (error) {
console.error(`错误: ${error}`);
} finally {
console.log('清理工作');
}
}
doTasks(); // 调用 async 函数返回的也是 Promise
-
优点:代码完全是线性的,没有回调,可读性极强。
-
并行处理:
async function fetchData() {
// 顺序执行(一个接一个)
const user = await fetchUser();
const posts = await fetchPosts();
// 并行执行(同时开始)
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
}
8. Class (类)
为什么需要?
为 JavaScript 提供更接近传统面向对象语言的、清晰的语法来创建对象和处理继承。
-
基本语法:
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法(添加到原型上)
greet() {
return `你好,我是${this.name}`;
}
// 静态方法(属于类本身,实例不能调用)
static info() {
return '这是一个Person类';
}
}
const p1 = new Person('元', 1);
console.log(p1.greet()); // "你好,我是元"
console.log(Person.info()); // "这是一个Person类"
继承:
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 必须先调用父类的 constructor
this.grade = grade;
}
// 重写父类方法
greet() {
return super.greet() + `,是一名${this.grade}年级学生。`;
}
}
const s1 = new Student('小明', 18, '高三');
console.log(s1.greet()); // "你好,我是小明,是一名高三年级学生。"
Getter / Setter:
class Rectangle {
constructor(width, height) {
this._width = width;
this._height = height;
}
get area() { // 像属性一样访问
return this._width * this._height;
}
set width(value) { // 像属性一样设置
if (value > 0) this._width = value;
}
}
const rect = new Rectangle(10, 5);
console.log(rect.area); // 50 (不是 rect.area())
rect.width = 20; // 触发 setter
重要提示:JavaScript 的 class 本质仍是基于原型的继承的语法糖,理解原型链仍然至关重要。
总结
变量提升,函数提升
Js本身没有类,Js万物皆对象
this 是动态绑定的,取决于函数调用的上下文。
普通函数调用时,this 指向全局对象(非严格模式)或 undefined(严格模式)。
对象方法调用时,this 指向该对象。
构造函数调用时,this 指向新创建的实例。
箭头函数没有自己的 this,它会继承外部函数的 this。
显式绑定:通过 call()、apply() 或 bind() 可以显式设置 this。
理解 this 的关键在于调用时的环境,它决定了 this 的指向。
实践建议
立即实践:在你的代码中全面替换掉 var,改用 let/const。
拥抱简洁:在合适的场景(特别是需要固定 this时)使用箭头函数。
告别拼接:所有字符串拼接都用模板字符串。
善用解构:在函数参数、数组/对象取值时优先考虑解构。
模块化思维:任何项目都应拆分为模块,用 import/export管理。
异步首选:处理异步操作时,优先使用 async/await,其次是 Promise,尽量避免传统回调。
组织代码:当需要创建多个相似对象或有继承关系时,使用 class来组织代码。
这些特性共同构成了现代 JavaScript 开发的“标准语法”,掌握它们是写出高质量、可维护代码的前提

1892

被折叠的 条评论
为什么被折叠?



