迷惑的知识点:
作用域
函数、闭包
面向对象
ES的新特性
其他知识
从图中可知 js、css文件并不是跟index.html文件一起下载下来的,而是需要时才会下载
浏览器内核来解析下载下来的文件
| 内存 | 使用 |
|---|---|
| Gecko | 早期被Netscape和Mozilla Firefox浏览器使用 |
| Trident | 微软开发,被IE4~IE11浏览器使用,但Edge转向使用Blink |
| Webkit | 苹果给予KHTML开发、开源的,用于Sfari,Google Chrome之前也在用 |
| Blink | Webkit的一个分支,Google开发,目前应用于Google Chrome、Edge、Opera等 |
| 。。。 | 。。。 |
浏览器内核常指浏览器的排版引擎
排版引擎(layout engine),也成为浏览器引擎(browser engine)、页面渲染引擎(rendering engine)或样板引擎
HTML Parser把HTML文件解析成DOM TreeDom进行操作从而修改Domt Tree,这执行Javascript代码就是js引擎CSS Parse解析成Style Rulescss规则Style Rules和Dom Tree结合(Attachment)到一起,生成渲染树(Render Tree)Painting)到界面上显示(Display)出来JavaScript代码通过JavaScript引擎来执行
| 引擎 | 使用 |
|---|---|
| SpiderMonkey | 第一款JavaScript引擎,有Brendan Eich开发 |
| Chakra | 微软开发,用于IE浏览器 |
| JavascriptCore | WebKit中的JavaScript引擎,由Apple公司开发 |
| V8 | Google开发的强大JavaScript引擎,也帮助Chrome从众多浏览器中脱颖而出 |
| 。。。 | 。。。 |
ECMAScript和WebAssembly,并在Windows7或更高版本,MacOS 10.12+和使用x64、IA-32、ARM或MIPS处理器的linux系统上运行
Parse解析Javascript源代码(包括词法分析和语法分析)成抽象语法树(AST)
AST可以通过V8引擎中的Ignition库转换成字节码(bytecode),不直接转换成机器码是为了根据运行环境做代码优化和环境适配等
V8引擎本身的源码非常复杂,大概有超过100w行C++代码,通过了解它的架构,我们可以知道他是如何对Javascript执行的
Parse模块会将Javascriptdiamagnetic转换成AST(抽象语法树),这是因为解释器并不直接认识JavaScript代码
Ignition是一个解释器,会将AST转换成ByteCode字节码
TurboFan优化所需要的信息(比如函数参数的类型信息,有了类型才能真实运算)Ignition会执行解释执行ByteCodeIgnition的V8官方文档https://v8.dev/blog/ignition-interpreterTurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码
Blink是Chrome内核,在解析到JavaScript时将JS代码通过流(Stream)的方式传到V8引擎Scanner(扫描器,做词法分析)Scanner将代码转换成许多Tokens,再通过Parse解析转化成AST
Parser就是直接将Tokens转成AST树结构PreParser称之为与解析
Outer()内部定义了Inner()函数,那么Inner()函数就会进行预解析在Parse阶段时,V8引擎会自动创建GlobalObject对象,比如Stirng、Date、Number、SetTimeout()、window...等都是GlobalObject的成员属性,所以在JS代码中可以字节调用这些对象
// 测试代码
var name = "why";
console.log(num1);
var num1 = 10;
var num2 = 20;
var result = num1 + num2;
foo(1)
function foo(num){
var m = 10;
var n = 20;
console.log('foo');
}
GlobalObject对象var GlobalObject = {
String : "类",
Date : "类型",
setTimeOut : "函数",
name : undefined,
num1 : undefined,
num2 : undefined,
result : undefined
foo : 0xa00
}
String、Date、setTimeOut是
GlobalObject自带的对象
name、num1、num2、result是解析出来的变量并添加到GlobalObject中
因为此时只是解析阶段,所以name、num1、num2、result都是undefined的
foo存储的函数地址
这些上下文中存在一个VO(variable object)(变量对象),其中VO分为GO和AO两种,全局上下文中VO = GO,函数执行上下文中VO = AO
代码从上下往下依次执行,从VO中取出目标对象并为其赋值
foo存储函数空间中存在一个父级作用域(parent scope),可以获得父级作用域中的数据,当foo作用域中的使用的数据在foo作用域中没找到就往父级作用域(parent scope)中查找,如果一直没有最终会找到全局作用域
var message = "Hello Global";
function foo(){
console.log(message);
}
function bar(){
message = "Hello Bar";
foo();
}
bar(); // 输出 Hello Global
GlobalObject对象foo的父级作用域(parent scope)就是GlobalObject
bar的父级作用域(parent scope)也是GlobalObject
赋值。。。
function foo(){
m = 100;
}
console.log(m);
function foo1(){
var a = b = 10;
/*
等价于
var a = 10;
b = 10;
*/
}
foo1();
console.log(a);
console.log(b);
foo函数来说,如果没有用var或者let定义变量,则m会被直接定义到全局变量(GO)中,所以对于console.log(m)会输出100而不是报错foo1函数来说,var a = b = 10;会被理解为var a = 10; b = 10;,根据1的解释,b会被定义到全局变量中,而a还在foo1的AO中,所以最后console.log(a)会报错,而console.log(b)会输出10每一个执行上下文会关联到一个环境变量(Variable Environment)中,在执行代码中变量和函数的声明会作为环境记录(Environment Record)添加到变量环境中
对于函数来说,参数也会被作为环境记录添加到变量环境中
所以对于上面的解释来说,将不再是VO(变量环境),而是环境记录(VariableEnvironment),也就是说不一定是O(object),只要是记录都行(map或者其他可以记录的类型)
不管什么语言,在代码执行的过程中都需要分配内存,不同的是某些语言需要手动管理内存,某些编程语言可以帮助管理内存
一般而言,内存管理存在如下的生命周期
JavaScript会在定义变量时为我们分配内存
因为内存是有限的,所以当内存不再需要的时候,我们需要对其进行释放,以便腾出更多的内存空间
再手动管理内存的语言中,我们需要通过一些方式来释放不再需要的内存,比如free函数:
1. 手动管理的方式会**影响编写逻辑的代码的效率**
2. 对开发者**要求较高**,不小心就会产生**内存泄漏**
JS引用使用标记清除算法,V8引擎为了更好的优化,它在算法的实现细节上也会结合一些其他的算法
函数第一公民 可以作为形式参数和返回值
副作用:在执行一个函数时,除了返回函数值以外,对调函数产生了附加的影响,比如修改了全局变量、修改参数或者改变外部的存储
副作用往往是产生BUG的温床
var names = ["avc", "cba", "eax", "fas"];
// 纯函数,确定输入确定输出,没有副作用(没有修改外部变量等,原来的数组name没有被修改)
var name2 = names.slice(0, 3);
// 非纯函数,调用之后原来的数组name被改变了
function foo1(num1, num2){ // 纯函数
return num1 + num2;
}
var name = "log";
function foo2(num1, num2) { // 非纯函数 修改了外界的值
name = "log1";
return num1 + num2;
}
function foo(m, n, x, y){
return m + n * 2 + x * 3 + y * y;
}
foo(1, 2, 3, 4);
function bar(m){
return function(n){
n = n * 2;
return function(x){
x = x * 3;
return function(y){
y = y * y;
return m + n + x + y;
}
}
}
}
bar(1)(2)(3)(4);
// 简易柯里化写法
var bar2 = m => n => x => y => m + n * 2 + x * 3 + y * y;
var bar2 = m => n => x => y => {
return m + n * 2 + x * 3 + y * y;
}
从
function foo()变function bar()的过程 称为柯里化
// 柯里化的代码复用
function MakeAddr(num){
return function Addr(count){
return count + num;
}
}
var addr = MakeAddr(5);
addr(1); // 5 + 1
addr(2); // 5 + 2
addr(3); // 5 + 3
// 普通写法
function Add(m , n){
return m + n;
}
Add(5, 1); // 5 + 1
Add(5, 2); // 5 + 2
Add(5, 3); // 5 + 3
如果需要频繁对一个数进行加减处理,使用柯里化的代码比普通写法的字母数更少(不用每次都写"5,")
// 函数的形参个数
function adddd(x, y, z){
}
console.log(adddd.length); // 输出3,即表示该函数有三个形参
// 自动柯里化函数
function hyCurring(fn){
function curried(...args){
if(args.length >= fn.length){
return fn.apply(this, args);
// return fn.call(this, ...args);
// return fn(...args);
}
else {
function curried2(...args){
return curried.apply(this, args.concat(args2));
}
}
}
return curried;
}
function double(num){
return num * 2;
}
function square(num){
return num ** 2;
}
var count = 10;
var result = square(double(count));
var count1 = 10;
var result1 = square(double(count));
var count2 = 10;
var result2 = square(double(count));
function composeFn(m, n){
return function(count){
n(m(count));
}
}
var newFn = composeFn(double, square);
count3 = 10;
result3 = newFn(count3); // 组合了 double和square的函数
function hyCompose(...fns){
val length = fns.length;
for(let i = 0; i < length; i++){
if(typeof fn[i] !== 'function'){
throw new TypeError("");
}
}
function compose(...args){
var index = 0;
var result = length ? fn[index].apply(this, args) : args;
while(index < length){
result = fns[index].call(this, result);
}
}
return compose;
}
var message = "VO : GO";
var obj = {name : "Y", message = "Obj message"};
function foo() {
function bar() {
with(obj){
console.log(message);
}
}
bar();
}
foo(); // 输出 Obj message
with() {}语句用于定义对象查找作用域
不建议使用with语句,存在兼容性问题
var jsString = 'var message = "hello world"; console.log(message);'
eval(jsString;)
通过
eval来将字符串翻译成js语句并执行
Google Chrome报错,不推荐在开发中使用eval可读性差
运行中可能被篡改
不能被js引擎优化,因为是eval去执行的不经过引擎