作用域和原型
作用域链
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。
作用域链是当可执行代码内部访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域继续查找... 一直找到全局作用域。
垃圾回收
当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。
- 标记清理
- 标记阶段:从根对象遍历、可访问的对象被标识为可到达。
- 清除阶段:从堆内存中线性遍历,如果有对象未被标志可到达,回收该对象的内存。
- 缺点:垃圾回收可能导致大量的内存碎片
- 引用计数
- 引用计数跟踪记录每个值被引用的次数,如果没有引用指向该对象,回收该对象。
- 缺点:循环引用没法回收
闭包
闭包指有权访问另一个函数作用域中的变量的函数 — js 高程
当函数可以记住并访问所在的词法作用域时,就产生了闭包。即使函数是在当前词法作用域之外执行—你不知道的 js
我认为后者更对,因为全局作用域中的变量被引用也应当是产生了闭包
- 设计私有的方法和变量(谈防抖和节流)
- 构建命名空间,以减少全局变量的使用,从而使用闭包模块化代码,减少全局变量的污染(IIFE 立即调用函数表达式去构建模块)
缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏(垃圾回收机制:标记清理、引用计数)
原型链
在 Javascript 中,函数也是对象,因此构造函数中有个 prototype 指针指向原型对象,而原型对象也有个指针 constructor 指回构造函数。通过构造函数实例化的对象,有个 __proto__ 属性指向其构造函数的原型,也即原型对象。Javascript 中是没有类这个概念的,虽然 es 6 提供了 class 关键字,但是其原理也是通过寄生组合继承,利用原型链实现继承的效果。
继承
原型链继承、盗用构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承、类
原型链 | 盗用构造函数 | 组合 | 原型式 | 寄生式 | 寄生组合 | 类 | |
---|---|---|---|---|---|---|---|
方法 | 无法向父类的构造函数传递参数 | ||||||
属性 | 引用类型的属性会被上升为原型属性在所有实例间共享 | ||||||
类型检测 | 类型检测不是父类实例 | ||||||
备注 | 中断了原型链,子类的实例化对象拿不到父类原型上定义的方法和属性 | 存在效率问题,父类的构造函数会被调用两次 | 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
指向空对象 - 如构造函数没有返回值,或者返回值不是一个对象,那么返回该空对象,若有返回值,正常返回