Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

一名【合格】前端工程师的自检清单之JavaScript基础 #287

Open
w4ctech opened this issue Jun 25, 2019 · 0 comments
Open

一名【合格】前端工程师的自检清单之JavaScript基础 #287

w4ctech opened this issue Jun 25, 2019 · 0 comments

Comments

@w4ctech
Copy link

w4ctech commented Jun 25, 2019

logo
  1. JavaScript规定了几种语言类型

    • 在JS中定义了7种数据类型?
      1. 基本类型:String、Number、Boolean、Symbol、Undefined、Null
      2. 引用类型:Object
  2. JavaScript的底层数据结构是什么

    • 先了解什么是数据结构,什么是数据类型;
    1. 数据结构:数据结构是计算机存储和组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。

    2. 数据类型:数据类型是数据结构中定义的一种性质相同的值的集合以及定义在这个值集合上的一组操作的总称。
      简单的理解就是数据结构是数据元素之间的组合,数据类型是有一些相同性质的数据元素。

      而在javasript中常见的数据结构就是栈与队列。

    3. 栈:是限定仅在表头进行插入和删除操作的线性表,就好比:一个死胡同,前面是“此路不通”,只有一个入口,如果一队人进入,只能队尾变对首出去。是一种先进后出的数据结构。

    4. 队列:同样它也是一种受限的线性表,不同的是它只允许在表的前端进行删除操作,在表的后端进行插入,就好比单行道,只能直行,不能调头。是一种先进先出的数据结构。
      除了上述的线性数据结构,还有一种常见的非线性的数据结构就是树。

    5. 树:在数据元素之间明显的存在一种层级关系,我们可以看作是“树”。通过递归来实现一定规律的树结构,是数据结构比较复杂的算法。

      堆栈、队列、树以及单向链接列表和双向链接列表都是JS底层的数据结构,也是编程语言中常用的数据结构。

  3. symbol类型在实际开发中的应用、可手动实现一个简单的 Symbol

    • Symbol是Es6引入的原始数据类型,表示一个独一无二的值。

    • symbol的应用场景:

      1. 使用Symbol来作为对象属性名(key);
      2. 使用Symbol来替代常量;
      3. 使用Symbol定义类的私有属性/方法。
    • symbol的一些特性:

      1. Symbol 值通过 Symbol 函数生成,使用 typeof,结果为 "symbol";
      2. Symbol 函数前不能使用 new 命令,否则会报错;
      3. instanceof 的结果为 false;
      4. 可以接受一个字符串作为参数,表示对 Symbol 实例的描述,参数为对象会自动转化为字符串;
      5. Symbol 值不能与其他类型的值进行运算,会报错。
      6. Symbol 值可以显式转为字符串。
      7. Symbol 值可以作为标识符,用于对象的属性名,可以保证不会出现同名的属性。
      8. Symbol 作为属性名,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、
      9. Object.getOwnPropertyNames()、JSON.stringify() 返回。但是,它也不是私有属性,有一个
      10. Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名。
    • 手动实现一个简单的symbol,相对与symbol的众多特性来说,要想完全模拟是不可能的,我们只能模拟一些特性。通过Object的一些方法来简单的实现一个属性名唯一的对象:

        (function() {
                var root = this;
      
                var generateName = (function(){
                    var postfix = 0;
                    return function(descString){
                        postfix++;
                        return 'symbol' + descString + '_' + postfix
                    }
                })()
            
                var SymbolPolyfill = function Symbol(description) {
            
                    if (this instanceof SymbolPolyfill) throw new TypeError('Symbol is not a constructor');
            
                    var descString = description === undefined ? undefined : String(description)
            
                    var symbol = Object.create({
                        toString: function() {
                            return this.__Name__;
                        }
                    })
            
                    Object.defineProperties(symbol, {
                        '__Description__': {
                            value: descString,
                            writable: false,
                            enumerable: false,
                            configurable: false
                        },
                        '__Name__': {
                            value: generateName(descString),
                            writable: false,
                            enumerable: false,
                            configurable: false
                        }
                    });
                    return symbol;
                }
                root.SymbolPolyfill = SymbolPolyfill;
            })()
      
  4. JavaScript中的变量在内存中的具体存储形式

    • 在JS中变量可以用来保存两种类型的值:基本类型和引用类型。

      1. 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中;
      2. 通过一个变量赋值到另一个变量的基本类型,只是创建了这个值的副本;
      3. 引用类型的值是对象,保存在堆内存中;
      4. 包含引用类型的值的变量,是一个指向该对象的指针;
      5. 通过一个变量赋值到另一个变量的引用类型,只是复制了指针,因此都指向该对象;
      6. 确定一个值是哪种基本类型可以使用typeof操作符;
      7. 确定一个值是哪种引用类型可以使用instanceof操作符;
      8. 无论是基本类型还是引用类型都存在一个执行环境当中,这个执行环境决定变量的生命周期;每一个新的执行环境都会有一个作用域链,用来搜索变量和函数。
      9. 这个执行环境分为全局作用域和函数作用域;
      10. 全局作用域只能访问全局作用域的定义的函数和变量,无法访问局部环境(函数作用域)中的函数和变量;而局部环境可以访问全局作用域的任何数据;

      优化内存最好的方式就是在变量所存储的数据无用的时候,将其值设为null,原值脱离执行环境,将会在下次运行时,被垃圾回收机制处理掉。

  5. 基本类型对应的内置对象,以及他们之间的装箱拆箱操作

    • 把基本数据类型转换为对应的引用类型的操作称为装箱,把引用类型转换为基本的数据类型称为拆箱。

    • 每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。

    • 装箱有两种方式:显式和隐式。

      var num = 9;
      num.toFixed(2);// "9.00"
      
      1. 隐式装箱
      var num = new Number(9);
      num.toFixed(2);// "9.00"
      num = null;    
      // 每次调用都会执行如上流程,
      
      1. 显式装箱
      var num = new Number(9);
      // num  Number{9}  
      
      • 拆箱操作可以通过一些操作符来实现,但实际调用的是toString()和valueOf()。
      var num = new Number(9);
      num+'';//'9'
      
    • 拆箱

      num.toString() 
      
      • 如果是new String(),调用valueOf()
  6. 理解值类型和引用类型

    • 值类型(stack)也就是基本数据类型,放在栈中,因其大小固定;引用类型(heap)放在堆中,因其大小不固定。
      但是对基本类型的操作(string、number)会执行装箱的操作,会转化成引用类型。
      同样对引用类型的操作也可能会执行拆箱的操作,转换为值类型。
  7. null和undefined的区别

    • undefined表示声明了变量,但未初始化,它的值为undefined。
    • null表示的是一个空对象指针,可以用typeof运算得到“object”。
    • 他们的出现的场景也不同:
      1. null是对象原型链的终点;
      2. 调用函数时,应该传入的参数没有传递,就是undefined;
      3. 函数没有返回值,默认是undefined;
      4. 对象的属性没有赋值,该属性值为undefined;
      5. 变量声明未赋值,值为undefined;
      6. undefined更多的表示“缺少的”值,而null更多的表示“空”。
  8. 至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型

    • 常用的类型检查有4种;
      1. typeof操作符:typeof是一个操作符,不是一个函数,所以后面的括号没有特定的意义,返回的结果包括:number、boolean、string、object、undefined、function等6种数据类型;
        • 优点:对基本类型的判断较为准确;
        • 缺点:无法精准的判断引用类型,null、Array判断为‘object’。
      2. instanceof操作符:instanceof是判断一个对象和函数在原型链上是否有关系;如果有,返回true,否则为false;
        • 优点:判断引用类型较为准确;
        • 缺点:无法对基本类型作出精准的判断;它限定了我们的数据类型必须是用new出来的,才能准确的判断。
      3. constructor:每个函数的定义,都会生成一个constructor;基本类型会执行隐时的执行装箱操作,创造一个构造函数的实例。
        • 优点:对基本类型和引入类型都可以判断;
        • 缺点:无法判断null和undefined,而且constructor是可以修改的,会导致检查结果的不准确。
      4. Object.prototype.toString.call():无论什么类型都可以判断,返回类型的格式为"[ object Xxx]"。
        • 缺点:Object.prototype.toString本身也可能被修改。
        • 对于数组的判断,可以通过instanceof、Object.prototype.toString.call()来判断。
  9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

    • 在JS中发生隐式转换的场景:

      1. 判断语句中:if 语句、while语句、switch语句等,会隐式转化为布尔值,来决定是否执行下面的流程。
        • 操作符引起的:布尔操作符(!、||、&&)、相等操作符(==、!=、===)等等;

      上面所说的是隐式转化为布尔值的一些场景,还有隐式转化成String和Number的。
      - +运算符可以把Number类型的转化为String;
      - -、*、/等运算符可以把String类型的转化成Number;

      1. 转化原则:布尔值的转换遵循是非原则,由返回值true和false决定;String和Number的转化是隐式转化为Object,调用Object的一些方式来实现转换。
      2. 如何避免:使用类型检查来防止转换导致的结果超出预知。
      3. 巧妙应用:布尔值的转换经常用在条件语句中,便于我们判断是否执行以下流程,防止出错,而其他的类型转换会很大程度的出现未知错误,所以少用,明确变量类型之后再用。
        var a = {
            i:0,
            valueOf:function(){
                return ++this.i;
            }
        }
        
        if( a== 1 && a==2 && a==3){
            console.log(1);
        }
        //1
        
  10. 出现小数精度丢失的原因, JavaScript可以存储的最大数字、最大安全数字, JavaScript处理大数字的方法、避免精度丢失的方法

    • 出现精度丢失的原因:在js中,采用双精度存储,占用64bit。1位用来表示符号位,11位用来表示指数,52位用来表示尾数。

      1. 这也是导致两个浮点数相加会出现问题
      0.1+0.2
      //0.30000000000000004 
      0.1+0.22
      //0.32
      
    • 因为尾数最大为52,所以在js中可以存储的最大安全数字Math.pow(2,53)-1,也就是Number.MAX_SAFE_INTEGER;

    • 可以存储的最大数字为Number.MAX_VALUE;

      Number.MAX_SAFE_INTEGER
      //9007199254740991
      Number.MAX_VALUE
      //1.7976931348623157e+308
      
    • 对于超出安全数字的操作,js提供了BigInt。

      1. BigInt是JavaScript中的一个新的原始类型,可以用任意精度表示整数。使用BigInt,即使超出JavaScript Number 的安全整数限制,也可以安全地存储和操作大整数。
      2. 要创建一个BigInt,在数字后面添加n后缀即可。
        1234567890123456789n * 123nR1234567890123456789n * 123n
        //151851850485185185047n
        
      3. 但是BigInt和Number之间不能混合操作。
      4. 对于浮点数的处理,为了避免精度丢失,通常情况下,我们会转化为整数处理,然后通过算数运算符转化为浮点数。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant