前言:作为一位入行3年的前端程序员练习生,打算今年把JS的基础复盘一下。
此文章会持续更新,有初级中级的同好们可以一起学习看看查漏补缺一下。
目前使用的技术栈
- 开发语言 : JS, TS
- 前端框架 - vue2,vue3
- 后端框架 - nest.js
JS基础 - JavaScript高级程序设计 (分析文章重点变成笔记)
第一章:什么是JavaScript ?
JavaScript 是一门用来与网页交互的脚本语言。
JavaScript 包含三个模块,ECMAScript,DOM,BOM。
ECMAScript:由ECMA-262定义并提供核心功能。
DOM:文档对象模型,提供与网页内容交互的方法和接口。
BOM: 浏览器对象模型,提供与浏览器交互的方法和接口。
第二章:HTML中的JavaScript
个人见解勿喷:其实在刚接触前端的时候看了下script标签是如何使用异步和同步加载的后面开始使用vue框架写代码后已经很多时间没有关注这块内容了,在复盘的过程中其实script标签基础还是很重要的,毕竟我们后面优化性能,如果优化打包后的js文件都和怎么在index.html引入文件有很大的关系。
HTML中主要是引入< script > 标签,可以使用行内的写法也可以使用src去引入外部文件。
-
defer属性: 把脚本推迟到文档渲染完毕后再执行。当浏览器遇到带有 defer 属性的 script 时,获取该脚本的网络请求也是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析并执行 JS 代码,而是等待 HTML 解析完毕再执行 JS 代码。
-
async属性: 异步加载脚本。当浏览器遇到带有 async 属性的 script 时,请求该脚本的网络请求是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器会暂停解析,先让 JS 引擎执行代码,执行完毕后再进行解析。
第三章:语言基础
- 语法 :
- 区分大小写,helloWorld和HelloWorld 是两个变量。
- 标识符: js里面的变量,函数,属性第一个字符必须是字母,下划线或者美元符号。大多数JS参数会使用驼峰的写法,因为ECMAScript内置的函数和对象使用了这样的格式。
- 注释: 单行注释和块注释
// 单行注释
/* 多行注释 */
- 严格模式: 'use strict'
- 语句:分号结尾(不是必要的,但是为了代码严谨性要加上), 控制语句中的代码块({}) 会让代码更清晰。
- 关键字与保留字
保留了一些关键字用于作为特定的用途。比如 if , else, switch 等。。。
- 变量
-
ECMAScipt变量是松散类型,可以保存任何类型的数据。ES中可以使用var,let,const 来定义变量。 const 和 let 只有在ES6后才可以使用。 var在任何版本中都可以使用。
-
var
作用域:
function test() {
var variable = 'helloWorld';
}
test();
console.log(variable) // 报错了!!!
这里报错的原因是因为在函数中定义的var是局部作用域,函数执行完后variable就被销毁了。
function test() {
variable = 'helloWorld';
}
test();
console.log(variable) // helloWorld
去掉var后,variable就变成了全局变量所以也成功的打印了。
声明提升(hoist):
function test() {
console.log(variable)
var variable = 'helloWorld';
}
test(); // undefined
var虽然是在打印后面声明的,但是因为var的声明提升机制,把variable的声明放到了函数的第一行。导致执行的时候不会报错,es在运行时等价于下面:
function test() {
var variable;
console.log(variable);
variable = 'helloWorld';
}
test(); // undefined
- let
let 也是用于声明变量,但是他的作用域是块级作用域。
if(true) {
let variable = 'let';
}
console.log(variable) // referenceError: variable 没有定义
暂时性死区: 在let 变量声明之前引用都会抛出RefereceError; 所以用let替代var是代码中更好的一个选择,相比于var定义会更加的规范。
全局声明: let在全局作用域中声明的变量不会成为window对象的属性(var会成为)
- const
const 和 let 的作用域都是一致的,但是const是用于声明常量的,当有修改的情况下会报错。 const也是主要在代码中出现的变量
const name = "john"; // 定义一个参数后续不会修改
const data = response.data.name; // 接口返回参数
在出现let和const后,var就不在使用了。并且const是优先于let使用命名变量。
5.数据类型 :
原始数据类型:undefined, Null, Boolean, String, Number, Symbol
typeof操作符可以用来鉴别变量的类型
- undefined : 未定义
- Null : 空指针,变量需要保存对象,而当时或者初始化的时候没有对象保存
- Boolean : true, false
- String : 字符串
- Number : 数字,浮点值等
- Symbol : 符号,es6新增数据类型
引用数据类型包括:
- 对象(Object):由键值对组成的无序集合,可以用于存储任意类型的数据。
- 数组(Array):可以容纳多个值的集合。
- 函数(Function):执行特定代码的代码块。
6.操作符 : 省略。。。 + - 等运算符
7.语句 : 流控制语句
- if 语句
if (condition) statement else statement2 ;
或者...
if(a) {
dosomething();
}else if(a > 0) {
dosomething();
}else{
dosomething();
}
- do-while 语句
do {
statement
} while (expression);
- while 语句
while {expression} statement;
- for 语句
for( initialization ;expression ;post-loop-expression) statement ;
- switch 语句
switch(expression) {
case value1:
statement
break;
case value2:
statement
break;
default:
statement
}
等等等 用来控制流的语句格式。 个人的话主要使用三种 ifelse,for , switch 这三种控制语句是用的比较多的, for的话 foreach 和 map 等api能更好的遍历数据。
8.函数 :
基本语法 :
function test (arg , arg2 ) {
statement
}
js函数和其他语言不同的一点,不需要指定函数的返回值,如果没有定义函数返回undefined
第四章:变量,作用域与内存
- **原始值和引用值 **
原始值(primitive value)是最简单的数据 - string, number, boolean ...
引用值(reference value)是有多个值组成的对象。
原始值和引用值的区别,原始值的变量是按照值访问的,操作的是变量的实际值,而引用值是保存在内存中的对象,所以变量改变的时候js操作的是对象的引用而非它实际的对象。
- 复制值
原始值的复制比较的简单,比如
let a = 1;
let b = a;
console.log(b) // 1
}
当a赋值给b的时候,a和b都是独立的两个变量,这两个变量可以单独使用使互相不影响的
但是呢, 引用值的复制是不一样的。
let obj1 = new Object();
let obj2 = obj1;
obj1.name = 'john'
console.log(obj2.name) // john
}
从这个案例中可以看出,我改变了obj1的name属性,也改变了obj2的name属性。 因为let obj2 =obj1 这样的复制是浅拷贝,obj1和obj2这两个对象指向的都是推内存中的同一个对象。
- 传递参数
let a = 'john';
function changeName (data) {
let a = 'newName';
console.log(a) // newName
}
changeName(a);
console.log(a) // john
函数内的变量是会跟着函数结束后一起销毁
- 确定类型
原始类型使用typeof去检查变量的类型是有效的除非是null,(typeof null)返回的是object 所以ES提供了instanceof操作符用来确定引用值的类型。
person instanceof Object
person instanceof Array
person instanceof RegExp
- 执行上下文与作用域
执行上下文的概念是非常重要的。变量或函数的上下文觉得了他们可以访问哪些数据。
全局对象 :在浏览器中是window对象,所以通过var定义的全局变量和函数都会成为window对象的属性和方法。使用let和const的顶级声明不会定义在全局上下文
var color = 'red';
function getColor() {
let a = 1;
funciton changeColor() {
let b = 2
}
funciton noColor() {
let a = b
}
}
getColor()
- 内部的函数是可以拿到外部的变量,但是外部是无法访问到内部的变量。上下文在其所有的代码都执行完毕后会被销毁,包括定义在它上面所有的变量和函数。在关闭应用的情况下才会被销毁。
- 每个函数都有自己的上下文,当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。 当函数执行完毕后,上下文栈会弹出该函数的上下文,并且将控制权返还给之前的执行上下文。
- 作用域链 - 上下文中的代码在执行的时候,会创建变量对象的一个作用域链。
-
作用域链增强
try-catch 中的catch块和with语句都会在作用域链前端添加一个变量对象。
-
变量声明
前段时间也遇到过一个开发中变量声明问题,其中我们的app里初始化webview的阶段,一个sdk注入了一段全局的js桥接代码,然后在另外一个在app环境中运行的h5项目报错了。 最后查到引发这个错误的原因是一个变量。js桥接里使用了 let config = {} 声明了一个config对象,然后在h5项目中也定义了一个let config对象。导致出现了重复声明。改变let config变成 var config后续就不会触发这个报错。
- 垃圾回收
JS是使用垃圾回收的语言,垃圾回收思路:确定哪个变量不会再使用,然后释放它占用的内存。
-
标记清理
最常用的回收策略是标记清理。当变量进入上下文,比如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记,当变量离开上下文时,也会被加上离开上下文的标记。
-
引用计数
思路:对每一个值都记录它被引用的次数。
- 内存管理
内存泄漏: 内存漏是指应用程序中的内存不再被使用但仍然被占用,导致内存消耗逐渐增加,最终可能导致应用程序性能下降或崩溃。 通常是由于开发者编写的代码未正确释放不再需要的对象或者数据而导致的。
- 意外声明全局变量
function fn() {
name = 'john';
}
-
闭包
-
定时器
-
事件监听器
-
循环引用
第五章:基本引用类型
略过