# JS 中那些绕的概念

# This 指向问题

总结就是指向当前执行上下文,举例如下:

  • 全局作用域中的普通函数,匿名函数中 this 指向Window
  • 方法调用中 this 指向调用的对象,其中定时器函数是由Window调用的
  • 构造函数中 this 指向构造函数的实例
  • 事件处理函数中 this 指向触发该事件的元素
  • 箭头函数中 this 指向定义位置的的上下文

# JS 执行顺序问题

  • JS 最大的特点是单线程,所有任务都需要排队,HTML5 允许脚本建立多个线程,于是有了同步和异步
  • 同步任务 一般程序 异步任务 回调函数 普通事件 资源加载 定时器
    • 1.先执行主线程执行栈中的同步任务
    • 2.将异步任务放入消息队列
    • 3.同步任务执行完后,异步任务进入执行栈,开始执行异步任务

# JS 线程与渲染线程

V8 将 JS 线程和渲染线程放置在同一个主线程中。

比如下面这段代码,会先等待一段事件,界面背景才会编程红色

let k = 0;
document.body.style.background = "red";
console.log(document.body.style.background);
for (let i = 0; i < 10 * 10000 * 10000; i++) {
  k += 1;
}
console.log(k);
// red
// 界面仍然是白色
// 1000000000
// 界面变为红色

# JS 原型继承问题

以下内容,我将prototype称为原型对象,constructor称为构造器,__proto__称为继承指针,继承指针是一个非标准属性,不过主流浏览器都支持这个属性(包括 Node 运行时)。

  • 构造函数,实例对象、原型对象的”三角“关系
    • 构造函数通过new命令创建实例对象,但是不同的实例之间可能用的是相同的方法,因此我们需要划分一片内存空间来共享这些公用方法,提高资源利用效率,这片内存空间就是prototype原型对象,在prototype原型对象上定义的方法和属性可以被实例对象继承。
    • 为了让三者之间的结构变得清晰,我们让实例对象都有一个继承指针__proto__来指向它的构造函数的prototype,注意这个属性是非标准的。
  • 何去何从问题?JavaScript原型链世界的基本法则
    • 一切函数都是Function构造函数的实例,包括Function构造函数自己,构造器的终点就是Function构造函数
    • 一切函数的prototype原型对象都是Object构造函数的实例。所以函数的prototype原型对象继承自 Object 构造函数的prototype
    • Object构造函数的原型对象是一个空对象{},这个空对象继承自null,原型链的终点就是null

# 作用域链问题

var fai = "互联网";
var obj = {
  fai: "人工智能",
  exec: function () {
    return (function (fai) {
      return function () {
        console.log("a\t" + this.fai);
        //  exec.fai = undefined  another.fai='机器学习'
        console.log("b\t" + fai); // obj.fai = "人工智能"
      };
    })(this.fai);
  }
};
var exec = obj.exec(); // 立即执行传入 obj.fai 然后返回闭包函数
console.log("c\t" + exec.fai); // exec.fai undefined
// c       undefined
console.log("d\t" + exec()); // 执行里面的exec() 再执行log()
// a       undefined
// b       人工智能
// d       undefined
var another = {
  fai: "机器学习",
  exec
};
another.exec();
// a       机器学习
// b       人工智能

# 变量提升

什么是变量提升,早期 JS 为了提升脚本的运行性能搞出来的一个 hack。现在属于糟粕。

# 回调函数

回调函数是一个通过函数指针调用的函数,如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用器所指向的函数时,我们就说这是回调函数。