Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

《你不知道的javascript(上)》第一部分 作用域和闭包 #10

Open
qunzi0214 opened this issue Sep 29, 2020 · 0 comments
Open
Labels
read book 读书笔记

Comments

@qunzi0214
Copy link
Owner

qunzi0214 commented Sep 29, 2020

作用域是什么

var 关键字会导致变量提升的本质:

javascript是在运行前极短的时间内被编译的,var a = 2 实际上是两个动作:编译器会在当前作用域声明变量a(如果之前没被声明过),紧接着运行时引擎在作用域内寻找该变量,如果能找到就对它进行赋值。

LHSRHS

var a = 2 // 此时对变量a是LHS查询,即找到变量本身的容器
console.log(a) // 此时对变量a是RHS查询,即找到变量对应的值(引用)

关于左右侧查询的异常

function foo(a) {
  console.log(a + b); // ReferenceError: 对b进行RHS报错
  b = a; // 对b进行LHS会在全局创建一个b
}

foo(2);

// 严格模式下对未声明的变量进行 LHS 或 RHS 都会报错

ReferenceError 和作用域判别失败相关,而 TypeError 代表作用域判别成功了,但是对结果的操作是非法的

词法作用域

词法阶段

  1. 词法作用域查找从内向外,找到第一个匹配的标识符停止
  2. 可以通过 window.a 的方式访问那些被同名变量遮蔽的全局变量
  3. 函数的词法作用域只由函数被声明时的位置决定
  4. 词法作用域只会查找一级标识符,foo.bar.baz 只会查找到 foo ,后续会交给对象属性访问规则

欺骗词法(eval 和 with)

function foo(str, a) {
  eval(str); // 严格模式下有自己的词法作用域
  console.log(a, b);
}

var b = 2;

foo('var b = 3;', 1);
function foo(obj) {
  with(obj) {
    a = 2;
  }
}

var obj1 = { a: 1 };
foo(obj1);
console.log(obj1.a); // 2
console.log(a); // ReferenceError
var obj2 = { b: 3 };
foo(obj2);
console.log(obj2.a); // undefined
console.log(a); // 2

// with会基于传入的对象生成一个全新的词法作用域,当obj2作为作用域时,其中没有a标识符,因此进行了正常的LHS查找,最终在全局创建了一个变量a

词法分析阶段基本能够知道全部的标识符在什么位置,从而可以预测以及优化运行时对它们的查找。因此欺骗词法会导致性能问题

函数作用域、块作用域

函数声明与函数表达式

如果function是声明中的第一个词,那么就是一个函数声明,否则是一个函数表达式。它们的区别是函数声明会将函数绑定在所在作用域中,而函数表达式会将函数绑定在自身的函数中。

为什么不推荐匿名函数

  1. 在函数调用栈中没有函数名,会导致调试困难
  2. 当函数需要引用自身时,只能使用已经过期的 arguments.callee (递归或是事件监听器需要解绑)
  3. 代码失去可读性

块级作用域对垃圾回收机制的优化

function process(data) {
  // do something
}

{
  let bigData = {};
  process(bigData);
}

var btn = document.getElementById('btn');
btn.addEventListener('click', function click(e) {
  // do something
}, false);

回调函数 click 形成了一个覆盖整个作用域的闭包,javascript引擎极有可能在 process 函数执行后还保留着
bigData (?!)。如果使用块级作用域,可以告诉引擎不需要保留 bigData

作用域闭包

定义

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

闭包和模块模式运用

var foo = (function CoolModule(id) {
  function change() {
    publicAPI.identify = identify2;
  }

  function identify1() {
    console.log(id);
  }

  function identify2() {
    console.log(id.toUpperCase());
  }

  var publicAPI = {
    change: change,
    identify: identify1,
  };

  return publicAPI;
})('some string');

foo.identify(); // some string
foo.change();
foo.identify; // SOME STRING

requireJS原理

var MyModule = (function Manager() {
  var modules = {};

  function define(name, deps, impl) {
    for (var i = 0; i < deps.length; i++) {
      deps[i] = modules[deps[i]];
    }
    modules[name] = impl.apply(impl, deps);
  }

  function get(name) {
    return modules[name];
  }

  return {
    define: define,
    get: get,
  };
})();

MyModule.define('bar', [], function () {
  function hello(who) {
    return 'hello ' + who;
  }

  return {
    hello: hello,
  }
});

MyModule.define('foo', ['bar'], function (bar) {
  function awesome() {
    console.log(bar.hello('Rango').toUpperCase());
  }

  return {
    awesome: awesome,
  }
});

var foo = MyModule.get('foo');
foo.awesome(); // HELLO RANGO
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
read book 读书笔记
Projects
None yet
Development

No branches or pull requests

1 participant