浅浅的梳理下,变量就是用来存储和使用的。方法是包含许多指令,来完成某一项任务或行为。
面向过程和面向对象,个人将面向对象进行拆分理解,“面向”这两个字就是转换思路,面对解决不同的事情,要用对应的办法。对象就是当前的事物,事物已对象形式,包含属性(特征)和行为(动作)。
面向对象和 “面向过程”的核心区别,就是前者先想 “有什么事物(对象)”,再想 “这个事物能做什么”;后者先想 “要做哪些步骤”,再一步步实现。
面向过程适合单一、独立、步骤化的小功能,面向对象适合关联、需要整合 / 复用的大模块。
-
面向过程:就像 “只实现汽车刹车这一个功能”—— 你只需要关注 “踩刹车→切断动力→刹车片夹紧→车速降为 0” 这几个步骤,按顺序写代码就行,不用管汽车的品牌、颜色这些无关属性。它的核心是 “按步骤做事”,适合单一、独立、步骤明确的小功能。
-
面向对象:就像 “开发整个汽车控制系统”—— 你需要统筹汽车的所有属性(品牌、速度、油量)和所有行为(启动、加速、刹车、鸣笛),把这些都封装在 “汽车” 这个对象里
面向对象就是把计算机里抽象的 “数据” 和 “操作”,包装成我们能直观理解的 “现实事物模型”,更好理解的去进行编程实现代码逻辑。
函数
函数声明: function A () , 单独声明,不赋值给变量,有提升特性
函数表达式: var A = function() ,把匿名函数赋值给变量A,这里的函数是匿名函数(没有名字)。或者 var A = function fn() , 把名为fn的函数赋值给A,这里就不是匿名函数了.
这种写法的意义:函数名fn仅在函数内部可用,方便递归调用(比如阶乘函数),但外部依然用变量名调用。
// 调用时用变量名,而非函数名
fn console.log(multiply(2, 3)); // 输出:6
console.log(fn(2, 3)); // 报错!fn是函数内部的局部名称,外部访问不到
箭头函数(匿名函数表达式的简化) const divide = (a, b) => a / b;
| 特征 | 函数表达式(匿名 / 具名) | 函数声明 |
|---|---|---|
| 核心形式 | 函数作为值赋值给变量 | 单独声明函数 |
| 提升特性 | 变量提升(值为 undefined) | 函数整体提升 |
| 命名 | 可匿名 / 具名 | 必须有名字 |
| 适用场景 | 按需定义、作为参数传递 | 全局 / 局部通用函数 |
document对象
通过使用document对象可以访问和修改用页面上的内容,进行交互式相应。
document对象下方的每一个方块被称为节点,每一个节点都是对象形式,即每个元素都会创建各自的节点(一种对象)
console.log(document) 这是看不到像普通 JS 对象那样的 “键值对” 格式,而是显示成页面 DOM 结构的可视化形式,这并不是因为 document 不是对象,而是浏览器对这类特殊对象做了可视化优化。document 本质上是一个对象(更准确地说是 DOM 对象 / 宿主对象),只是浏览器控制台为了方便你调试页面结构,把它渲染成了 DOM 树的可视化格式,而非普通 JS 对象的键值对格式。
但可以通过console.dir(document)标准对象键值对格式查看 DOM 对象的所有属性 / 方法。
加分号
计算机是一步一步执行指令的,每一条单独的指令或者步骤都被称为一条语句,所以每一条语句用分号结束,让代码更容易阅读,分号同时也告知解析器这个步骤执行完毕,应当开始执行下一条语句。
表达式
常见表达式类型:字面量、算术、赋值、函数调用、成员访问、逻辑、三元表达式等,都是日常开发中高频使用的。
断标准:表达式有 “计算结果”,语句是 “执行动作”
| 表达式类型 | 示例 | 计算结果(示例) |
|---|---|---|
| 字面量表达式 | 123、"hello"、true | 字面量本身(123、"hello") |
| 算术表达式 | a+b、10*5、x/2 | 计算后的数值(如10*5=50) |
| 赋值表达式 | e = 1、s = a+b | 被赋的值(如e = 1结果是1) |
| 函数调用表达式 | Math.max(1,2)、alert() | 函数的返回值(如Math.max(1,2)=2) |
| 成员访问表达式 | document.title、arr[0] | 访问到的属性 / 元素值 |
| 逻辑表达式 | a && b、x > 5 | 布尔值(如3>5=false) |
| 三元表达式 | age >= 18 ? "成年" : "未成年" | 条件成立的结果 |
对象
对象中的属性,其实可以看成一个变量来理解,隶属于某个对象的变量,但这个 “变量” 的 “使用范围” 完全依附于所属的对象 —— 只有通过这个对象(或对象的实例)才能访问 / 修改它,脱离了这个对象,就无法直接使用这个属性。
修改对象的基本操作:
对象名.属性名 = 属性值
或
对象名['属性名'] = 属性值
删除对象的基本操作:
delete 对象名.属性名
this
this是一个关键字,通常在函数内部或者对象内部使用,通常是指向当前函数所操作的对象。this取什么样的值是在调用时,而非定义时。
var width = 600
var shape = {width:300}
var showwidth = function(){
document.write(this.width)
}
shape.getWidth = showwidth;
shape.getWidth()
输出的是300 , 这里this指向当前调用的对象,而不是全局上下文。
对象中顺序不是很重要,因为是通过键访问每一个数据,使用点语法。
数组也是一种特殊类型的对象,存储一组相关的数据。
微任务和宏任务
js是一个单线程,同一时间只能做一件事情。如果操作DOM是多线程,浏览器无法以哪个线程为主,造成页面操作错乱。
但为了不被耗时操作(比如网络请求、定时器)阻塞,就设计了任务队列机制
-
同步代码:直接在主线程执行,执行完才会处理异步任务;
-
异步任务:分成宏任务和微任务两类,放进对应的队列,等同步代码执行完后再按规则执行。
所以微任务和宏任务出现在异步任务中,宏任务是 “粗粒度” 的异步任务,执行优先级更低,每次执行一个宏任务后,会先清空所有微任务,再执行下一个宏任务。
常见的宏任务类型
-
setTimeout、setInterval、setImmediate(Node 环境) -
requestAnimationFrame(浏览器环境) -
I/O 操作(比如文件读写、网络请求)
-
页面渲染(浏览器环境)
-
script 标签中的整体代码
微任务执行优先级更高,所有微任务会在当前宏任务执行完、下一个宏任务执行前全部执行完毕。
常见的微任务类型
-
Promise.then()、Promise.catch()、Promise.finally() -
async/await(本质是 Promise 的语法糖,await 后的代码会进入微任务) -
queueMicrotask()(手动创建微任务) -
MutationObserver(浏览器环境)// 同步代码(第一个宏任务的开头) console.log('1. 同步代码'); // 宏任务:setTimeout setTimeout(() => { console.log('4. 宏任务 - setTimeout'); // 宏任务内的微任务 Promise.resolve().then(() => { console.log('5. 宏任务内的微任务 - Promise.then'); }); }, 0); // 微任务:Promise.then Promise.resolve().then(() => { console.log('3. 微任务 - Promise.then'); // 微任务内嵌套微任务,仍会在本轮微任务阶段执行 queueMicrotask(() => { console.log('3.1 嵌套微任务 - queueMicrotask'); }); }); // 同步代码 console.log('2. 同步代码'); 1. 同步代码 2. 同步代码 3. 微任务 - Promise.then 4. 嵌套微任务 - queueMicrotask 5. 宏任务 - setTimeout 6. 宏任务内的微任务 - Promise.then -
先执行所有同步代码,输出
1、2; -
同步代码执行完,清空微任务队列:先执行
Promise.then输出3,再执行嵌套的queueMicrotask输出3.1; -
微任务队列清空后,执行下一个宏任务(
setTimeout),输出4; -
这个宏任务执行完,又清空它内部的微任务队列,执行
Promise.then输出5。
async/await 与微任务的关系
async/await 是 Promise 的语法糖,await 后面的代码会被包裹成 Promise.then(微任务):
async function test() {
console.log('1. async 同步代码');
await Promise.resolve(); // 此处暂停,后面的代码进入微任务
console.log('3. await 后的微任务');
}
console.log('0. 最外层同步代码');
test();
console.log('2. 最外层同步代码');
0. 最外层同步代码
1. async 同步代码
2. 最外层同步代码
3. await 后的微任务
this
this 的指向不是在定义时确定,而是在调用时确定—— 谁调用函数,this 就指向谁。
function fn1(){
console.log(this)
}
fn1() //window
fn1.call({x:100}) // {x:100}
const fn2 = fn1.bind({x:200})
fn2() //{x:200}
new 调用(构造函数)→ this 指向新实例
// 构造函数
function Person(name) {
this.name = name; // this 指向 new 出来的实例
console.log(this); // 输出:Person { name: '张三' }
}
// new 调用
const p1 = new Person('张三');
console.log(p1.name); // 输出:张三(this 绑定到 p1)
call/apply/bind 调用 → this 指向指定对象
通过 call/apply/bind 显式绑定 this
-
call/apply:立即执行函数,第一个参数是this指向的对象; -
bind:返回一个新函数,永久绑定this,不会被覆盖(除非用new)function sayName() { console.log(this.name); } const obj1 = { name: '李四' }; const obj2 = { name: '王五' }; // call 绑定 this 到 obj1 sayName.call(obj1); // 输出:李四 // apply 绑定 this 到 obj2(和 call 唯一区别:参数传递方式不同) sayName.apply(obj2); // 输出:王五 // bind 绑定 this 到 obj1,返回新函数 const fn = sayName.bind(obj1); fn(); // 输出:李四 fn.call(obj2); // 仍输出:李四(bind 绑定后不会被 call 覆盖)
对象方法调用 → this 指向调用对象
当函数作为对象的方法调用时,this 指向调用这个方法的对象
普通函数调用 → this 指向全局对象
当函数既不是 new 调用、也不是显式绑定、也不是对象方法调用时,就是 “普通调用”:
-
浏览器环境:
this指向window; -
Node 环境:
this指向global; -
严格模式(
use strict):this指向undefined(避免污染全局)。
箭头函数 → 无自己的 this,继承外层作用域的 this
箭头函数是特例:没有自己的 this,它的 this 是定义时外层作用域的 this
-
箭头函数不能用
new调用(没有constructor); -
箭头函数不能用
call/apply/bind修改this。const obj = { name: '孙八', // 普通方法:this 指向 obj normalFn: function() { console.log(this.name); // 输出:孙八 }, // 箭头函数:this 继承外层(这里外层是全局,所以指向 window) arrowFn: () => { console.log(this.name); // 输出:undefined(window 没有 name) }, // 解决回调函数 this 问题(常用) fixCallback: function() { // 箭头函数继承外层的 this(即 obj) setTimeout(() => { console.log(this.name); // 输出:孙八 }, 0); } }; obj.normalFn(); obj.arrowFn(); obj.fixCallback();
DOM 事件处理函数 → this 指向触发事件的元素
在浏览器中,DOM 事件绑定的函数里,this 指向触发事件的 DOM 元素。
const btn = document.getElementById('btn');
btn.onclick = function() {
console.log(this); // 输出:<button id="btn">点击</button>
this.style.color = 'red'; // 按钮文字变红
};
特殊
const zhangsan = {
name:'张三',
sayHi(){
console.log(this) //当前对象 zhangsan
},
wait(){
//setTimeout中有个函数,这个函数this指向是window,
//这里的触发执行是setTimeout触发的,而不是zhangsan对象触发执行
setTimeout(function (){
console.log(this) //window
})
},
waitAgain(){
//setTimeout中的函数是箭头函数,虽然是setTimeout触发,但是箭头函数取值是上级作用域的值
//箭头函数不会创建自己的 `this` 上下文,是继承的是外层作用域
//这里箭头函数的外层作用域是 `waitAgain` 方法的作用域,`this` 指向 `zhangsan`
setTimeout(()=>{
console.log(this) //当前对象
})
}
}
// 箭头函数写法
waitAgain() {
setTimeout(() => {
console.log(this); // zhangsan
});
}
// 等价于传统保存 that 写法
waitAgain() {
const that = this;
setTimeout(function () {
console.log(that); // zhangsan
});
}
| 调用场景 | this 指向 | 优先级 |
|---|---|---|
| new 调用(构造函数) | 新创建的实例对象 | 最高 |
| call/apply/bind 调用 | 显式指定的对象 | 次高 |
| 对象方法调用(obj.fn ()) | 调用方法的对象(obj) | 中等 |
| 普通函数调用(fn ()) | 全局对象(window/global)/ 严格模式 undefined | 较低 |
| 箭头函数 | 定义时外层作用域的 this | 特殊 |
| DOM 事件函数 | 触发事件的 DOM 元素 | 特殊 |
-
this指向 “调用者”,调用时才确定,不是定义时; -
优先级:new > call/apply/bind > 对象方法 > 普通调用;
-
箭头函数无自己的
this,继承外层的this,无法被修改