Skip to content

Latest commit

 

History

History
969 lines (712 loc) · 44.6 KB

基础知识.md

File metadata and controls

969 lines (712 loc) · 44.6 KB

基础知识

目录

  1. JavaScript
  2. 浏览器的主要组件
  3. JS引擎
  4. JS的预编译
  5. JS数据类型
  6. 基本包装类型
  7. 数据创建方式
  8. 进制数
  9. 运算符优先级
  10. 自动插入分号机制(Automatic Semicolon Insertion,ASI)
  11. ECMAScript语法提案的批准流程
  12. AJAX(Asynchronous JavaScript and XML)
  13. 浏览器发起网络请求方式
  14. 搜索引擎原理
  15. SEO
  16. 垃圾回收
  17. 数组的空位(hole)
  18. javascript伪协议
  19. JSON
  20. <script>的加载和执行时机
  21. 静态资源使用额外域名的原因
  22. 缓动函数
  23. 位运算

JavaScript

  1. 头等函数(first-class function)

    一种编程语言被称为具有头等函数时,语言中的函数将会像任何其他变量一样被对待(如:函数可以作为参数传递给其他函数、可以作为返回值被返回、可以作为值赋值给变量)。

  2. 原型编程(prototype-based programming)

    它的类(class)没有明确的定义,只是通过向其它的类中添加属性和方法来得到它,甚至偶尔使用空对象来创建类。

    JS的面向对象是基于原型的面向对象(the prototype-based OO)。

  3. 引擎、编译器、作用域

    1. 引擎

      从头到尾负责整个JS程序的编译及执行过程。

    2. 编译器

      负责语法分析及代码生成等脏活累活。

    3. 作用域

      JS是词法作用域

      负责收集并维护由所有声明的标识符(变量、函数)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

  4. JavaScript范围

    ECMAScript是JavaScript的标准,狭义的JavaScript指ECMAScript。浏览器、Node.js、Deno、Bun、等都是JavaScript的运行时环境。

    JavaScript = ECMAScript + 宿主环境提供的API。

    1. 浏览器(web应用)的JavaScript包括:

      1. ECMAScript:描述该语言的语法和基本对象。

      2. Web API:

        1. 文档对象模型(DOM):描述处理网页内容的方法和接口。
        2. 浏览器对象模型(BOM):描述与浏览器进行交互的方法和接口。
    2. Node.js的JavaScript包括:

      1. ECMAScript:描述该语言的语法和基本对象。

      2. 操作系统的API:

        1. 操作系统(OS)。
        2. 文件系统(file)。
        3. 网络系统(net)。
        4. 数据库(database)。
  5. 浏览器中的JavaScript使用限制

    为了用户的(信息)安全,在浏览器中的JavaScript的能力是受限的。目的是防止恶意网页获取用户私人信息或损害用户数据。

    • JavaScript最初被创建的目的是“使网页更生动”,它不提供对内存或CPU的底层访问,不需要这些功能。此类限制的例子包括:

      1. 网页中的JavaScript不能读、写、复制和执行硬盘上的任意文件。它没有直接访问操作系统的功能。

        • 现代浏览器允许JavaScript做一些文件相关的操作,但是这个操作是受到限制的。仅当用户做出特定的行为,JavaScript才能操作这个文件。如:用户把文件“拖放”到浏览器中,或者通过<input>标签选择了文件。
        • 有很多与相机/麦克风和其它设备进行交互的方式,但是这些都需要获得用户的明确许可。因此,启用了JavaScript的网页应该不会偷偷地启动网络摄像头观察你,并把你的信息发送到网络上。
      2. 不同的标签页/窗口之间通常互不了解。

        有时候,也会有一些联系,例如一个标签页通过JavaScript打开的另外一个标签页。但即使在这种情况下,如果两个标签页打开的不是同一个网站(域名、协议或者端口任一不相同的网站),它们都不能相互通信(同源策略)。这个限制也是为了用户的信息安全。

      3. JavaScript可以轻松地通过互联网与当前页面所在的服务器进行通信。但是从其他网站/域的服务器中接收数据的能力被削弱了。尽管可以,但是需要来自远程服务器的明确协议(CORS,在HTTP header中)。这也是为了用户的信息安全。

    JavaScript的能力很大程度上取决于它运行的环境。如果在浏览器环境外(如:在服务器上)使用JavaScript,则不存在此类限制。

  6. 是什么使得JavaScript与众不同?
    • 至少有3件事值得一提:

      1. 与HTML/CSS完全集成。
      2. 简单的事,简单地完成。
      3. 被所有的主流浏览器支持,并且默认开启。

    JavaScript是将这三件事结合在一起的唯一的浏览器技术。这就是为什么JavaScript与众不同。这也是为什么它是用于创建浏览器界面的使用最广泛的工具。

浏览器的主要组件

浏览器主要组件图

  1. 用户界面

    包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面之外,其他显示的各个部分都属于用户界面。

  2. 浏览器引擎

    在用户界面和呈现引擎之间传送指令。

  3. 渲染引擎

    也称为浏览器内核(web browser engine)、排版引擎(layout engine)或样板引擎,是一种软件组件,负责获取标记式内容(如:HTML、XML以及图像文件等)、整理信息(如CSS、XSL),并将排版后的内容输出至显示屏或打印机。

    所有网页浏览器、电子邮件客户端以及其他需要根据表示性的标记语言来显示内容的应用程序,都需要排版引擎。

    各浏览器使用的浏览器内核
    1. IE:Trident。
    2. Chrome:前WebKit,现Blink。
    3. Firefox:Gecko。
    4. Safari:WebKit。
    5. Opera:前Presto,现Blink。
    6. Edge:EdgeHTML。
  4. 网络

    用于网络调用,如:HTTP请求。其接口与平台无关,并为所有平台提供底层实现。

  5. 用户界面后端

    用于绘制基本的窗口小部件,如:组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。

  6. JS引擎(JS解释器、JS Engine、JS虚拟机)

  7. 数据存储

    这是持久层。浏览器需要在硬盘上保存各种数据,如:cookie。HTML5定义了「网络数据库」,这是一个完整(但是轻便)的浏览器内数据库。

JS引擎

  1. JS引擎提供了执行JavaScript的运行时环境。JS引擎都实现了Standard ECMA-262(ECMAScript:JavaScript使用的标准)。
  2. React Native、Node.js、Deno、Bun等都是通过扩展JS Engine,在具备强大的本地资源和原生接口调用能力的同时,结合JavaScript丰富的库和社区和及其稳定的跨平台能力进行开发。

一个专门处理JS脚本的虚拟机。

JS引擎使用情况
  1. V8:Chrome,Opera,Node.js,MongoDB,Hippy(Android)。
  2. JavaScriptCore(JSC、JSCore)、Nitro:React Native 0.7-,Hippy(iOS),Safari。
  3. Hermes:React Native 0.7+。
  4. SpiderMonkey:Firefox。
  5. Chakra:Edge,ie9+。
  6. JScript:ie8-,ASP。
  • 引擎工作流程(不同JS引擎的工作原理基本都大同小异):

    1. (Parsing:)首先对输入的JS代码进行词法分析(Lexical Analysis),将代码分解为一个个的词法单元(tokens),然后进行语法分析(syntactic analysis),根据语法规则将词法单元组织成抽象语法树(Abstract Syntax Tree,AST)的形式;
    2. 解释执行(Interpretation)或即时编译(Just-In-Time Compilation,JIT);
    3. 在执行JS代码时,引擎会进行一系列的优化操作,以提高代码的执行效率。引擎还会对执行过程进行监控和分析,根据代码的热度和执行频率进行动态优化,以提高性能。
    • JS引擎使用垃圾回收(Garbage Collection)算法来自动回收不再使用的内存对象,以避免内存泄漏和资源浪费。

对于前端来说,除了各引擎内部逻辑之外,在调试JS代码时连接的debugger协议也应不同JS引擎而不同。如:Chrome DevTools Protocol,CDPWebkit Inspector Protocol

JS的预编译

参考:JavaScript - 预编译

  1. JS是一门脚本语言,不经过编译而直接运行,但运行前先进行预编译。

  2. JS的预编译是以代码块<script></script>为范围,即每遇到一个代码块都会进行:预编译 -> 执行

  3. 预编译:在内存中开辟一块空间,用来存放变量函数,进行变量、函数的提升(hoisting)

    为使用var声明的变量、使用function声明的函数在内存中开辟一块空间,用来存放两者声明(不会赋值,所有变量的值都是undefined、函数内容会被预编译);

    const/let不允许同名声明。

    • 在预编译时,function的优先级比var高:

      1. 在预编译阶段,同时声明同一名称的函数和变量(顺序不限),会被声明为函数。
      2. 在执行阶段,若变量有赋值,则这个名称会重新赋值给变量。
      e.g.
      // 预编译阶段a1/b2为函数,运行时a1/b2赋值成为变量
      console.log(a1);        // => ƒ a1() {}
      var a1 = 1;
      function a1() {}
      console.log(a1);        // => 1
      
      console.log(b2);        // => ƒ b2() {}
      function b2() {}
      var b2 = 1;
      console.log(b2);        // => 1
      
      
      // 预编译阶段c3/d4为函数,运行时没有赋值
      console.log(c3);        // => ƒ c3() {}
      var c3;
      function c3() {}
      console.log(c3);        // => ƒ c3() {}
      
      console.log(d4);        // => ƒ c4() {}
      function d4() {}
      var d4;
      console.log(d4);        // => ƒ c4() {}
      
      
      // 预编译阶段e5为变量,运行时被赋值给匿名函数
      console.log(e5);        // => undefined
      var e5 = function () {};
      console.log(e5);        // => ƒ () {}(匿名函数)
  4. 变量赋值是在JS执行阶段(运行时)进行的。

JS数据类型

  1. 基本数据类型:

    UndefinedNullBooleanNumberStringSymbolBigInt

    1. 存放在栈内存(Stack),按值访问

      基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说,更加容易管理内存空间。

      基本数据类型的赋值图

    2. 不能添加新属性;基本包装类型的属性只能读取、不能修改

  2. 引用数据类型:

    Object(由Object原型链继承的其他所有引用数据类型,包括基本包装类型

    1. 存放在堆内存(Heap),按指针访问

      引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JS不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用。

      引用数据类型的赋值图

    2. 所有引用数据类型的实例,都可以添加新属性;已有属性的读取/修改,根据属性描述(数据属性/访问器属性)决定。

  • 判断是基本数据类型(不是引用数据类型):

    function isPrimitive(value) {
      return value === null || (typeof value !== "object" && typeof value !== "function")
      // 或(不跨帧):return !(value instanceof Object)    // 或(不跨帧):return !Object.prototype.isPrototypeOf.call(Object.prototype, value)
      // 或:return (
      //   typeof value === "number" ||
      //   typeof value === "string" ||
      //   typeof value === "boolean" ||
      //   typeof value === "symbol" ||
      //   typeof value === "bigint" ||
      //   value === null ||
      //   value === undefined
      // );
    }

基本包装类型

  1. 针对基本数据类型

    BooleanNumberStringSymbolBigInt(除了 UndefinedNull

  2. 作用

    使JS的对象涵盖所有的值(除了 UndefinedNull),允许基本数据类型转换为基本包装类型后方便地调用Object对象提供的方法(如:valueOftoString)。

    1. 基本数据类型与基本包装类型的自动转换

      'abc'.length // 3
      // abc是一个字符串,本身不是对象,不能调用length属性;
      // JS引擎自动将其转为基本包装类型,在这个对象上调用length属性;
      // 调用结束后,这个临时对象就会被销毁。
    2. 自动转换生成的基本包装类型是只读的,无法修改

      var s = '' // s:基本数据类型
      s.x = 1    // 自动转换后进行赋值操作,然后销毁这个基本包装类型的实例,
      s.x        // undefined。这时的自动转换生成的基本包装类型的实例,和上面那个已经销毁的实例不是同一个
  • 在非严格模式,若是基本数据类型(除了undefined/null之外)使用this替代设置this,则this成为那个值的基本包装类型。

    e.g.
    function A () {
     console.log(this)
    }
    
    A.call()          // => 全局变量
    A.call(undefined) // => 全局变量
    A.call(null)      // => 全局变量
    A.call(1)         // => Number的基本包装类型
    A.apply(1)        // => Number的基本包装类型
    A.bind(1)()       // => Number的基本包装类型
    
    
    function B () {
     'use strict'
     console.log(this)
    }
    
    B.call()          // => undefined
    B.call(undefined) // => undefined
    B.call(null)      // => null
    B.call(1)         // => 1
    B.apply(1)        // => 1
    B.bind(1)()       // => 1
e.g.
  • 基本包装类型种类

    // Boolean
    new Boolean(true)
    // 或
    Object(true) // 或:`new Object(true)`、`Object(Boolean基本包装类型)`、`new Object(Boolean基本包装类型)`
    
    
    // Number
    new Number(1)
    // 或
    Object(1) // 或:`new Object(1)`、`Object(Number基本包装类型)`、`new Object(Number基本包装类型)`
    
    
    // String
    new String('')
    // 或
    Object('') // 或:`new Object('')`、`Object(String基本包装类型)`、`new Object(String基本包装类型)`
    
    
    // Symbol
    Object(Symbol()) // 或:`new Object(Symbol())`
    
    
    // BigInt
    Object(BigInt(1)) // 或:`new Object(BigInt(1))`、`Object(1n)`、`new Object(1n)`
  • 判断数据类型

    // 以 Number 为例:
    
    new Number(1) == 1  // true
    new Number(1) === 1 // false
    
    Object.prototype.toString.call(1)             // "[object Number]"
    Object.prototype.toString.call(new Number(1)) // "[object Number]"
    
    typeof 1             // 'number'
    typeof new Number(1) // 'object'
    
    1 instanceof Number             // false
    new Number(1) instanceof Number // true

数据创建方式

  1. 引用数据类型

    1. ObjectArrayFunctionRegExp的字面量和构造函数(或普通函数)创建的结果一致,都是引用数据类型。

    2. 其他引用数据类型仅能够通过构造函数(或普通函数)方式创建,没有字面量创建方式

    3. 创建方式区别:

      1. 字面量:

        加载JS脚本后就编译(compilation),更好的性能。

      2. 构造函数(或普通函数):

        运行JS脚本时编译(runtime compilation),仅在需要传参数情况下才使用此方式。

    1. 对象

      e.g.

      // ①对象字面量
      var obj1 = {a: 'b'}
      
      
      // ②构造函数实例化
      
      // 基本包装类型:若参数是null或undefined,将会创建并返回一个空对象;否则,将返回一个与给定值对应类型的对象(基本包装类型)
      new Object()            // {}
      new Object(null)        // {}
      new Object(undefined)   // {}
      new Object(1)           // Number的基本包装类型,等价于:new Number(1)
      new Object(true)        // Boolean的基本包装类型,等价于:new Boolean(true)
      new Object('str')       // String的基本包装类型,等价于:new String('str')
      new Object(Symbol())    // Symbol的基本包装类型
      new Object(1n)          // BigInt的基本包装类型
      
      // 引用类型等于脱去new Object创建的对象
      new Object({a: 'b'})    // {a: 'b'}
      new Object(new Date())  // new Date()
      
      
      // ③普通函数(与new的实例化方式返回结果一致)
      Object()
    2. 数组

      e.g.

      // ①数组字面量
      var arr1 = [2, 3];          // [2, 3]
      
      
      // ②构造函数实例化
      var arr2 = new Array();     // []
      var arr3 = new Array(2);    // [undefined, undefined](空位)
      var arr4 = new Array(2, 3); // [2, 3]
      
      
      // ③普通函数(与new的方式结果一致)
      var arr5 = Array();         // []
      var arr6 = Array(2);        // [undefined, undefined](空位)
      var arr7 = Array(2, 3);     // [2, 3]
  2. 基本数据类型

    1. 有字面量方式和构造函数(或普通函数)方式:

      BooleanNumberString

    2. 有字面方法和普通函数方式:

      BigInt

    3. 有普通函数方式:

      Symbol

    4. 仅有字面量方式:

      UndefinedNull

    1. 字符串

      e.g.

      // ①字符串字面量
      var str1 = 'string';                // 'string'
      
      
      // ②普通函数(与字面量的方式结果一致)
      var str2 = String('string');        // 'string'
      
      
      // ③构造函数实例化
      var str3 = new String('string');    // 基本包装类型,等价于:new Object('string')
      
      
      console.log(typeof str1, str1 instanceof String);   // => string false
      console.log(typeof str2, str2 instanceof String);   // => string false
      console.log(typeof str3, str3 instanceof String);   // => object true
      
      console.log(str1 === str2, str2 === str3);          // => true false

进制数

  1. 2进制表示:

    0b二进制数0B二进制数

  2. 8进制表示:

    0o八进制数0O八进制数

    0八进制数(不推荐)

  3. 16进制表示:

    0x十六进制数0X十六进制数

JS内部会自动将8进制、16进制、2进制转为10进制。

不同进制数互相转换、用Number(字符串)将带进制数前缀的字符串转为10进制数。

运算符优先级

决定了表达式中运算执行的先后顺序。优先级高的运算符会作为优先级低的运算符的操作数。

  1. 优先级 顺序执行;

  2. 若优先级相同,则按结合性顺序执行(左结合:从左到右求值;右结合:从右到左求值);

  3. 无论优先级如何都不会执行短路求值后不需要执行的部分。

    短路求值(Short-circuit evaluation、minimal evaluation、McCarthy evaluation、最小化求值),是一种逻辑运算符的求值策略:只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值。例如,当AND的第一个运算数的值为false时,其结果必定为false;当OR的第一个运算数为true时,最后结果必定为true,在这种情况下,就不需要知道第二个运算数的具体值。在一些语言中(如Lisp),默认的逻辑运算符就是短路运算符,而在另一些语言中(如Java,Ada),短路和非短路的运算符都存在。

    e.g. 在表达式a && (b++)中,若a为假时(falsy),则即使(b++)在圆括号中,也不会被求值、不会被执行到。

自动插入分号机制(Automatic Semicolon Insertion,ASI)

  1. ASI机制不是说在解析过程中解析器自动把分号添加到代码中,而是说解析器除了分号还会以换行为基础按一定的规则作为断句(EOC)的依据,从而保证解析的正确性。

  2. 解析器会尽量将新行并入当前行,当且仅当符合ASI规则时才会将新行视为独立的语句:

    1. ;空语句
    2. var语句
    3. 表达式语句(一定会产生一个值)
    4. do-while语句(不是while
    5. continue语句
    6. break语句
    7. return语句
    8. throw语句

前置分号策略:只要判断行首字符为:[ ( + - / `之一,就在其前面增加分号。

ECMAScript语法提案的批准流程

一种新的语法从提案到变成正式标准,需要经历五个阶段:

  1. Stage 0 - Strawman(展示阶段)
  2. Stage 1 - Proposal(征求意见阶段)
  3. Stage 2 - Draft(草案阶段)
  4. Stage 3 - Candidate(候选人阶段)
  5. Stage 4 - Finished(定案阶段)

一个提案只要能进入Stage 2,就差不多会包括在以后的正式标准(Standard)里面。

AJAX(Asynchronous JavaScript and XML)

综合了多项技术的浏览器端网页开发技术,能在不刷新整个页面的前提下维护数据。

  1. 运用HTML或XHTML + CSS来表达信息;
  2. 运用JavaScript操作DOM来运行动态效果;
  3. 运用XML和XSLT操作数据;
  4. 运用XMLHttpRequestFetch与网页服务器进行异步数据交换。

浏览器发起网络请求方式

  1. XMLHttpRequest

    1. 同步、异步
    2. CORS解决跨域问题(跨域时,经过一些设置可以携带cookie)
    3. 携带cookie(同源 或 设置允许跨域的cookie)
  2. fetch

    1. 仅异步
    2. CORS解决跨域问题(跨域时,经过一些设置可以携带cookie)
    3. 携带cookie(同源 或 设置允许跨域的cookie)
    4. 返回Promise实例
  3. navigator.sendBeacon

    产生原因

    这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向Web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在unload事件处理器中产生的异步XMLHttpRequest。

    • 过去,为了解决这个问题, 统计和诊断代码通常要在:

      1. 发起一个同步XMLHttpRequest来发送数据。
      2. 创建一个<img>元素并设置src,大部分用户代理会延迟卸载(unload)文档以加载图像。
      3. 创建一个几秒的no-op循环。

    上述的所有方法都会迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。下一个页面对于这种较差的载入表现无能为力。

    这就是sendBeacon()方法存在的意义。使用sendBeacon()方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能,这意味着:数据发送是可靠的、数据异步传输、不影响下一导航的载入。

    1. 仅异步

    2. 仅POST

    3. unload状态下也可以异步发送,不阻塞页面刷新/跳转等操作

    4. 跨域问题

      1. 若请求头的content-typeapplication/x-www-form-urlencodedmultipart/form-datatext/plain,则没有跨域问题
      2. 否则需要CORS解决跨域问题
    5. 携带cookie(同源 或 设置允许跨域的cookie)

  • 资源引用(如:<img>

    1. 仅GET
    2. 没有跨域问题(除非被JS操作)
    3. 携带cookie(同源 或 设置允许跨域的cookie)

搜索引擎原理

  1. 在互联网中发现、搜集、下载网页信息

    • 搜索引擎蜘蛛

      1. 判断:

        只能用userAgent判断。各大厂的蜘蛛有严格的行为规范和固定的userAgent。

      2. 大部分蜘蛛:不会去执行任何JS内容、完全忽略Flash。

        Googlebot可以获取IntersectionObserver API滚动加载的内容(无法scroll其他交互)。

      3. 抓取互联网上的网页:顺着当前网页上的超链接到达另一个网页,不断进行。

  2. 对信息进行提取和组织建立索引库

    1. 提取网站信息后对主要内容进行分词
    2. 对分词进行倒排索引(inverted index):从关键词匹配到文档、文档中位置、次数等信息的索引库。
  3. 检索器根据用户输入的查询内容,在索引库检出文档,对将要输出的结果进行排序,最后将查询结果返回用户

    1. 对用户输入的查询内容进行分词。
    2. 对每个分词查找索引库,合并查找结果。
    3. 排序规则。

SEO

SEO(search engine optimization,搜索引擎优化):利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。

  1. 站内SEO

    1. 优化、语义化HTML结构

    2. 添加信息:

      1. <title>标题</title>

      2. <meta name="keywords" content="关键字"><meta name="description" content="描述">

        关键字密度和关联度。

      3. <a>添加href属性,用JS事件绑定去拦截浏览器默认行为。

      4. <img>添加alt<a>添加title

      5. 每个页面有且仅有一个<h1>

    3. SEO相关内容:进行服务端渲染、不要在客户端异步加载并渲染

      用户推荐相关的,不要做SEO,因此不要把千人千面的推荐内容提交给蜘蛛(若要对推荐内容做SSR,可以单独用userAgent判断蜘蛛而特殊对待)。

    4. lazyload的SEO(lazyload大部分时候和SEO相违背)

      1. 可以在lazyload的图片后面添加<noscript><img src="真实地址"></noscript>来针对蜘蛛。

      2. 对于判断是蜘蛛的请求,不用lazyload,直接展示原始内容(展示想给蜘蛛看的内容)。

      3. 大部分蜘蛛:不会去执行任何JS内容、完全忽略Flash。

        Googlebot可以获取IntersectionObserver API滚动加载的内容(无法scroll其他交互)。

  2. 站外SEO

    1. 添加各具体搜索引擎提供的SEO配置,使用各搜索引擎后台监控SEO效果

      主动推送网站各页面链接,主动上传sitemap文件,加入各种搜索引擎的认证。

    2. 站点根目录放置robots.txt

      robots.txt可以设置:允许/拦截某种机器人、禁止爬取目录、sitemap路径、Crawl-delay指令(爬虫间隔时间,秒)、等。

    3. 生成合理的站点地图(sitemap

      .xml、.html、.txt

      <?xml version="1.0" encoding="UTF-8"?>
      <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
         <url>
            <loc>http://www.example.com/</loc>
            <lastmod>2005-01-01</lastmod>
            <changefreq>weekly</changefreq>
            <priority>1.0</priority>
         </url>
         <url>
            <loc>http://www.example.com/about</loc>
            <lastmod>2005-01-01</lastmod>
            <changefreq>monthly</changefreq>
            <priority>0.8</priority>
         </url>
      </urlset>
    4. 外部链接的导入、导出,影响网页级别(PageRank)

      • 对不想宣传的链接,在<a>中添加rel="nofollow"
    5. <link rel="canonical" href="网址">整合重复的网址

      搜索引擎认为不同url是不同页面(如:baidu.combaidu.com?q=1是不同页面),因此需要避免重复内容导致搜索引擎重复处理(降低抓取效果)。

    6. URL相关:

      1. 域名选择,URL选择。

      2. URL在站点的结构深度。

        保持较浅的域名深度有助于提高网站的可访问性和索引效率,从而为SEO提供更好的基础。

      3. 域名注册时间,URL的年龄。

      4. 新内容增加的规律性。

      5. 内容的独特性。

垃圾回收

垃圾回收器会按照固定的时间间隔(或代码执行中预定的时间)周期性地执行,找出不再继续使用的变量,然后释放其占用的内存。

垃圾回收器必须跟踪并判断变量是否有用,对于不再有用的变量打上标记,以备将来回收。

  1. 标记清除(mark-and-sweep)(现代浏览器使用方式)

    垃圾回收器在运行时给存储在内存中的所有变量加上标记;然后,去掉环境中的变量以及被环境中变量引用的变量的标记;最后,对那些带标记的值进行释放。

  2. 引用计数(reference counting)

    跟踪记录每个值被引用的次数,被引用一次加1,引用取消就减1,当引用次数为0时,则说明没有办法再访问这个值了,当垃圾回收器下次运行时,释放引用次数为0的值所占空间。

    可能产生一个严重的问题:循环引用,引用次数永远不会是0。

  • 利于垃圾回收的编码方式:

    1. 变量 = null等方法,让变量成为零引用,从而进行清除元素、垃圾回收(导致内存泄露的情况除外)。
    2. 块级作用域中的变量,当块级作用域执行结束时,就可以进行垃圾回收。因此为变量显式声明块级作用域并把变量绑定在其中,有利于垃圾回收、代码性能。

数组的空位(hole)

来自:阮一峰:数组的空位阮一峰:数组的空位(ES6)

  1. 数组的空位:数组的某一个位置没有任何值

    1. 空位是可以读取的,返回undefined
    2. 空位不是undefined,空位没有任何值。一个位置的值等于undefined,依然有值。
    3. 使用delete删除一个数组成员,会形成空位,并且不会影响length属性。
    4. 给一个数组的length属性赋予大于其长度的值,新创建的项都是空位。
    5. 给一个数组新增一个下标大于其length + 1的项,会在中间产生空位。
    6. 空位所在的下标不存在数组内(in)。
    e.g.
    ;[, , ][0]                           // undefined
    0 in [undefined, undefined]          // true
    0 in [, undefined]                   // false
    0 in Array.apply(null, new Array(2)) // true(密集数组:没有空位的数组)
    0 in new Array(2)                    // false(稀疏数组:有空位的数组)
    1. new Array(数量)(或Array(数量))返回的是有空位的稀疏数组。
    2. Array.apply(null, new Array(数量));返回的是没有空位的密集数组。
    3. 若数组最后一个元素后面有逗号,并不会产生空位,而是忽略这个逗号:[1, ].length === 1
  2. ES5和ES6对处理空位的区别:

    1. ES5大多数情况下会忽略空位:

      1. forEachfiltereverysomereducereduceRight等遍历方法的回调函数会跳过空位;map的回调函数会跳过空位,但返回值保留空位。
      2. jointoString空位undefinednull处理成空字符串''
    2. ES6明确将空位转为undefined

      意味着用ES6语法遍历带空位的数组,也会对是空位的项进行回调函数执行,空位的项的值为undefined

javascript伪协议

伪协议(自定义协议):操作系统提供支持的、为关联应用程序而使用的、在标准协议(httphttpsftp等)之外的,一种协议(mailtotelfiledata自定义URL Scheme等)。

  1. 由JS引擎运行,若最后一个执行结果(;分割执行语句)是String类型,则返回给当前页面替换原页面内容(允许任何HTML标签)
  2. 所有直接修改URL的地方都可使用,如:<a><iframe><img>src属性,window.location.hrefwindow.open
  3. 为了JS与HTML解耦合,尽量不要使用其进行JS逻辑

JSON

  • 从结构上看,所有的数据(data)最终都可以分解成三种类型:

    1. 标量(scalar)

      单独的字符串(String)或数字(Number)或其他值(BooleanNull等)。

    2. 序列(sequence)

      又称为:数组(array)、列表(list)。

      若干个相关的数据按照一定顺序排序在一起。

    3. 映射(mapping)

      又称为:散列(hash)、字典(dictionary)

      键-值。

  1. JSON(JavaScript Object Notation)是一种数据交换格式

    1. 定义明确简单

      易于人阅读和编写、也易于机器解析和生成。

    2. 采用完全独立于语言的文本格式

      能够跨编程语言传输数据。

      因为传递的是文本格式,所以无法传递byte类型数据(二进制文件,如:图片)。

  2. JSON的数据类型

    来自:www.json.org

    作者-道格拉斯·克罗克福特(Douglas Crockford)-设计的JSON实际上是JavaScript的一个子集,并且声称JSON的规格永远不必升级,因为该规定的都规定了。

    1. Object

      JSON的对象

      注意:键名必须使用双引号";不能增加额外的逗号,

    2. Array

      JSON的数组

      注意:不能增加额外的逗号,

    3. String

      JSON的字符串

      注意:字符串必须使用双引号"

    4. Number

      JSON的数字

      注意:必须是十进制;浮点数不能省略小数点前的0(错误:.1;正确:0.1)。

    5. truefalsenull

    不支持JS的其他基本数据类型:UndefinedSymbolBigInt

    • 以上数据类型在键值的任意组合、嵌套

      JSON的值

    • 间隔区域可添加任意数量的空白

    • 字符集必须是Unicode的UTF-8

  3. JS使用JSON

  4. JSON的发展

    1. JSON Schema
    2. MessagePack
    3. JSON5
  • JSON与XML的对比
    1. XML(Extensible Markup Language,可扩展标记语言)

      在JSON出现之前,都是XML来传递数据。

      因为XML是一种纯文本格式,所以它适合在网络上交换数据。XML本身不算复杂,但是,加上DTD、XSD、XPath、XSLT等一大堆复杂的规范以后……即使你努力钻研几个月,也未必搞得清楚XML的规范。

    2. JSON出现

      2002年,道格拉斯·克罗克福特(Douglas Crockford)发明了JSON这种超轻量级的数据交换格式。

      由于JSON非常简单,很快就风靡Web世界,并且成为ECMA标准。几乎所有编程语言都有解析JSON的库,而在JavaScript中,我们可以直接使用JSON,因为JavaScript内置了JSON的解析。

    • 现在

      1. JSON和XML都是数据交换语言,完全独立于任何程序语言的文本格式。
      2. JSON的存在是典型的20%功能解决80%需求。为什么不要XML?因为里面80%的功能你不需要,等你需要时你就明白,这事只能XML干,JSON不行。
  • JSON流问题的解决:

    1. ndjson

<script>的加载和执行时机

  1. 没有deferasync

    立即加载并顺序执行(同步),阻塞解析HTML。

  2. defer

    异步加载,在DOM解析完成后、DOMContentLoaded触发前执行,顺序执行。

    (浏览器兼容性)多个defer脚本不一定按照顺序执行,也不一定会在DOMContentLoaded事件触发前执行,因此最好只包含一个延迟脚本。

  3. async

    异步加载,加载完马上执行,不影响DOMContentLoaded触发时机。(乱序执行,仅适用于不考虑依赖、不操作DOM的脚本)

    动态创建的<script>默认是async(可以手动设置dom.async = false,这样就是defer效果——按添加顺序加载,尽管此时dom.defer === false)。

  4. 模块化属性:

    1. type="module":与defer相同。
    2. type="module" async:与async相同。

JS脚本加载图

判断JS、CSS文件是否加载完毕
  1. JS

    1. 监听文件的load事件,触发则加载完成。
    2. 监听JS文件的readystatechange事件(大部分浏览器只有document能够触发),当文件的readyState值为loaded/complete则JS加载完成。
  2. CSS

    1. 监听文件的load事件,触发则加载完成。
    2. 轮询CSS文件的cssRules属性是否存在(必须用document.styleSheets才能返回CSSStyleSheet实例,不是DOM),当存在则CSS加载完成。
    3. 写一个特殊样式,轮询判断这个样式是否出现,来判断CSS加载完成。

静态资源使用额外域名的原因

  1. cookie free

    cookie是同源(且同路径),不同域名可以避免某些静态资源携带不必要的cookie而占用带宽

  2. 浏览器对同一域名有HTTP并发数限制

    1. 客户端:PC端口号数量有限(65536个)、线程切换开销大。
    2. 服务端:服务器的负载、并发接收限制。
  3. 动静分离,静态资源方便做CDN

    将网站静态资源(HTML、JS、CSS、图片、字体、多媒体资源等)与后台应用(API)分开部署。

    1. 缺点:

      1. 需要处理跨域请求
      2. 开发量大。
    2. 优点

      1. cookie free和HTTP并发限制的需要。
      2. API更加便利、易维护。
      3. 前后端分离开发。
      4. 减轻API服务端压力。

缓动函数

缓动函数,描述某数值随时间变化的速率:y=f(x) // 输入x:0~1的时间;输出y:0~1的进度。贝塞尔曲线(Bézier curve):输入参数,输出期望的缓动函数。

  1. CSS

    MDN:easing-function

    1. animationanimation-timing-function
    2. transitiontransition-timing-function
    3. 借助postcss-easing-gradients实现background的属性值linear-gradient按缓动函数渐变的背景色
  2. JS

    1. Canvas 2D API

      1. ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

        绘制三次贝塞尔曲线路径的方法。

      2. ctx.quadraticCurveTo(cpx, cpy, x, y)

        绘制二次贝塞尔曲线路径的方法。

    2. JS实现三次贝塞尔曲线:bezier-easingbezierjs

缓动函数速查表

位运算

位运算直接处理每一个比特位(bit),是非常底层的运算,优势是速度快,劣势就是不直观且只支持整数运算。

  1. 符号

    位运算 用法 描述
    按位与(&) a & b 对于每一个比特位,两个操作数都为 1 时,结果为 1,否则为 0
    按位或(|) a | b 对于每一个比特位,两个操作数都为 0 时,结果为 0,否则为 1
    按位异或(^) a ^ b 对于每一个比特位,两个操作数相同时,结果为 0,否则为 1
    按位非(~) ~ a 反转操作数的比特位,即 0 变成 1,1 变成 0
    左移(<<) a << b 将 a 的二进制形式向左移 b (< 32) 比特位,右边用 0 填充
    有符号右移(>>) a >> b 将 a 的二进制形式向右移 b (< 32) 比特位,丢弃被移除的位,左侧以最高位来填充
    无符号右移(>>>) a >>> b 将 a 的二进制形式向右移 b (< 32) 比特位,丢弃被移除的位,并用 0 在左侧填充
  2. 会将位运算中的左右操作数都转换为有符号32位整型,且返回结果也是有符号32位整型

    1. 当操作数不是Number类型时,会先Number(操作数)再运算(可能转为NaN或(+-)Infinity

    2. 当操作数是浮点型时首先会被转换成整型再运算

    3. 当操作数过大,超过了Int32范围([-Math.pow(2, 31), Math.pow(2, 31) - 1]),超过的部分会被截取,取低位32bit

      Before: 11100110111110100000000000000110000000000001
      After:              10100000000000000110000000000001
      
  • 基本使用举例

    为了方便比较,以下演示代码中的注释,都写成了8位二进制数(上文已经说明,事实上在JS中,位运算最终的结果都是Int32)。

    1. 枚举属性:

      通过位移的方式, 定义一些枚举常量

      const A = 1 << 0; // 0b00000001
      const B = 1 << 1; // 0b00000010
      const C = 1 << 2; // 0b00000100
    2. 位掩码:

      通过位移定义的一组枚举常量,可以利用位掩码的特性,快速操作这些枚举常量(增加、删除、比较)

      const A = 1 << 0; // 0b00000001
      const B = 1 << 1; // 0b00000010
      const C = 1 << 2; // 0b00000100
      
      // ①增加属性
      const ABC = A | B | C; // 0b00000111
      // ②删除属性
      const AB = ABC & ~C; // 0b00000011
      
      // ③属性比较
      // AB(A | B)当中包含B
      console.log((AB & B) === B); // true
      // AB(A | B)当中不包含C
      console.log((AB & C) === 0); // true
      // A和B相等
      console.log(A === B); // false