Javascript-进阶

事件委托

  • 事件冒泡

    当子元素事件被触发的时侯,子元素所有的父级元素同名事件会被依次触发

    子元素 -> 父元素 -> body

  • 事件委托L 给父元素注册事件,委托给子元素处理

    • 事件委托原理: 事件冒泡
    • 事件委托注意点: 不能使用thisthis 指向父元素

this指向

  • 指向问题

    1
    2
    3
    4
    5
    6
    // 普通的函数被独立调用
    function foo() {
    // foo Window
    console.log("foo", this);
    }
    foo();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var obj = {
    name: "lisi",
    foo: function () {
    // foo index.html:15 foo {name: 'lisi', foo: ƒ}
    console.log("foo", this);
    },
    };
    // obj 调用
    obj.foo();


    // 独立调用
    var baz = obj.foo;
    // foo window
    baz();

    1
    2
    3
    4
    5
    6
    7
    "use strict";
    // 严格模式下,独立调用的函数中的 this 指向的是 undefined
    // 独立调用
    var baz = obj.foo;
    // foo undefined
    baz();

  • 隐式绑定

    1
    2
    3
    4
    5
    6
    7
    8
    function bar(params) {
    // bar obj
    console.log("bar", this);
    }
    var obj = {
    foo: bar,
    };
    obj.foo();
  • new 绑定

    1
    2
    3
    4
    5
    function foo() {
    this.name = "lisi";
    console.log("foo", this);
    }
    new foo();
    • 创建新的空对象
    • this 指向这个空对象
    • 执行函数整体中的代码
    • 没有显示返回非空对象时,默认返回这个对象
  • 显示绑定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 需求: 执行函数 并且函数中的 this 指向 obj 对象
    var obj = {
    name: "lisi",
    };
    function foo() {
    this.name = "lisi";
    console.log("foo", this);
    }
    foo.call(obj); | foo.apply(obj);
    • call apply 方法
      • 第一个参数是相同的,要求传入一个对象
        • 这个对象就是给this 准备的
        • 在调用函数时,会将this 绑定到这个传入的对象上
      • 后面的参数apply 为数组,call 为参数列表

箭头函数

  • 箭头函数

    1
    2
    3
    4
    var foo = (params) => {
    console.log(params);
    };
    foo("lisi");
    • 箭头函数不会绑定this,arguments 属性
    • 箭头函数不能作为构造函数来使用(不能和 new 一起来使用,会抛出错误)
  • 练习

    1
    2
    3
    4
    var names = ["a", "b", "c"];
    names.forEach((item) => {
    console.log(item);
    });
  • 箭头函数中this 使用

    1
    2
    3
    4
    5
    // 查找规则的 this
    var bar = () => {
    console.log(this);
    };
    bar();
    全局 this
    • 验证this

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      var obj = {
      name: "obj",
      foo: function () {
      var bar = () => {
      console.log("bar: ", this);
      };
      return bar;
      },
      };
      var fn = obj.foo();
      fn.apply("bbb");
      this 查找规则

浏览器运行原理(da31)

闭包

  • 闭包的定义

    • 维基百科

      闭包: 又称调法闭包或函数闭包

      是在支持头等函数的编程语言中,实现词法绑定的一种技术

      闭包在实现上是一个结构体,他存储了一个函数和一个关联的环境(相当于一个符号查找表)

      闭包跟函数的最大区别在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即使脱离了捕捉时的上下文,它也能正常运行

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <script>
      // 自由变量
      var name = "coder";
      var age = 18;
      // 函数
      function foo() {
      console.log(name, age);
      }
      foo();
      </script>
      闭包
    • MDN Javascript 的解释

      • 一个函数和对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包
      • 闭包可以让你在一个内层函数中访问到其外层函数的作用域
      • Javascript 中,每当创建一个函数,闭包就会在函数创建的同时被创建
    • 个人理解

      • 一个普通的函数function,如果它可以访问外层作用域的自由变量,那么这个函数和周围环境就是一个闭包
      • 从广义的角度来说,Javascript 中的函数就是闭包
      • 从下一角度说: Javascript 中一个函数,如果访问了外层作用的变量,那么它是一个闭包

对象增强和函数增强

  • 函数对象的属性

    • name 获取函数名称

      1
      2
      3
      4
      5
      6
      function foo() {}
      function bar() {}
      var fnArr = [foo, bar];
      for (var fn of fnArr) {
      console.log(fn.name);
      }
    • length 参数个数

      1
      2
      3
      4
      function foo() {}
      foo(12, 34);
      // 0
      console.log(foo.length);
  • arguments

    • arguments 是一个对应于传递给函数的参数类数组(array-like) 对象

      1
      2
      3
      4
      5
      function foo(x, y) {
      // Arguments(2) [12, 34, callee: ƒ, Symbol(Symbol.iterator): ƒ]
      console.log(arguments);
      }
      foo(12, 34);
    • array-like 意味着它不是一个数组类型,而是一个对象类型

      • 但是它却拥有数组的一些特性,比如length 比如可以通过索引index 来访问
      • 但是它却没有数组的一些方法,比如filter,map
    • arguments 转换为数组

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // 方式一
      function foo() {
      var newArguments = [];
      for (var arg of arguments) {
      newArguments.push(arg);
      }
      console.log(newArguments);
      }
      foo(1, 2, 3, 4);
      1
      2
      3
      4
      5
      6
      // 方式二: slice
      function foo() {
      var newArguments = [].slice.apply(arguments);
      console.log(newArguments);
      }
      foo(1, 2, 3, 24, 6);
      1
      2
      3
      4
      5
      6
      7
      8
      // 方式三
      function foo() {
      var newArguments = Array.from(arguments);
      console.log(newArguments);
      }
      foo(1, 2, 3, 4, 6);

      var newArguments = [...arguments];
    • 箭头函数不绑定arguments

      1
      2
      3
      4
      var foo = () => {
      console.log(arguments);
      };
      foo(1, 2, 3, 24, 6);
      箭头函数
  • 函数的剩余参数(rest)

    1
    2
    3
    4
    5
    // 剩余参数必须在其他参数最后
    function foo(...args) {
    console.log(args);
    }
    foo(12, 56, 67, 90);
  • 剩余参数和arguments 的区别

    • 剩余参数只包含那些没有对应形参的实参,arguments 对象包含了传给函数的所有实参
    • arguments 对象不是一个真正的数组,rest 参数是一个真正的数组,可以进行数组的所有操作
    • arguments 是早期ECMAScript 中为了方便去获取所有的参数提供的一个数据结构,rest 参数是es6 中提供并且希望依此来替代arguments
  • 纯函数

    • 维基百科(满足如下条件)

      • 此函数在相同的输入值时,需要产生相同的输出
      • 函数的输出和输入值以外的其他隐藏信息或状态无关,也和I/O 设备产生的外部输出无关
      • 该函数不能有语义上可观察的函数副作用诸如触发事件使输出设备输出时,或更改输出值以外的物件的内容等
    • 小总结

      • 确定的输入,一定会产生确定的输出,

      • 函数在执行过程中,不能产生副作用

      • 副作用

        在计算机科学中,表示在执行一个函数时,除了返回函数值外,还对调用函数产生了附加影响,比如修改了全局变量,修改参数或者改变外部的存储

      • 案例

        • slice(纯函数 没有改变原数组)
        • splice(非纯函数 改变了原数组)
  • 函数的柯里化

    • 维基百科

      • 在计算机科学中,柯里化又翻译为卡瑞化或加里化
      • 是把接受多个参数的函数,编程一个接受一个单一参数(最初函数的第一个参数) 的函数,并且返回接受余下的参数,而且返回结果的新函数技术
      • 柯里化生成如果你固定某些参数,你将得到接受余下参数的一个函数
    • 柯里化总结

      • 传递给函数一部分参数来调用它,让它返回一个函数去处理余下的一个函数

      • 这个过程称之为柯里化

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        function foo(x, y, z) {}
        foo(1, 2, 3);

        // 转换为柯里化: foo(1)(2)(3)
        function bar(x) {
        console.log(x);

        return function foo1(y) {
        console.log(y);

        return function foo2(z) {
        console.log(z);
        };
        };
        }
        // 接受一个参数
        bar(3)(4)(5);
    • 自动柯里化函数封装

  • 组合函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    var num = 100;
    // 需求一: num * 2
    function doubleNum(num) {
    return num * 2;
    }
    // 需求二: num ** 2
    function powNum(num) {
    return num ** 2;
    }
    console.log(powNum(doubleNum(100)));
    console.log(powNum(doubleNum(200)));

    // 组合函数
    function composeFn(num) {
    return powNum(doubleNum(num));
    }
    console.log(composeFn(100));
    console.log(composeFn(200));
  • eval 函数

    • eval 是一个特殊函数,它可以将传入的字符串当作Javascript 代码来运行
    • eval 会将最后一句执行的语句的结果作为返回值
  • 严格模式(待补充day33)

Es5中继承

  • 对象的原型
    • 普通对象的原型