# 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。现在属于糟粕。
# 回调函数
回调函数是一个通过函数指针调用的函数,如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用器所指向的函数时,我们就说这是回调函数。