跳到主要内容

作用域和原型

作用域链

作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。

作用域链是当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域继续查找... 一直找到全局作用域。

垃圾回收

当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。

  • 标记清理
    • 标记阶段:从根对象遍历、可访问的对象被标识为可到达。
    • 清除阶段:从堆内存中线性遍历,如果有对象未被标志可到达,回收该对象的内存。
    • 缺点:垃圾回收可能导致大量的内存碎片
  • 引用计数
    • 引用计数跟踪记录每个值被引用的次数,如果没有引用指向该对象,回收该对象。
    • 缺点:循环引用没法回收

闭包

闭包指有权访问另一个函数作用域中的变量的函数 — js 高程

当函数可以记住并访问所在的词法作用域时,就产生了闭包。即使函数是在当前词法作用域之外执行—你不知道的 js

我认为后者更对,因为全局作用域中的变量被引用也应当是产生了闭包

  • 设计私有的方法和变量(谈防抖和节流)
  • 构建命名空间,以减少全局变量的使用,从而使用闭包模块化代码,减少全局变量的污染(IIFE 立即调用函数表达式去构建模块)

缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏(垃圾回收机制:标记清理、引用计数)

原型链

在 Javascript 中,函数也是对象,因此构造函数中有个 prototype 指针指向原型对象,而原型对象也有个指针 constructor 指回构造函数。通过构造函数实例化的对象,有个 __proto__ 属性指向其构造函数的原型,也即原型对象。Javascript 中是没有类这个概念的,虽然 es 6 提供了 class 关键字,但是其原理也是通过寄生组合继承,利用原型链实现继承的效果。

img

继承

原型链继承、盗用构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承、类

原型链盗用构造函数组合原型式寄生式寄生组合
方法无法向父类的构造函数传递参数
属性引用类型的属性会被上升为原型属性在所有实例间共享
类型检测类型检测不是父类实例
备注中断了原型链,子类的实例化对象拿不到父类原型上定义的方法和属性存在效率问题,父类的构造函数会被调用两次Object. Create创建增强返回

this 指向问题

this 既不指向函数自身也不指向函数的词法作用域,this 实际上是在函数调用时发生的绑定,它完全取决于函数在哪里被调用。遵循以下四个规则

  • new 调用 ?绑定到新创建的对象
  • call 或者 apply (或者 bind )调用? 绑定到指定的对象
  • 由上下文对象调用 ? 绑定到那个上下文对象
  • 默认 :在严格模式下绑定到 undefined ,否则绑定到全局对象

箭头函数的 this 的行为和上述规则无关,它指向定义时外层函数作用域中的 this。本质上,这是因为箭头函数没有自己的 this ,从而发生了作用域的查找。

class A {
constructor(fn) {
this.tag = "A";
console.log(this.tag, "constructor");
fn();
}
}
const foo = {
tag: "foo",
bar() {
return new A(() => {
console.log(this.tag, "anonymous");
});
},
};
foo.bar.call({ tag: "foobar" });
/*
* A constructor
* foobar anonymous
*/

new 关键字作用

  • 创建一个空对象
  • 绑定原型链,也就是将创建的新对象的 __proto__ 指向构造函数的原型
  • 将构造函数中的 this 指向空对象
  • 如构造函数没有返回值,或者返回值不是一个对象,那么返回该空对象,若有返回值,正常返回