资料引用https://www.imooc.com/read/70/article/1616
复杂作用域
变量提升只有声明会被提前
闭包应用
实现类似于java中private类型的私有变量.
柯里化和偏函数
内存管理机制
JS 的内存生命周期:
好比农民种地:挖坑—>用坑—>还坑
栈和堆
JS 中的数据类型,整体上来说只有两类:基本类型和引用类型。
基本类型包括:Sting、Number、Boolean、null、undefined、Symbol。这类型的数据最明显的特征是大小固定、体积轻量、相对简单,它们被放在 JS 的栈内存里存储。
而排除掉基本类型,剩下的数据类型就是引用类型,比如 Object、Array、Function 等等等等。这类数据比较复杂、占用空间较大、且大小不定,它们被放在 JS 的堆内存里存储。

在访问 a、b、c 三个变量时,过程非常简单:从栈中直接获取该变量的值。
而在访问 d 和 e 时,则需要分两步走:
- 从栈中获取变量对应对象的引用(即它在堆内存中的地址)
- 拿着 1 中获取到的地址,再去堆内存空间查询,才能拿到我们想要的数据
垃圾回收算法有两种 —— 引用计数法和标记清除法
引用计数法
引用计数法无法甄别循环引用场景下的 “垃圾”
标记清除法
标记清除法是现代浏览器的标准垃圾回收算法
在标记清除算法中,一个变量是否被需要的判断标准,是它是否可抵达
闭包与内存泄漏
该释放的变量(内存垃圾)没有被释放,仍然霸占着原有的内存不松手,导致内存占用不断攀高,带来性能恶化、系统崩溃等一系列问题,这种现象就叫内存泄漏。
//经典代码 "theThing"问题
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing) // 'originalThing'的引用
console.log("嘿嘿嘿");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log("哈哈哈");
}
};
};
setInterval(replaceThing, 1000);
this 基本指向原则
多数情况下,this 指向调用它所在方法的那个对象。
**说得更通俗点, 谁调的函数,this 就归谁。**当调用方法没有明确对象时,this 就指向全局对象。
this 的指向是在调用时决定的,而不是在书写时决定的。这点和闭包恰恰相反。
“即不管方法被书写在哪个位置,它的 this 只会跟着它的调用方走”
在三种特殊情境下,this 会 100% 指向 window:
- 立即执行函数(IIFE)
- setTimeout 中传入的函数
- setInterval 中传入的函数
箭头函数
所以说箭头函数中的 this,和你如何调用它无关,由你书写它的位置决定
执行上下文与调用栈
执行上下文主要分为三类:
- 全局上下文 —— 全局代码所处的环境,不在函数中的代码都在全局执行上下文中
- 函数上下文 —— 在函数调用时创建的上下文
- Eval 执行上下文 —— 运行 Eval 函数中的代码时所创建的环境,Eval 被前端诟病多年, 所以Eval 执行上下文,不在我们本文的讨论范围内。
全局上下文
当我们的 JS 脚本跑起来之后,第一个被创建的执行上下文就是全局上下文。
当我们的脚本里一行代码也没有的时候里,全局上下文里会比较干净,只有两个东西:
- 全局对象(浏览器里是 Window, Node 环境下是 Global)
- this 变量。这里的 this ,指向的还是全局变量
每一个执行上下文都会经历这样一个生命周期:
- 创建阶段 —— 执行上下文的初始化状态,此时一行代码都还没有执行,只是做了一些准备工作
- 创建全局对象(Window 有了)
- 创建 this ,并让它指向全局对象
- 给变量和函数安排内存空间
- 默认给变量赋值为 undefined;将函数声明放入内存
- 创建作用域链
- 执行阶段 —— 逐行执行脚本里的代码
函数上下文
它与全局上下文之间的不同主要体现在以下方面上:
- 创建的时机 —— 全局上下文在进入脚本之初就被创建,而函数上下文则是在函数调用时被创建
- 创建的频率 —— 全局上下文仅在代码刚开始被解释的时候创建一次;而函数上下文由脚本里函数调用的多少决定,理论上可以创建无数次
- 创建阶段的工作内容不完全相同 —— 函数上下文不会创建全局对象(Window),而是创建参数对象(arguments);创建出的 this 不再死死指向全局对象,而是取决于该函数是如何被调用的 —— 如果它被一个引用对象调用,那么 this 就指向这个对象;否则,this 的值会被设置为全局对象或者 undefined(在严格模式下)
调用栈
站在调用栈的角度,理解作用域的本质
- 作用域对外隔离
- 闭包 —— 特殊的 “弹出”
- 自由变量的查找 —— 作用域链与上下文变量的结合
var name = 'xiuyan'
function testA() {
console.log('执行第一个测试函数的逻辑');
testB();
console.log('再次执行第一个测试函数的逻辑');
}
function testB() {
console.log(name);
}
testA();
/*
注意!这里是沿着作用域链找,可不是沿着调用栈一层一层往上找哦!调用栈是在执行的过程中形成的,而作用域链可是在书写阶段就决定了。因此,testB 里找不到的变量,绝不会去 testA 里找,而是去全局上下文变量里找!
*/
原型编程范式
ES6 的类其实是原型继承的语法糖:
ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为 JavaScript 引入新的面向对象的继承模型。 ——MDN
当我们尝试用 class 去定义一个 Dog 类时:
class Dog {
constructor(name ,age) {
this.name = name
this.age = age
}
eat() {
console.log('肉骨头真好吃')
}
}
其实完全等价于写了这么一个构造函数:
function Dog(name, age) {
this.name = name
this.age = age
}
Dog.prototype.eat = function() {
console.log('肉骨头真好吃')
}
理解原型与原型链

今天先这样吧. 到点睡觉了…害 希望能找到实习