深入理解JS
一、JS基本概念
JavaScript是一种高级的、面向对象的编程语言,通常用于在Web页面中添加交互和动态效果。不仅在前端开发,后端开发中,JavaScript结合node.js能够实现搭建服务器、处理HTTP请求、访问数据库等功能。
JavaScript的发展:
JavaScript的渲染进程
在现代浏览器中,JavaScript 代码通常运行在
渲染进程中。它主要负责处理网页的交互和动态效果,以及添加、删除或修改DOM元素等。
浏览器进程:
| 现代浏览器进程 | 作用 |
|---|---|
| Browser进程 | 主进程,管理其他进程 |
| GPU进程 | 处理图形操作 |
| 插件进程 | 运行插件 |
| Utility进程 | 处理无交互任务 |
| 渲染进程 | 处理网页渲染 |
JavaScript的特点
JavaScript的数据类型
JavaScript的数据类型可以分为两类,一类为
原始类型(Primitive types)和引用类型(Reference types)。
代码实现数据检测
function checkDataType(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}
console.log(checkDataType(123)); // 输出:Number
console.log(checkDataType('hello')); // 输出:String
console.log(checkDataType(true)); // 输出:Boolean
console.log(checkDataType(undefined)); // 输出:Undefined
console.log(checkDataType(null)); // 输出:Null
console.log(checkDataType([1, 2, 3])); // 输出:Array
console.log(checkDataType({})); // 输出:Object
console.log(checkDataType(function(){})); // 输出:Function
console.log(checkDataType(Symbol())); // 输出:Symbol
console.log(checkDataType(new Date())); // 输出:Date
JavaScript的作用域
JavaScript的作用域指的是变量、函数和对象在代码中被访问的范围。JavaScript采用词法作用域,即变量的作用域由它们在代码中声明的位置决定。
JavaScript有两种作用域:
| 作用域 | |
|---|---|
| 全局作用域 | 代码中任何地方都可以访问的变量和函数 |
| 局部作用域 | 变量和函数只能在其定义的函数内部访问。 |
全局作用域代码示例:
var globalVar = "I am a global variable!";
let anotherGlobalVar = "I am also a global variable!";
function myFunction() {
console.log(globalVar); // 输出"I am a global variable!"
console.log(anotherGlobalVar); // 输出"I am also a global variable!"
}
myFunction();
局部作用域代码示例:
function myFunction() {
let localVar = "I am a local variable!";
const anotherLocalVar = "I am also a local variable!";
console.log(localVar); // 输出"I am a local variable!"
console.log(anotherLocalVar); // 输出"I am also a local variable!"
}
myFunction();
JavaScript变量提升
在 JavaScript 中,变量提升是指在代码执行前将变量声明“移动”到它们所在作用域的顶部,这意味着可以在其声明之前使用这些变量。
代码示例:
①
console.log(myVar); // 输出"undefined"
var myVar = "Hello World!";
在使用myVar之前声明了它,但JavaScript引擎将会在执行代码前将其声明“提升”至作用域的顶部。
②
console.log(myVar); // 输出"undefined"
var myVar = "Hello World!";
myVar的声明被提升到作用域的顶部,但由于我们在声明前尝试访问变量的值,所以输出结果为undefined。
③
myFunction();
function myFunction() {
console.log("Hello World!");
}
在调用myFunction之前定义了该函数。由于函数声明也被提升到作用域的顶部,因此即使在调用函数之前声明该函数。
*** 需要注意的是,使用
let和const关键字声明的变量并不会被提升。如果尝试在其声明之前访问这些变量,则会引发一个ReferenceError错误。
二、JavaScript是怎么执行的
JavaScript代码的执行分为两个阶段:
解析和运行
解析阶段
在解析阶段,JavaScript引擎会对代码进行词法分析和语法分析,生成抽象语法树(Abstract Syntax Tree,AST),并且检查代码中是否有语法错误。如果有错误,就会在这个阶段报错。
graph TD
词法分析/语法解析 --> token/AST-->代码生成
运行阶段
在运行阶段,JavaScript引擎会根据生成的
AST来逐行执行代码。在执行过程中,JavaScript引擎会将变量和函数声明提升到作用域顶部,以便在后续的执行中能够正确地访问它们。同时,JavaScript引擎也会对代码进行优化,例如使用JIT(Just-In-Time)编译器将频繁执行的代码编译成本地机器码,从而提高代码的执行效率。
graph TD
AST --> 编译-->机器码-->指令序列/代码生成 -->垃圾回收
三、JavaScript进阶
闭包
JavaScript中的闭包是指
一个函数可以访问其外部环境中定义的变量,即使这些变量在函数被调用后已经不存在也可以访问。简单来说,闭包就是一个函数和其相关的引用环境组合而成的实体。
代码示例:
function outerFunction() {
var outerVariable = "I am in the outer function!";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
var innerFunc = outerFunction();
innerFunc(); // 输出: "I am in the outer function!"
This
在 JavaScript 中,
this关键字指向当前执行代码的对象。它的值取决于函数被调用的方式。
当一个函数被直接作为全局对象的属性调用时,this 指向全局对象 window:
function myFunction() {
console.log(this);
}
myFunction(); // 输出: Window { ... }
当一个函数被作为对象的方法调用时,this 指向该对象:
var myObject = {
myMethod: function() {
console.log(this);
}
};
myObject.myMethod(); // 输出: Object { ... }
当一个函数被使用 call() 或 apply() 方法调用时,可以通过这些方法的第一个参数来指定 this 的值:
function myFunction() {
console.log(this);
}
var myObject = {};
myFunction.call(myObject); // 输出: Object { ... }
当一个函数被作为构造函数调用时,this 指向新创建的对象:
function Person(name) {
this.name = name;
console.log(this);
}
var person1 = new Person("Alice"); // 输出: Person { name: "Alice" }
垃圾回收
JavaScript的垃圾回收是自动进行的,它通过标记清除算法来实现。当一个对象不再被引用时,它就会被标记为垃圾对象,并在垃圾回收器运行时被清除。
代码示例:
function createObject() {
var obj = {};
return obj;
}
var obj1 = createObject(); // 创建一个对象并将它赋值给obj1
var obj2 = createObject(); // 再创建一个对象并将它赋值给obj2
obj1.reference = obj2; // obj1 引用了 obj2,形成了循环引用
obj1 = null; // 将 obj1 设为 null,此时它所引用的对象被标记为垃圾对象
obj2 = null; // 同样将 obj2 设为 null,但它所引用的对象并不会被立即清除
// 垃圾回收器运行后,发现 obj2 所引用的对象也没有被其他变量引用,因此该对象被标记为垃圾对象
// 最终内存被释放,程序结束
笔记参考
- 维基百科、百度百科
- 课程资料、课件
- www.freecodecamp.org/news/what-i…