东莞市住房和城乡建设局网站,sem优化案例,专科函授网页设计实训报告,怎样建设数字中国文章目录 1.什么是V81.1 扫描器Scanner1.2 解析器parser1.3 预解析PreParser1.4 解释器Ignition1.5 编译器TurboFan 1.什么是V8
V8是谷歌的开源高性能JavaScript和WebAssembly引擎#xff0c;用C编写。它被用于Chrome和Node.js等。它实现ECMAScript和WebAssembly#xff0c;… 文章目录 1.什么是V81.1 扫描器Scanner1.2 解析器parser1.3 预解析PreParser1.4 解释器Ignition1.5 编译器TurboFan 1.什么是V8
V8是谷歌的开源高性能JavaScript和WebAssembly引擎用C编写。它被用于Chrome和Node.js等。它实现ECMAScript和WebAssembly并在Windows 7或更高版本、macOS 10.12以及使用x64、IA-32、ARM或MIPS处理器的Linux系统上运行。V8可以独立运行也可以嵌入到任何C应用程序中。 扫描器Scanner 解析器Parser 预解析PreParser 解释器Ignition 编译器TurboFan 1.1 扫描器Scanner
Blink谷歌浏览器的渲染引擎基于webkit分支开发主要负责HTML DOM CSS 渲染嵌入V8引擎执行js计算样式和布局嵌入合成器绘制图形。
Blink 拿到html代码分析找到script代码交给V8引擎解析注意Blink是通过流的形式传给V8的。
通过以流的形式传输数据Blink可以逐步接收和处理来自网络的字节流并在需要时将相应的数据传递给V8引擎执行。这种流式处理方式使得浏览器可以在数据到达的同时并行处理不同的任务提高了页面的加载速度和用户体验
Scanner扫描器首先会进行词法分析
V8_INLINE Token::Value Scanner::ScanSingleToken() {Token::Value token;do {next().location.beg_pos source_pos();if (V8_LIKELY(static_castunsigned(c0_) kMaxAscii)) {token one_char_tokens[c0_];switch (token) {case Token::LPAREN:case Token::RPAREN:case Token::LBRACE:case Token::RBRACE:case Token::LBRACK:case Token::RBRACK:case Token::COLON:case Token::SEMICOLON:case Token::COMMA:case Token::BIT_NOT:case Token::ILLEGAL:// One character tokens.return Select(token);case Token::CONDITIONAL:// ? ?. ?? ??Advance();if (c0_ .) {Advance();if (!IsDecimalDigit(c0_)) return Token::QUESTION_PERIOD;PushBack(.);} else if (c0_ ?) {return Select(, Token::ASSIGN_NULLISH, Token::NULLISH);}return Token::CONDITIONAL;case Token::STRING:return ScanString();case Token::LT:// !--Advance();if (c0_ ) return Select(Token::LTE);if (c0_ ) return Select(, Token::ASSIGN_SHL, Token::SHL);if (c0_ !) {token ScanHtmlComment();continue;}return Token::LT;case Token::GT:// Advance();if (c0_ ) return Select(Token::GTE);if (c0_ ) {// Advance();if (c0_ ) return Select(Token::ASSIGN_SAR);if (c0_ ) return Select(, Token::ASSIGN_SHR, Token::SHR);return Token::SAR;}return Token::GT;case Token::ASSIGN:// Advance();if (c0_ ) return Select(, Token::EQ_STRICT, Token::EQ);if (c0_ ) return Select(Token::ARROW);return Token::ASSIGN;case Token::NOT:// ! ! !Advance();if (c0_ ) return Select(, Token::NE_STRICT, Token::NE);return Token::NOT;case Token::ADD:// Advance();if (c0_ ) return Select(Token::INC);if (c0_ ) return Select(Token::ASSIGN_ADD);return Token::ADD;case Token::SUB:// - -- -- -Advance();if (c0_ -) {Advance();if (c0_ next().after_line_terminator) {// For compatibility with SpiderMonkey, we skip lines that// start with an HTML comment end --.token SkipSingleHTMLComment();continue;}return Token::DEC;}if (c0_ ) return Select(Token::ASSIGN_SUB);return Token::SUB;case Token::MUL:// * *Advance();if (c0_ *) return Select(, Token::ASSIGN_EXP, Token::EXP);if (c0_ ) return Select(Token::ASSIGN_MUL);return Token::MUL;case Token::MOD:// % %return Select(, Token::ASSIGN_MOD, Token::MOD);case Token::DIV:// / // /* /Advance();if (c0_ /) {uc32 c Peek();if (c # || c ) {Advance();Advance();token SkipSourceURLComment();continue;}token SkipSingleLineComment();continue;}if (c0_ *) {token SkipMultiLineComment();continue;}if (c0_ ) return Select(Token::ASSIGN_DIV);return Token::DIV;case Token::BIT_AND:// Advance();if (c0_ ) return Select(, Token::ASSIGN_AND, Token::AND);if (c0_ ) return Select(Token::ASSIGN_BIT_AND);return Token::BIT_AND;case Token::BIT_OR:// | || | ||Advance();if (c0_ |) return Select(, Token::ASSIGN_OR, Token::OR);if (c0_ ) return Select(Token::ASSIGN_BIT_OR);return Token::BIT_OR;case Token::BIT_XOR:// ^ ^return Select(, Token::ASSIGN_BIT_XOR, Token::BIT_XOR);case Token::PERIOD:// . NumberAdvance();if (IsDecimalDigit(c0_)) return ScanNumber(true);if (c0_ .) {if (Peek() .) {Advance();Advance();return Token::ELLIPSIS;}}return Token::PERIOD;case Token::TEMPLATE_SPAN:Advance();return ScanTemplateSpan();case Token::PRIVATE_NAME:if (source_pos() 0 Peek() !) {token SkipSingleLineComment();continue;}return ScanPrivateName();case Token::WHITESPACE:token SkipWhiteSpace();continue;case Token::NUMBER:return ScanNumber(false);case Token::IDENTIFIER:return ScanIdentifierOrKeyword();default:UNREACHABLE();}}if (IsIdentifierStart(c0_) ||(CombineSurrogatePair() IsIdentifierStart(c0_))) {return ScanIdentifierOrKeyword();}if (c0_ kEndOfInput) {return source_-has_parser_error() ? Token::ILLEGAL : Token::EOS;}token SkipWhiteSpace();// Continue scanning for tokens as long as were just skipping whitespace.} while (token Token::WHITESPACE);return token;
}首先获取当前字符c0_的值并设置token为初始值。判断c0_是否是ASCII字符如果是则根据c0_的值来确定token的类型并返回相应的Token。对于一些特殊情况如条件运算符、字符串、小于号、大于号、等号、逻辑非、加号、减号、乘号、取模、除号、按位与、按位或等根据当前字符和后续字符的组合来确定token的类型并返回相应的Token。如果c0_不是ASCII字符或者不满足以上条件则判断c0_是否是标识符的起始字符如果是则调用ScanIdentifierOrKeyword()函数来获取标识符或关键字的Token。如果c0_是HTML注释的结束符’-则调用SkipSingleHTMLComment()函数来跳过整个HTML注释。如果扫描到文件末尾则返回Token::EOS。否则如果遇到空白字符则调用SkipWhiteSpace()函数来跳过连续的空白字符并继续扫描下一个Token。返回扫描到的Token。
js代码就会变成tokens 接下来进行语法分析。
1.2 解析器parser
parser 的作用就是将 tokens 转化为 AST 抽象语法树。
Program
└── VariableDeclaration
├── Identifier (name: 2)
└── StringLiteral (value: 1)1.3 预解析PreParser
作用是在 JavaScript 代码执行之前对代码进行可选的预处理。预解析器的存在是为了提高代码的执行效率。
V8 引擎采用了延迟解析Lazy Parsing的策略它的原理是只解析当前需要的内容而把其他内容推迟到函数被调用时再进行解析。这样可以减少不必要的解析工作提高网页的运行效率。
例如在一个函数 outer 内部定义了另一个函数 inner那么 inner 函数就会进行预解析。这意味着在函数 outer 被调用之前只会对 outer 函数的内容进行解析而对于 inner 函数的解析会在 outer 函数调用到 inner 函数时才进行。
通过延迟解析的方式V8 引擎可以避免解析和编译未被执行的函数节省了不必要的时间和资源开销提高了 JavaScript 代码的执行效率。这种优化策略在大型复杂的 JavaScript 应用程序中尤为重要可以帮助提升整体性能和用户体验。
1.4 解释器Ignition
作用主要就是将AST 抽象语法树 转化成 字节码(bytecode)
跨平台执行不同的硬件架构和操作系统有不同的机器码格式。通过将代码转换为字节码可以使得同一份字节码在不同的平台上都能执行实现跨平台的能力。快速启动和解析将代码转换为字节码可以比直接生成机器码更快速地进行启动和解析。字节码通常具有更简单的格式和结构可以更快地被引擎加载和解释执行。动态优化现代的JavaScript引擎通常具有即时编译JIT功能可以将热点代码编译成高效的机器码。通过首先将代码转换为字节码引擎可以更好地进行动态优化和编译根据实际执行情况生成最优的机器码。这种方式可以在运行时根据代码的实际执行情况进行优化而不需要提前生成固定的机器码。代码安全性字节码作为中间表示形式可以提供一定的代码安全性。字节码相对于源代码或机器码来说更难以理解和修改可以提供一定程度的代码保护。
Program
└── VariableDeclaration
├── Identifier (name: 2)
└── StringLiteral (value: 1)0001: PushString 1
0002: StoreVar 2将字符串字面量 “‘1’” 推入堆栈栈帧。在这个例子中它将字符串 “‘1’” 推入堆栈。
将栈顶的值存储到变量 “2” 中。在这个例子中它将栈顶的字符串值存储到变量 “2”。
1.5 编译器TurboFan
编译器就是将字节码也可以叫中间代码 最后 转换成 机器码 能让我们的CPU识别。
CPU有不同的架构 ARM X86。
X86机器码
MOV EAX, 1 ; 将字符串 1 存储到寄存器
EAX MOV [2], EAX ; 将寄存器 EAX 的值存储到变量 2 对应的内存地址中ARM机器码。
LDR R0, 1 ; 将字符串 1 的地址加载到寄存器
R0 STR R0, [2] ; 将寄存器 R0 中的值存储到变量 2 对应的内存地址中