ES6入门学习日记

106 阅读6分钟

资料引用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. 从栈中获取变量对应对象的引用(即它在堆内存中的地址)
  2. 拿着 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('肉骨头真好吃')
}

理解原型与原型链

图片描述

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