首先得了解什么是JS引擎
JS引擎是执行 JavaScript 代码的程序或解释器,它负责将高级 JavaScript 代码转换为计算机能够理解的低级机器码。
通俗一点来讲,JS引擎就像一个翻译官,能够将你写的js代码翻译成计算机能够读懂的语言,这样计算机就能够按照你的意思去工作。
目前了解的两个主要的JS运行环境
- 浏览器
浏览器中的JS引擎能够让网页实现各种交互效果,像点击按钮后弹出提示框,或者动态更新页面内容等。 - Node.js
Node.js则可以执行后端代码,能够让JS脱离浏览器环境,在服务器端运行,比如搭建Web服务器,处理HTTP请求等。
JS引擎执行js代码的过程
- 词法分析 把代码拆解为一个个词法单元
词法单元:代码中最小的有意义的部分,比如:变量名,函数名,关键字,运算符等
js
var a = 2//以下就是拆解后的词法单元
var
a
=
2
//空格在这里的作用是分隔`var`和`a`,属于分隔符,但在词法分析中通常不会被当作独立的词法单元保留,而是作为词法单元之间的边界标记。
-
语法分析 ---AST(抽象语法术)
就是对上一步词法分析拆解的词法单元进行语法分析,分析它们都是干什么的,同时查找语法是否有问题
-
代码生成
将AST转换为目标代码(会与源代码有差异,也就是进行了一些优化后的代码)
-
执行代码
S作用域
- 全局作用域
最外层的作用域,声明在函数和代码块之外 - 函数作用域
在函数内部声明的作用域,也就是在函数内部声明的变量,只能在函数内部生效 - 块级作用域
前提是由{}包裹的代码块,其次是还要使用let 和 const声明的变量,才能够形成块级作用域,这两者缺一不可
特殊点:即使有{}包裹,但是用var进行变量声明,这形成不了块级作用域,该变量还是可以被外部作用域访问
js
var a = 10;//属于全局作用域
console.log(a);//在全局中查找a
//输出结果:10
function foo(a, b) {
{
let a = 10//属于块级作用域
const b = 10//属于块级作用域
console.log(a + b);//在块级作用域内查找a和b
//输出结果:20
}
console.log(a + b);////属于函数作用域,在函数作用域内查找a和b的值,也就是传进来的两个参数a,b,不能用块级作用域内的a,b
//输出结果:3
}
foo(1, 2)
作用域查找规则
- 先在当前作用域中查找,找得到就返回,找不到就去外层作用域查找
- 只能从内到外查找,不能从外到内查找
(比如:在当前函数作用域内找到你想要的变量a你就可以去该函数的外层作用域内找你想要的变量a但如果反过来,外层作用域想要用变量a,但外层没有,而函数作用域内有,这时,就不能由外向内层进行查找了)
js
var a = 10;
function foo(b) {
{
const b = 10
console.log(a + b);//在块级作用域内又b直接用,但是没有a,则向外层作用域找,也就是函数foo的作用域,也没有就再去外层作用域,也就是全局作用域内找到a=10
//输出结果:20
}
function bar() {
console.log(a + b);//在函数bar内部都没有a,b,则向外层的函数foo的作用域内找,b=2,a没找到,再去外层作用域,也就是全局作用域内找到a=10
//输出结果:12
}
bar()
}
foo(2)
console.log(a + b);//全局作用域尝试访问变量b,但全局没有定义b,且作用域查找不能从外向内,因此报错`b is not defined`
let const 和var的区别
- let,const 声明的变量不存在声明提升(会形成暂时性死区),var存在(var可提升并初始化为 undefined,let和const提升但不初始化)
js
console.log(a); // undefined (var提升)先进行var a变量的声明,后再进行赋值,所以在执行前,就已经声明了变量a,只是没有赋值,所以输出undefind
var a = 1;
//暂时性死区
console.log(b); // ReferenceError 报错
let b = 2;
//暂时性死区
console.log(c); // ReferenceError 报错
const c = 3;
- let,const 声明的变量不能重复声明
js
var d = 4;
var d = 5; // 允许
let e = 6;
let e = 7; // 语法错误:Identifier 'e' has already been declared
const f = 8;
const f = 9; // 不允许
- var 声明的全局变量会挂载到window上,let和const不会
js
var j = 16;
console.log(window.j); // 16 (浏览器环境,非node.js环境)
let k = 17;
console.log(window.k); // undefined
const l = 18;
console.log(window.l); // undefined
- consyt 声明的变量 值不能修改,let可以
js
var g = 10;
g = 11; // 允许
let h = 12;
h = 13; // 允许
const i = 14;
i = 15; // 不允许
- const的特殊特性:基本类型值不能修改,但引用类型,引用地址不能修改,但内容可以修改,且必须初始化
js
// 基本类型:值不可变
const PI = 3.14;
PI = 3.14159; // TypeError
// 引用类型:引用不可变,内容可变
const person = { name: 'Alice' };
person.name = 'Bob'; // 允许修改对象属性
console.log(person); // { name: 'Bob' }
// 尝试重新赋值整个对象会报错
person = { name: 'Charlie' }; // TypeError: Assignment to constant variable
// 必须初始化
const NAME; //SyntaxError: Missing initializer
默认使用 const
当变量不需要重新赋值时
减少意外修改的风险
需要重新赋值时使用 let
扩展----欺骗词法
- with
with的作用是什么?
with的主要作用就是将对象中的值进行批量修改,但同时存在一个风险
在对对象属性进行修改时,如果对象中没有该属性,会导致该属性泄露到了全局
js
var o1 = { a: 1 };
var o2 = { b: 2 };
function foo(obj) {
with (obj) {
a = 2; // 由于o2没有a属性,会泄漏到全局作用域
}
}
foo(o2);
console.log(a); // 输出2(全局变量被隐式创建) console.log(o1); // 输出{a: 1}(o1未被修改)
- eval ---将本不属于当前作用域的代码,强行添加到当前作用域中执行
eval的作用是什么?
eval()
是 JavaScript 中的一个全局函数,用于将字符串作为 JavaScript 代码执行
js
function foo(str){
eval(str) //eval会直接将str字符串当作var a=10的变量声明和赋值来执行
console.log(a,b);//因为eval的缘故,在函数作用域内多了a的声明和赋值
//输出结果:10,2
}
var b=2
foo('var a=10')