美文网首页
JavaScript 知识框架一

JavaScript 知识框架一

作者: wyc0859 | 来源:发表于2022-05-24 02:05 被阅读0次

    JS目录

    给 JavaScript 做一个顶层目录,可这样划分:文法、语义、运行时
    再细分为:


    建立知识框架

    两个目标:一是理解原理和背景,二是把不方便查阅和记忆的内容整理好
    前端知识在总体上分成基础部分和实践部分
    基础部分包含3大块: JS语言、CSS 及 HTML、浏览器的实现原理及API

    一、类型

    JS分7 种类型是:

    Undefined;Null;Boolean;String;Number;Symbol;Object
    
    Undefined与null

    Undefined 类型表示未定义,它的类型只有一个值,就是 undefined。任何变量在赋值前是 Undefined 类型、值为 undefined
    编程规范要求用 void 0 代替 undefined,因为:JS 的代码 undefined 是一个变量,而并非是一个关键字,这是 js 语言公认的设计失误之一,所以,为了避免无意中被篡改,建议用 void 0 来获取 undefined 值
    Undefined 跟 Null 有一定的表意差别,Null 表示的是:“定义了但是为空”。
    Null与 undefined 不同,null 是 JavaScript 关键字,所以在任何代码中,你都可以放心用 null 关键字来获取 null 值。

    String

    String 用于表示文本数据。String 有最大长度是 2^53 - 1。
    String 的意义并非“字符串”,而是字符串的 UTF16 编码
    字符串的操作 charAt、charCodeAt、length 等方法针对的都是 UTF16 编码。所以,字符串的最大长度,实际上是受字符串的编码长度影响的。
    JavaScript 中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串的内容,所以字符串具有值类型的特征。

    Number

    JavaScript 为了表达几个额外的语言场景(比如不让除以 0 出错,而引入了无穷大的概念)

    规定了几个例外情况:
    NaN,占用了 9007199254740990,它不是个数字
    Infinity,无穷大;
    -Infinity,负无穷大。

    Symbol

    Symbol 是 ES6 中引入的新类型,它是一切非字符串的对象 key 的集合
    在 ES6 规范中,整个对象系统被用 Symbol 重塑。
    我们创建 Symbol 的方式是使用全局的 Symbol 函数。
    例如: var mySymbol = Symbol("my symbol");

    Object

    Object 的定义是“属性的集合”。属性分为数据属性和访问器属性,二者都是 key-value 结构,key 可以是字符串或者 Symbol 类型。
    我们必须认识到 3 与 new Number(3) 是完全不同的值,它们一个是 Number 类型, 一个是对象类型。
    因为 C++ 和 Java 的成功,在这两门语言中,每个类都是一个类型,二者几乎等同,以至于很多人常常会把 JavaScript 的“类”与类型混淆。
    Number、String 和 Boolean,三个构造器是两用的,当跟 new 搭配时,它们产生对象,当直接调用时,它们表示强制类型转换。
    Symbol 函数比较特殊,直接用 new 调用它会抛出错误,但它仍然是 Symbol 对象的构造器。

    JavaScript 中的“类”仅仅是运行时对象的一个私有属性,而 JavaScript 中是无法自定义类型的。
    JS 语言设计上试图模糊对象和基本类型之间的关系,可以把对象的方法在基本类型上使用
    比如: console.log("abc".charAt(0)); //a
    其原理是,. 点运算符提供了装箱操作,它会根据基础类型构造一个临时对象,能在基础类型上调用对应对象的方法

    二、装箱、拆箱

    前文提到,全局的 Symbol 函数无法使用 new 来调用,但我们仍可以利用装箱机制来得到一个 Symbol 对象,我们可以利用一个函数的 call 方法来强迫产生装箱。

    var symbolObject = (function(){ return this; }).call(Symbol("a"));
    

    使用内置的 Object 函数,我们可以在 JS 中显式调用装箱能力

    var symbolObject = Object(Symbol("a")); 
    console.log(typeof symbolObject); //object
    

    拆箱转换
    拆箱转换,它是对象类型到基本类型的转换。
    会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。
    在 ES6 之后,还允许对象通过显式指定 Symbol.toPrimitive 来覆盖原有的行为(用这方法就不会执行valueOf和toString方法)


    三、对象属性:

    对象属性分为:数据属性、访问器属性
    数据属性:它比较接近于其它语言的属性概念,具有四个特征

    value:就是属性的值。
    writable:决定属性能否被赋值。
    enumerable:决定 for in 能否枚举该属性。
    configurable:决定该属性能否被删除或者改变特征值。
    

    在大多数情况下,我们只关心数据属性的值即可

    访问器属性:
    属性在读和写时执行,它允许使用者在写和读属性时,得到完全不同的值,可视为一种函数的语法糖

    getter:函数或 undefined,在取属性值时被调用。
    setter:函数或 undefined,在设置属性值时被调用。
    enumerable:决定 for in 能否枚举该属性。
    configurable:决定该属性能否被删除或者改变特征值。
    
    对象分类

    1、宿主对象(host Objects):由 JavaScript 宿主环境提供的对象,它们的行为完全由宿主环境决定。
    2、内置对象(Built-in Objects):由 JavaScript 语言提供的对象。

    • 2.1、固有对象(Intrinsic Objects ):由标准规定,随着 JavaScript 运行时创建而自动创建的对象实例。
    • 2.2、原生对象(Native Objects):可以由用户通过 Array、RegExp 等内置构造器或者特殊语法创建的对象。
    • 2.3、普通对象(Ordinary Objects):由{}语法、Object 构造器或者 class 关键字定义类创建的对象,它能够被原型继承。

    宿主对象,分为固有宿主的和用户可创建的宿主两种
    固有宿主如:window
    用户创建宿主如: document.createElement 就可以创建一些 DOM 对象

    固有对象,在任何 JavaScript 代码执行前就已经被创建出来了,它们通常扮演者类似基础库的角色

    三个值:Infinity、NaN、undefined
    九个函数:eval、isFinite、isNaN、parseFloat、parselnt、decodeURl、decodeURIComponent、encodeURI、encodeURlComponent
    构造器:Array、Date、RegExp、Promise、Proxy、Map、WeakMap、Set、WeakSet、Function、Boolean、String、Number、Symbol、Object、Error、EvalError....
    四个用于当作命名空间的对象:Atomics、JSON、Math、Reflect
    

    原生对象,分成以下类:

    JavaScript 用对象模拟函数的设计代替了一般编程语言中的函数,它们可以像其它语言的函数一样被调用、传参。
    任何对象只需要实现[[call]],它就是一个函数对象,可以去作为函数被调用。而如果它能实现[[construct]],它就是一个构造器对象,可以作为构造器被调用。
    用户用 function 关键字创建的函数必定同时是函数和构造器,但它们表现出来的行为效果却并不相同。


    JS 的执行

    Promise:是 JavaScript 语言提供的一种标准化的异步管理方式,它的总体思想是,需要进行 io、等待或者其它异步操作的函数,不返回真实结果,而返回一个“承诺”,函数的调用方可以在合适的时机,选择等待这个承诺兑现(通过 Promise 的 then 方法的回调)

    var r = new Promise(function(resolve, reject){ 
        console.log("a"); 
        resolve() 
    }); 
    setTimeout(()=>console.log("d"), 0)  //0秒的setTimeout
    r.then(() => console.log("c"));  
    console.log("b")
    

    打印顺序是abcd,不论代码顺序如何,d 必定发生在 c 之后,哪怕Promise返回需要3秒,也仍然是先c后d。
    因为 Promise 产生的是 JavaScript 引擎内部的微任务,而 setTimeout 是浏览器 API,它产生宏任务。

    generator/iterator 也常常被跟异步一起来讲,但它们并非异步代码,只是在缺少 async/await 的时候,一些框架使用这样的特性来模拟 async/await。但是 generator 并非被设计成实现异步,所以有了 async/await 之后,generator/iterator 就不应用来模拟异步的方法,只用于迭代器。

    通常把宿主发起的任务称为宏观任务,JS引擎发起的任务称为微观任务
    Promise 产生异步代码,JS保证这些异步代码在一个宏观任务中完成 (每个宏观任务包含1个微观任务队列)
    异步执行的顺序:
    1、首先我们分析有多少个宏任务;
    2、在每个宏任务中,分析有多少个微任务;
    3、根据调用次序,确定宏任务中的微任务执行次序;
    4、根据宏任务的触发规则和调用次序,确定宏任务的执行次序;
    5、确定整个顺序


    在只有 var,没有 let 的旧 JavaScript 时代,诞生了一个技巧,叫做:立即执行的函数表达式(IIFE),通过创建一个函数,并且立即执行,来构造一个新的域,从而控制 var 的范围。

    (function(){ 
      var a;
    }());
    或者
    (function(){ 
      var a; 
    })();
    推荐用法:
    void function(){
     var a; 
    }();
    

    有了let,就不需要用这种立即执行函数了

    执行上下文

    一旦上下文被切换,整个语句的效果可能都会发生改变。所以切换上下文的时机就显得非常重要。切换上下文最主要的场景是函数调用。

    1、看个简单的切换上下文代码:

    //a.js
    var a = 1;
    function foo(){ 
      console.log(a); 
      console.log(b);  
    }
    
    //b.js
    引入a.js
    var b = 2;
    foo();    //这里就是切换上下文
    foo打印a 为1,打印b 报错
    

    看以下的这段 JS代码:

    var b = {}
    let c = 1
    this.a = 2; 
    //1、var 把 b 声明到哪里;
    //2、b 表示哪个变量;
    //3、b 的原型是哪个对象;
    //4、let 把 c 声明到哪里;
    //5、this 指向哪个对象
    

    2、回答上面的问题,先从理解this关键字开始
    this 是执行上下文中很重要的一个组成部分。同一个函数调用方式不同,得到的 this 值也不同
    this 跟面向对象毫无关联,它是与函数调用时使用的表达式相关

    普通函数的 this 值由 ‘ 调用它所使用的引用 ’ 决定,下面示例

    function showThis(){
      console.log(this);
    }
    var o = { 
      x:2,
      showThis: showThis
    }
    showThis(); // global
    o.showThis(); // Reference 类型 o   //{ x: 2, showThis: [Function: showThis] } 
    

    注意:上面获取函数的表达式,它返回的并非函数本身,而是一个 Reference 类型
    Reference 类型由两部分组成:一个对象和一个属性值
    上面代码中 o.showThis 产生的 Reference 类型,即由对象 o 和属性“showThis”构成

    3、JavaScript 用一个栈来管理执行上下文,这个栈中的每一项又包含一个链表。如下图所示:



    当函数调用时,会入栈一个新的执行上下文,函数调用结束时,执行上下文被出栈。
    而 this 则是一个更为复杂的机制

    4、操作 this 的内置函数
    Function.prototype.call 和 Function.prototype.apply 可以指定函数调用时传入的 this 值

    function foo(a, b, c) {
      console.log(this)
      console.log(a, b, c)
    }
    foo(1, 2, 3) //this为 global
    foo.call({}, 1, 2, 3) //this为{}
    foo.apply({}, [1, 2, 3]) //this为{}
    call 和 apply 作用是一样的,只是传参方式有区别
    

    来源: winter 老师的 重学前端

    相关文章

      网友评论

          本文标题:JavaScript 知识框架一

          本文链接:https://www.haomeiwen.com/subject/xyprprtx.html