一、预编译:

  作用域的创建阶段(预编译阶段)

    1.创建ao对象

    2.找到形参和变量的声明,作为ao对象的属性名,默认值是undefined

    3.将实参与形参相统一

    4.找到函数的声明,如果重名时函数的声明会覆盖变量的声明

  下面是一道阿里例题:

  function fn(a,c){

          console.log(a); // function a(){}
          var a = 123;
          console.log(a); // 123
          console.log(c); // function c(){}
          function a(){
              if(false){
                  var d = 678;
              }
          }
          console.log(d); // undefined
          console.log(b); // undefined
          var b = function(){}
          console.log(b); // function (){}
          function c(){}
          console.log(c); // function c(){}
      }
      fn(1,2);
 
 
二、let、var、concat区别
     let没有变量提升与暂时性死区
     let是声明块儿级作用域的变量,只在作用域内起作用
     let变量不能重复声明
  const声明的变量无法更改
 
 
三、JS查看数据类型的方法
     typeof
    一般返回number”、”string”、”boolean”、”object”、”function” 和 “undefined”
    对于像数组Array和时间Data则会返回Object
    //  typeof null === 'object';
    // Numbers
      typeof 37 === 'number';
      typeof 3.14 === 'number';
      typeof(42) === 'number';
      typeof Math.LN2 === 'number';
      typeof Infinity === 'number';
      typeof NaN === 'number'; // Despite being "Not-A-Number"
      typeof Number(1) === 'number'; // but never use this form!
    // Strings
      typeof "" === 'string';
      typeof "bla" === 'string';
      typeof (typeof 1) === 'string'; // typeof always returns a string
      typeof String("abc") === 'string'; // but never use this form!
    // Booleans
      typeof true === 'boolean';
      typeof false === 'boolean';
      typeof Boolean(true) === 'boolean'; // but never use this form!
    // Symbols
      typeof Symbol() === 'symbol'
      typeof Symbol('foo') === 'symbol'
      typeof Symbol.iterator === 'symbol'
    // Undefined
      typeof undefined === 'undefined';
      typeof declaredButUndefinedVariable === 'undefined';
      typeof undeclaredVariable === 'undefined'; 
    // Objects
      typeof {a:1} === 'object';
      typeof [1, 2, 4] === 'object';
      typeof new Date() === 'object';
      typeof new Boolean(true) === 'object'; 
      typeof new Number(1) === 'object'; 
      typeof new String("abc") === 'object';
    // Functions
      typeof function(){} === 'function';
      typeof class C {} === 'function';
      typeof Math.sin === 'function';
 
    typeof null都是返回‘object’的,这个是因为javascript中的值由两部分组成,一部分是表示类型的标签,另一部分是表示实际的值。对象类型的值类型标签是0,不巧的是null表示空指针,它的类型标签也被设计成0,于是就有这个typeof null === ‘object’这个‘恶魔之子’
 
 
     instanceof
    instanceof 左操作数是一个类,右操作数是标识对象的类。如果左侧的对象是右侧类的实例,则返回true.而js中对象的类是通过初始化它们的构造函数来定义的。即instanceof的右操作数应当是一个函数。所有的对象都是object的实例。如果左操作数不是对象,则返回false,如果右操作数不是函数,则抛出typeError。    
      var simpleStr = "This is a simple string";
      var myString  = new String();
      var newStr    = new String("String created with constructor");
      var myDate    = new Date();
      var myObj     = {};
      var myNonObj  = Object.create(null);

      console.log(simpleStr instanceof String); // 返回 false,虽然String.prototype在simpleStr的原型链上,但是后者是字面量,不是对象
      console.log(myString  instanceof String); // 返回 true
      console.log(newStr    instanceof String); // 返回 true
      console.log(myString  instanceof Object); // 返回 true

      console.log(myObj instanceof Object);    // 返回 true, 尽管原型没有定义
      console.log(({})  instanceof Object);    // 返回 true, 同上
      console.log(myNonObj instanceof Object); // 返回 false, 一种创建非 Object 实例的对象的方法

      console.log(myString instanceof Date); //返回 false

      console.log( myDate instanceof Date);     // 返回 true
      console.log(myDate instanceof Object);   // 返回 true
      console.log(myDate instanceof String);   // 返回 false 
 
  
     toString
     // null undefined
      console.log(Object.prototype.toString.call(null)) //[object Null] 很给力
      console.log(Object.prototype.toString.call(undefined)) //[object Undefined] 很给力

    // Number
      console.log(Object.prototype.toString.call(Infinity)) //[object Number]
      console.log(Object.prototype.toString.call(Number.MAX_SAFE_INTEGER)) //[object Number]
      console.log(Object.prototype.toString.call(NaN)) //[object Number],NaN一般是数字运算得到的结果,返回Number还算可以接受
      console.log(Object.prototype.toString.call(1)) //[object Number]
      var n = 100
      console.log(Object.prototype.toString.call(n)) //[object Number]
      console.log(Object.prototype.toString.call(0)) // [object Number]
      console.log(Object.prototype.toString.call(Number(1))) //[object Number] 很给力
      console.log(Object.prototype.toString.call(new Number(1))) //[object Number] 很给力
      console.log(Object.prototype.toString.call('1')) //[object String]
      console.log(Object.prototype.toString.call(new String('2'))) // [object String]

    // Boolean
      console.log(Object.prototype.toString.call(true)) // [object Boolean]
      console.log(Object.prototype.toString.call(new Boolean(1))) //[object Boolean]
    
    // Array
      console.log(Object.prototype.toString.call(new Array(1))) // [object Array]
      console.log(Object.prototype.toString.call([])) // [object Array]

    // Object
      console.log(Object.prototype.toString.call(new Object())) // [object Object]
      function foo() {}
      let a = new foo()
      console.log(Object.prototype.toString.call(a)) // [object Object]

    // Function
      console.log(Object.prototype.toString.call(Math.floor)) //[object Function]
      console.log(Object.prototype.toString.call(foo)) //[object Function]

    // Symbol
      console.log(Object.prototype.toString.call(Symbol('222'))) //[object Symbol]
      
    // RegExp
      console.log(Object.prototype.toString.call(/sss/)) //[object RegExp]
 
 
四、js中的栈内存和堆内存
  栈内存主要用于存储各种基本类型的变量、以及对象变量的指针
  堆内存主要负责像对象Object这种引用类型的存储
  一般来说栈内存线性有序存储,容量小,系统分配效率高。而堆内存首先要在堆内存新分配存储区域,之后又要把指针存储到栈内存中,效率相对就要低一些了
  垃圾回收方面,栈内存变量基本上用完就回收了,而推内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。
 
 
五、this指向问题
  在函数中直接使用
  一般为谁调用指向谁
  箭头函数指向父上下执行对象、
 
 
六、深浅拷贝
  赋值
    当把一个对象赋值给一个新变量,其实是把这个对象的地址赋给该变量(两个对象公用一个地址)
 
  浅拷贝
    重新在堆中创建内容存放对象,拷贝前后的基本数据类型互不影响,但是引用类型公用一块儿内存
    JS常见问题简单理解_作用域

 

 

    浅拷贝的实现方式:
      ...(ES6拓展运算符)
      数组里面的concat
      Object.assign()
      lodash clone
 
  深拷贝
    重新在堆中创建内容存放对象,拷贝前后的两个对象互不影响
    JS常见问题简单理解_作用域_02

 

 

      深拷贝实现方式:
        $.extends
        json.stringify
 
 
 
七、防抖和节流
  防抖
    持续触发函数,一定时间内,没有触发该函数时,函数执行一次
    如果设定时间到来之前又触发了事件,就重新开始延时
    JS常见问题简单理解_操作数_03

 

 

  节流
    当持续触发事件的时候,保证一段时间内,只调用一次事件处理函数(一段时间内只做一件事)
    JS常见问题简单理解_预编译_04

 

 

 

 

八、作用域

  浅:

    全局作用域

            // 1、全局作用域在页面打开时被创建、页面关闭时被销毁
            // 2、编写script标签中的变量和函数,作用域为全局,在页面任何位置都可以访问到
            // 3、在全局作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用
            // 4、全局作用域中声明的变量和函数会作为winow对象的属性和方法保存

    函数作用域

            // 1、调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
            // 2、每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
            // 3、在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域的变量
            // 4、在函数作用域中访问变量、函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域中
 
  深:
    //执行的上下文
            // 1、当函数代码执行的前期会创建一个执行期上下文的内部对象 AO(作用域)
            // 2、这个内部的对象是预编译的时候创建出来的,因为当函数被调用的时候,会先进性预编译
            // 3、在全局代码执行的前期会创建一个执行期的上下文的对象 GO
    
      //函数作用域预编译
            // 1、创建AO对象
            // 2、找形参和变量的声明,作为AO对象的属性名
            // 3、将实参和形参相统一
            // 4、找函数的声明,会覆盖变量的声明
      //全局作用域预编译
            // 1、创建GO对象
            // 2、找变量声明,将变量声明作为GO对象的属性名,默认值undefined
            // 3、找函数声明

      //作用域链
            //会被保存到一个隐式的属性中去[[scope]] 这个属性是我们用户访问不到的,但是的确存在,让js引擎来访问的,里面存储的就是作用域链(AO和GO的集合)
 
 
 
九、闭包
  函数中return出函数
  JS常见问题简单理解_栈内存_05

 

      //当a函数执行的时候,创建了ao作用域同时也声明定义了b函数
      //此时a函数能访问自己的ao作用域,b函数也能访问a的作用域
      //当a函数执行完毕的时候,a函数对a作用域的连线剪短,但b函数还是能访问a的作用域,虽然b函数被保存到外部来执行了,但是b函数定义的时候是可以访问a函数作用域的
      // let res = a();
      // res();