深入理解 JS(讲师:张琪)
01 JS的基本概念
-
JS诞生:1995年,Brendan Eich进入网景公司,在公司"看上去与Java足够相似",但是比Java简单,使得非专业的网页作者也能很快上手"的要求下,用10天确定了设计思路并为其浏览器开发了js功能。[1]
-
什么是JavaScript?
JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。[2]
以下是 JavaScript 的一些基本概念:
-
变量:JavaScript 中的变量用于存储数据。使用 var、let 或 const 来声明变量,分别表示变量的作用域和可变性。
-
数据类型:JavaScript 的数据类型分为原始类型和对象类型。原始类型包括字符串、数字、布尔值、null、undefined 和 Symbol,对象类型包括数组、函数、对象等。
-
运算符:JavaScript 中的运算符用于对变量或值进行运算,包括算术运算符、比较运算符、逻辑运算符等。
-
条件语句:JavaScript 中的条件语句用于根据条件执行不同的代码块,包括 if 语句、switch 语句等。
-
循环语句:JavaScript 中的循环语句允许多次执行相同的代码块,常见的循环语句有 for 循环、while 循环、do...while 循环等。
-
函数:JavaScript 中的函数可以封装一段代码用于重复调用,函数可以接收参数并返回值。函数是 JavaScript 中最重要的一个特性之一,也是实现模块化编程的基础。
-
对象和面向对象编程:JavaScript 是一种基于对象的语言,它将所有事物都视为对象。JavaScript 中的对象可以包含属性和方法,也可以使用面向对象编程的方式创建自定义对象。
-
DOM 和 BOM:JavaScript 是一种用于浏览器的脚本语言,它可以操作文档对象模型(DOM)和浏览器对象模型(BOM),来修改页面内容和处理用户输入。
02 JS是怎么执行的
JavaScript 在浏览器中的执行过程如下:
-
解析 HTML:浏览器首先会解析 HTML 页面,根据 HTML 标记构建 DOM 树和 CSSOM 树。
-
解析 JavaScript:在解析 HTML 的过程中,如果遇到 script 标签,浏览器会将 script 标签中的 JavaScript 代码提取出来,然后执行。
-
编译和解释:JavaScript 引擎会对提取出来的 JavaScript 代码进行编译和解释,生成可执行的字节码或机器码。
-
执行 JavaScript 代码:当 JavaScript 代码被编译和解释后,它就可以被执行了。执行过程中,JavaScript 引擎会使用执行上下文来跟踪变量和函数的作用域、声明和赋值等信息。
-
更新 DOM:当 JavaScript 代码执行完成后,它可能会对 DOM 树进行操作,例如改变页面的内容或样式。
-
渲染页面:最后,浏览器会使用渲染引擎将 DOM 树和 CSSOM 树合并,并绘制出最终的页面。
而在 Node.js 中,JavaScript 的执行过程略有不同:Node.js 会使用 V8 引擎来执行 JavaScript 代码,但它并没有浏览器环境中的 DOM 和 BOM,而是提供了一组基于事件驱动的 API,可以用来处理文件、网络请求等操作。
03 JS的进阶知识点
闭包
JavaScript 中的闭包是指一个函数可以访问在其外部定义的变量,即使在函数被调用和返回后,这些变量仍然可以被使用。闭包可以通过在函数内部定义函数,并返回函数引用的方式实现。
下面是一个简单的闭包示例:
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
counter(); // 输出 3
在这个例子中,createCounter 是一个函数,它定义了一个名为 count 的局部变量,并返回一个匿名函数的引用。这个匿名函数可以访问 createCounter 内部定义的 count 变量,并且每次调用后都会将 count 加 1 并输出结果。
由于 JavaScript 中的函数都是对象,因此在每次调用 createCounter 函数时,都会创建一个新的执行上下文来保存 count 和匿名函数的引用。当 createCounter 返回匿名函数的引用后,外部作用域中的 count 变量并不会被销毁,而是被匿名函数持有,并且可以被多次调用。
闭包常常用于实现模块化编程和保护私有变量等场景。但是过多地使用闭包可能会导致内存泄漏等问题,因此需要谨慎使用。
this
在JavaScript中,this关键字是一个非常重要的概念。它指向当前代码执行的上下文对象,可以是全局对象、函数对象或者调用该函数的对象。
下面介绍一些常见场景下的this指向:
-
全局作用域下,this指向全局对象,在浏览器中通常是window对象,在Node.js环境中通常是global对象。
-
构造函数中,this指向创建的新对象。
-
对象方法中,this指向调用该方法的对象。
-
如果一个方法没有明确指定this,那么它的this值将根据调用上下文来判断。如果方法是通过点符号(.)调用的,this指向该方法所属的对象。如果方法是作为函数直接调用的,this通常指向全局对象。
-
使用apply、call和bind方法可以改变函数调用时的this指向。
下面是一些简单示例:
// 全局作用域
console.log(this === window); // true
// 构造函数
function Person(name) {
this.name = name;
}
const person = new Person('Tom');
console.log(person.name); // 'Tom'
// 对象方法
const obj = {
name: 'Bob',
sayName() {
console.log(this.name);
}
};
obj.sayName(); // 'Bob'
// 函数中的this
function func() {
console.log(this);
}
func(); // window 或 global(取决于环境)
// 改变this指向
function sayHi() {
console.log(`Hi, ${this.name}`);
}
const obj1 = { name: 'Alice' };
const obj2 = { name: 'Bob' };
sayHi.call(obj1); // 'Hi, Alice'
sayHi.apply(obj2); // 'Hi, Bob'
const sayHello = sayHi.bind(obj2);
sayHello(); // 'Hi, Bob'
需要注意的是,在箭头函数中,this指向的是定义该函数时的上下文,而不是调用该函数的上下文。这是因为箭头函数没有自己的执行上下文,而是继承了父级上下文的this值。