我的自检清单---知识体系之一 JavaScript基础

970 阅读7分钟

前段时间刷掘金发现一篇很不错的文章(清单),觉得挺有道理的。有些知识点以前看过后,过段时间后发现又忘了,然后又去查找资料,反反复复 感觉知识点永远不是自己的且学的又比较零散。所以按照 一名【合格】前端工程师的自检清单去完善自己知识体系(根据自身情况稍作调整)。

这里说明下大部分知识点详解都是引用下面链接的,都是很不错的文章,值得去研究,在此感谢各位作者大大了。 如果引用涉及作者版权之类,请私信我,会尽快删掉的。

参考

知识体系

一、JavaScript 基础

变量和类型:

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

    • 基本数据类型:Number,Undefined,Boolean,String,Null,Symbol,BigInt
    • 引用数据类型:Object
    • 基本数据类型和引用类型的区别?
  2. JavaScript对象的底层数据结构是什么?: HashMap

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

  4. JavaScript中的变量在内存中的具体存储形式

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

  6. 理解值类型和引用类型

  7. nullundefined的区别

    • undefined 表示根本不存在定义
    • null 表示一个值被定义了,定义为“空值”;
  8. 至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型 ES6 有个Array.isArray 可以很好的判断是否为数组类型。 判断 JS 数据类型的四种方法:

    1. typeof:

      • 对于基本类型,除 null 以外,均可以返回正确的结果。
      • 对于引用类型,除 function 以外,一律返回 object 类型。
      • 对于 null ,返回 object 类型。
      • 对于 function 返回 function 类型。
    2. instanceof:

      • instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型
    3. constructor:

      • null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
      • 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
    4. toString:

      • toString()Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。 此方法也有缺点,不能精确的判断自定义类型,对于自定义类型只会返回 [object Object],不过可以用instanceof代替判断。

        
        function Person () {}
        const p = new Person();
        Object.prototype.toString.call(p); // [object Object]
        p instanceof Person; // true
        
        
  9. 可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用。

  10. 出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法。

    • JavaScript可以存储最大的数字:Number.MAX_VALUE 它的值可以通过Number.MAX_VALUE得到,在控制台打印得到值:1.7976931348623157e+308。
    • 最大安全数字:Number.MAX_SAFE_INTEGER: 9007199254740991

原型及原型链

  1. 理解原型设计模式以及JavaScript中的原型规则

    • 原型规则

      • 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性; var arr = [];arr.a = 1;
      • 所有的引用类型(数组、对象、函数),都有一个_proto_属性(隐式原型),属性值是一个普通的对象;
      • 所有的函数,都具有一个prototype(显式原型),属性值也是一个普通对象;
      • 所有的引用类型(数组、对象、函数),其隐式原型指向其构造函数的显式原型;(obj._proto_ === Object.prototype)
      • 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中去寻找;
      • JS中的原型规则与原型链
    • 深入理解JavaScript系列(42):设计模式之原型模式

  2. instanceof的底层实现原理,手动实现一个instanceof

  3. 实现继承的几种方式以及他们的优缺点

  4. 至少说出一种开源项目(如Node)中应用原型继承的案例[暂时没看过源码,看了之后再来回答]

  5. 可以描述new一个对象的详细过程,手动实现一个new操作符

  6. 理解es6 class构造以及继承的底层实现原理

    es6 引入的 class 类实质上是 JavaScript 基于原型继承的语法糖。

    
    class Animal {
        constructor(name) {
            this.name = name;
        }
    
    sayHi() {
            return `Hello ${this.name}`;
        }
    }
    
    // es5
    function Animal(name) {
        this.name = name;
    }
    
    Animal.prototype.sayHi = () => {
        return `Hello ${this.name}`;
    };
    
    

    让我们来看看通过babel转换的Animal类,是否跟上面es5类似呢?

    "use strict";
    
    /**
     * 判断left 是否是 right 的实例, 底层利用 instanceof,
     */
    function _instanceof(left, right) {
        if (
            right != null &&
            typeof Symbol !== "undefined" &&
            right[Symbol.hasInstance]
        ) {
            return !!right[Symbol.hasInstance](left);
        } else {
            return left instanceof right;
        }
    }
    
    function _classCallCheck(instance, Constructor) {
        if (!_instanceof(instance, Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }
    
    /**
     * 利用Object.defineProperty添加对象属性
    */
    function _defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) descriptor.writable = true;
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    
    /**
     * 给构造函数or构造函数原型添加属性or方法
    */
    function _createClass(Constructor, protoProps, staticProps) {
        if (protoProps) _defineProperties(Constructor.prototype, protoProps);
        if (staticProps) _defineProperties(Constructor, staticProps);
        return Constructor;
    }
    
    var Animal =
    /*#__PURE__*/
    (function() {
        // 构造函数
        function Animal(name) {
            // 检测 this(this 指向生成的实例)是否是构造函数Animal的实例
            _classCallCheck(this, Animal);
    
            this.name = name;
        }
    
        // 向构造函数添加方法
        _createClass(Animal, [
            {
                key: "sayHi",
                value: function sayHi() {
                return "Hello ".concat(this.name);
                }
            }
        ]);
    
        return Animal;
    })();
    

    从以上转义后的代码来看,底层还是采用了原型的继承方式。

作用域和闭包

  1. 理解词法作用域和动态作用域

  2. 理解JavaScript的作用域和作用域链

  3. 理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题

  4. this的原理以及几种不同使用场景的取值。

    this是什么?this就是函数运行时所在的环境对象。

  5. 闭包的实现原理和作用,可以列举几个开发中闭包的实际应用

    
    // 1. 经典闭包面试题
    for (var i = 0; i < 5; i++) {
        setTimeout(() => {
            console.log(i);
        })
    }
    
    for (var i = 0; i < 5; i++) {
        (function (j) {
            setTimeout(() => {
                console.log(j)
            })
        }(i))
    }
    
    
    // 2. 模块化方案
    const module = (function() {
        var name = 'rudy';
        var getName = function () {
            return name;
        }
    
        var changeName = function (newName) {
            name = newName;
            return name;
        }
    
        return {
            getName: getName,
            changeName: changeName
        }
    }())
    
    // 私有变量
    var privateNum = (function () {
        var num = 0;
        return function () {
            return num++;
        }
    }())
    privateNum(); // 0
    privateNum(); // 1
    privateNum(); // 2
    
    

    必要应用场景实在是太多了,以上只列举了部分场景。

  6. 理解堆栈溢出和内存泄漏的原理,如何防止

  7. 如何处理循环的异步操作;

    如果需要简单的处理下for循环的异步操作,就是让每个循环体拥有自己的作用域,可以利用es6中的let或者闭包来解决。

    
    // let
    for (let i = 0; i < 5; i++) {
        setTimeout(() => {
            console.log(i);
            // do something
        })
    }
    
    // 闭包
    for (var i = 0; i <5; i++) {
        (function (j) {
            setTimeout(() => {
                console.log(j);
                // do something
            })
        }(i))
    }
    
    
  8. 理解模块化解决的实际问题,可列举几个模块化方案并理解其中原理

执行机制

  1. 为何try里面放return,finally还会执行,理解其内部机制
  2. JavaScript如何实现异步编程,可以详细描述EventLoop机制
  3. 宏任务和微任务分别有哪些