JS基础知识

Q:JS引擎线程的执行过程的三个阶段?

语法分析

预编译阶段

  1. js的运行环境
    1.全局环境(JS代码加载完毕后,进入代码预编译即进入全局环境)
    2.函数环境(函数调用执行时,进入该函数环境,不同的函数则函数环境不同)
    3.eval(不建议使用,会有安全,性能等问题)
    每进入一个不同的运行环境都会创建一个相应的执行上下文(Execution Context),那么在一段JS程序中一般都会创建多个执行上下文,js引擎会以栈的方式对这些执行上下文进行处理,形成函数调用栈(call stack),栈底永远是全局执行上下文(Global Execution Context),栈顶则永远是当前执行上下文。
  2. 函数调用栈

首次运行JS代码时,会创建一个全局执行上下文并Push到当前的执行栈中。每当发生函数调用,引擎都会为该函数创建一个新的函数执行上下文并Push到当前执行栈的栈顶。当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文控制权将移到当前执行栈的下一个执行上下文。

  1. 执行上下文的创建
  • 创建变量对象(Variable Object)
  • 建立作用域链(Scope Chain):变量对象(Variable Object)转为活动对象(Active Object)
    var num = 30;
    function test() {
        var a = 10;
        function innerTest() {
            var b = 20;
            return a + b
        }
        innerTest()
    }
    test()
    //innerTest的调用链
    innerTestEC = {
        //变量对象
        VO: {b: undefined}, 
        //作用域链
        scopeChain: [VO(innerTest), AO(test), AO(global)],  
        //this指向
        this: window
    }
  • 确定this的指向

执行阶段

  • 永远只有JS引擎线程在执行JS脚本程序
  • 宏任务
  • 同步任务指的是在JS引擎主线程上按顺序执行的任务,只有前一个任务执行完毕后,才能执行后一个任务,形成一个执行栈(函数调用栈)。
  • 异步任务指的是不直接进入JS引擎主线程,而是满足触发条件时,相关的线程将该异步任务推进任务队列(task queue),等待JS引擎主线程上的任务执行完毕,空闲时读取执行的任务,例如异步Ajax,DOM事件,setTimeout等
  • 事件循环
  • 主线程执行栈
  • 异步任务等待触发
  • 任务队列
  • 微任务:微任务(micro-task)的API主要有:Promise, process.nextTick
console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
// script start
// script end
// promise1
// promise2
// setTimeout

Q:变量提升的原因?

在创建阶段,函数声明存储在环境中,而变量会被设置为 undefined(在 var 的情况下)或保持未初始化(在 let 和 const 的情况下)。所以这就是为什么可以在声明之前访问 var 定义的变量(尽管是 undefined ),但如果在声明之前访问 let 和 const 定义的变量就会提示引用错误的原因。此时let 和 const处于未初始化状态不能使用,只有进入执行阶段,变量对象中的变量属性进行赋值后,变量对象(Variable Object)转为活动对象(Active Object)后,let和const才能进行访问

Q:什么是闭包?

  • 在函数内部定义新函数
  • 新函数访问外层函数的局部变量,即访问外层函数环境的活动对象属性
  • 新函数执行,创建新的函数执行上下文,外层函数即为闭包

Q:什么是解构赋值?

//对象的解构赋值
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
//字符串的解构赋值
const [a, b, c, d, e] = 'hello';

Q:yield关键字?

每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止 1、每次执行next(),都会执行一个yield返回的值 2、yield表达式本身没有返回值,或者说总是返回undefined(由next返回) 3、next()可无限调用,但既定循环完成之后总是返回undeinded

function* myYield2(x) {
  let y = 2*(yield (x+1));
  let z = yield (y+3);
  return (x+y+z)
}
var num = myYield2(3)
console.log(num.next())//{value: 4, done: false}
console.log(num.next(2))//{value: 7, done: false}
console.log(num.next(2))//{value: 9, done: false}
console.log(num.next(2))//{value: undefined, done: true}
  • 1.var num = myYield2(3) 执行了初始化,x为3
  • 2.执行【第一个next()】,相当于执行了yield (x+1) 而x为3,所以返回value为4 传参不传参都无所谓,因为传的参数只是代替上面yield的值,第一个之前没有其他的yield
  • 3.执行【第二个next(2)】,相当于let y = 2*(2); 结果y=4;yield (y+3);相当于yield (4+3) 最终结果返回7 执行【第三个next(2)】,相当于执行了let y = 2*(2);let z = 2;return (3+4+2);最终结果return也可以认为是一个yield
  • 4.【第四个next(2)】,已经没有yield了,所以返回undefined

Q:Promise函数?

Promise对象就是一个容器,里面保存着某个未来才会结束的事件结果,它有两个特点 1、对象的状态不受外界的影响 2、一旦状态改变,就不会再变,任何时候都可以得到这个结果

//Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
//promise实例生成后,可以通过then方法分别指定resolved状态和rejected状态的回调函数,其中第二个函数可以不提供
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

Q:Call&Apply方法

var person = {
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
}
var person1 = {
    firstName:"Bill",
    lastName: "Gates",
}
var person2 = {
    firstName:"Steve",
    lastName: "Jobs",
}
person.fullName.call(person1);  // 分别接受参数,将返回 "Bill Gates"
person.fullName.apply(person1);  // 接受参数数组,将返回 "Bill Gates"

Q:await和async调用?

为了解决promise调用时相互依赖导致代码难以理解问题

Q:箭头函数

x => x * x 
//等价于
function (x) {
    return x * x;
}