js预编译

  1. function fn(a, c) {
  2. console.log(a);
  3. var a = 123;
  4. console.log(a);
  5. console.log(c);
  6. function a() {}
  7. if (false) {
  8. var d = 678;
  9. }
  10. console.log(d);
  11. console.log(b);
  12. var b = function () {};
  13. console.log(b);
  14. function c() {}
  15. console.log(c);
  16. }
  17. fn(1, 2);
  18. AO: {
  19. a: undefined 1 function a(){}
  20. c: undefined 2 function c(){}
  21. d: undefined
  22. b: undefined
  23. }
  24. // [Function: a]
  25. // 123
  26. // [Function: c]
  27. // undefined
  28. // undefined
  29. // [Function: b]
  30. /// [Function: c]

作用域的创建阶段 ==>预编译的阶段
预编译的时候做了哪些事情
js的变量对象 AO对象 供js引擎自己去访问的

  1. 创建了AO对象
  2. 找行参和变量的声明,作为AO对象的属性名,值为undefined
  3. 实参和形参相统一
  4. 找函数声明,会覆盖变量的声明

this 面试题(阿里)

  1. var name = 222;
  2. var a = {
  3. name: 111,
  4. say: function () {
  5. console.log(this, this.name);
  6. },
  7. };
  8. var fun = a.say;
  9. fun(); // fun.call(window) 222
  10. a.say(); // a.say.call(a) 111
  11. var b = {
  12. name: 333,
  13. say: function (fn) {
  14. fun(); // fun.call(window)
  15. },
  16. };
  17. b.say(a.say); // 222
  18. b.say = a.say;
  19. b.say(); // b.say.call(b) 333

函数中直接使用

  1. // js
  2. function get(params) {
  3. console.log(params);
  4. }
  5. get("nihao");
  6. get.call(window, "nihao");

函数作为对象的方法被调用(谁调用我 我就指向谁)

  1. // js
  2. var person = {
  3. name: "李四",
  4. run: function (time) {
  5. console.log(`${this.name} 在喝水 最多${time}min就喝完了`);
  6. },
  7. };
  8. person.run(30);
  9. person.run.call(person, 30);

箭头函数中的this

  • 箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。
  • 箭头函数中,this指定的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

  • 箭头函数中的this是在定义函数的时候绑定

    1. // js
    2. var x = 11;
    3. var obj = {
    4. x: 22,
    5. say: () => {
    6. console.log(this.x);
    7. },
    8. };
    9. obj.say(); // 11
  • 所谓的定义时候绑定,就是this是继承自父执行上下文中的this,比如这里箭头函数的this.x,箭头函数本身与say平级以key:value的形式,也就是箭头函数本身所在的对象为obj,而obj的父执行上下文就是window,因此这里的this.x实际上表示的是window.x,因此输出是11。

    1. // js
    2. var obj = {
    3. birth: 1990,
    4. getAge: function () {
    5. var b = this.birth;
    6. var fn = () => new Date().getFullYear() - this.birth;
    7. return fn;
    8. },
    9. };
    10. obj.getAge();
  • 例子中箭头函数本身是在getAge方法中定义的,因此,getAge方法的父执行上下文是obj,因此这里的this指向则为obj对象。

    js作用域

  • 作用域的说明:一般理解指一个变量的作用范围

  1. 全局作用域
    1. 全局作用域在页面打开时被创建,页面关闭时被销毁
    2. 编写在script标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到
    3. 在全局作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用
    4. 全局作用域中声明的变量和函数会作为window对象的属性和方法保存
  2. 数据作用域
    1. 调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
    2. 每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
    3. 在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量
    4. 在函数作用域中访问变量、函数时,会先在自身作用域中寻找,若没有找到时,则会到函数的上一级作用域中寻找,一直到全局作用域
  • 作用域的深层次理解
  1. 执行期的上下文
    1. 当函数代码执行的前期,会创建一个执行期上下文的内部对象AO(作用域)
    2. 这个内部的对象是预编译的时候创建出来的,因为当函数被调用时,会进行预编译
    3. 在全局代码执行的前期会创建一个执行期的上下文的对象GO
  • 函数作用域预编译
  1. 创建ao对象 AO{ }
  2. 找形参和变量声明,将变量和形参名当做AO对象的属性名,值为undefined
  3. 实参形参相统一
  4. 在函数体里面找函数声明,值赋予函数体
  • 全局作用域的预编译
  1. 创建GO对象
  2. 找变量声明,将变量声明作为GO对象的属性名,值为undefined
  3. 找函数声明,值赋予函数体
  • 作用域链
  1. 会被保存到一个隐式的属性中去[ [ scope ] ] 这个属性是我们用户访问不到的,但是的的确确是存在的,让js引擎来访问的,里面存储的就是作用域链 AO GO AO和GO的集合

    闭包的底层原理

    ```javascript var createLogin = function () { var div = document.createElement(“div”); div.innerHTML = “我是登录的弹窗”; div.style.display = “none”; document.body.appendChild(div); return div; };

var getSingle = function (fn) { var result; return function () { return result || (result = fn.apply(this, arguments)); }; };

var create = getSingle(createLogin); document.getElementById(“loginBtn”).onclick = function () { var loginLay = create(); loginLay.style.display = “block”; }; ``` 函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会重新读条。
函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。

  • debounce
    • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
    • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
  • throttle
    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断


柯里化本质上是降低通用性,提高适用性。