博客列表 >ES6中的箭头函数以及类的原理与实现

ES6中的箭头函数以及类的原理与实现

longlong
longlong原创
2020年09月04日 17:51:451724浏览

1. 箭头函数

1.1 箭头函数的应该场景分析

  • 给原生的回调函数/匿名函数提供了一个更加简洁的书写方式

  • 解决回调中this关键字的指向问题

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>箭头函数应用场景</title>
  7. </head>
  8. <body>
  9. <script>
  10. // 1. 简化原生回调函数的书写方式
  11. let demo1 = function (name) {
  12. return name;
  13. };
  14. console.log(demo1("jack")); // jack
  15. let demo2 = (name) => name;
  16. console.log(demo2("tom")); // tom
  17. // 用于遍历
  18. let arr = ["a", "b", "c", "d"];
  19. let ul = document.createElement("ul");
  20. let str = "";
  21. arr.forEach((item) => {
  22. str += `<li>我是${item}</li>`;
  23. });
  24. ul.innerHTML = str;
  25. document.body.appendChild(ul);
  26. // 2. 解决回调中this指向的问题
  27. let name = "outName";
  28. const p1 = {
  29. name: "p1的name",
  30. getName: function () {
  31. // 使用原生的回调方式 访问到的是全局name
  32. setTimeout(function () {
  33. console.log(this.name);
  34. }, 500);
  35. },
  36. };
  37. p1.getName(); // outName
  38. const p2 = {
  39. name: "p2的name",
  40. getName: function () {
  41. // 使用箭头函数,访问到的是当前对象的name
  42. setTimeout(() => {
  43. console.log(this.name);
  44. }, 500);
  45. },
  46. };
  47. p2.getName(); // p2的name
  48. // 通过上面示例理解如下两个问题:
  49. // 1. 普通回调函数中的this是指向全局的
  50. // 2. 箭头函数中实际上是没有this的,而箭头函数支持词法作用域(块级作用域),所以箭头函数中的
  51. // this指向 是根据它的作用域而判断到底是指向当前对象还是全局的
  52. // 3. 千万不能理解成箭头函数中的 this 就一定是指向当前对象,如下:
  53. const p3 = {
  54. name: "p3的name",
  55. getName: () => {
  56. console.log(this.name);
  57. },
  58. };
  59. p3.getName(); // outName
  60. const p4 = {
  61. name: "p4的name",
  62. getName: function () {
  63. console.log(this.name);
  64. },
  65. };
  66. p4.getName(); // p4的name
  67. // 从上面可以看出,p3的getName()方法使用箭头函数方式书写,但是其中的this指向了全局
  68. // 箭头函数理解这块困了我很久,最终我总结如下:
  69. // 如果箭头函数中存在this,那么这个this会和定义这个箭头函数的this绑定,简单的说,就是箭头函数的父函数的this是谁,那么箭头函数的this就是谁
  70. const p5 = {
  71. name: "p5的name",
  72. getName: () => {
  73. console.log(this); // Window
  74. setTimeout(() => {
  75. console.log(this.name);
  76. }, 500);
  77. },
  78. };
  79. p5.getName(); // outName
  80. // 1. setTimeout中的this会和getName中的this绑定,而getName中的this是Window,所以最终这个出现的this是全局
  81. const p6 = {
  82. name: "p6的name",
  83. getName: function () {
  84. console.log(this); // 当前对象
  85. setTimeout(() => {
  86. console.log(this.name);
  87. }, 500);
  88. },
  89. };
  90. p6.getName(); // p6的name
  91. // 2. 此时,getName的this和当前对象绑定了,所以setTimeout箭头函数中的this也绑定到了当前对象
  92. // 3. 最后总结:其实这个箭头函数也并不是哪里都适合用
  93. // 当我们不需要使用this对象的时候,可以放心大胆的用箭头函数来简化语法
  94. // 当我们需要this对象的时候,你得明确知道你需要的this是全局还是当前对象,然后再酌情使用,就像p5和p6的例子中,
  95. // 如果你需要当前对象,那么在父函数getName()中,你就不能使用箭头函数的语法来书写getName()
  96. </script>
  97. </body>
  98. </html>

1.2 箭头函数示例

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>箭头函数示例</title>
  7. <style>
  8. p {
  9. background-color: black;
  10. color: white;
  11. line-height: 50px;
  12. width: 500px;
  13. text-align: center;
  14. }
  15. </style>
  16. </head>
  17. <body>
  18. <form action="" method="post">
  19. <label for="idNumber">请输入用户id号:</label>
  20. <input type="text" name="idNumber" id="idNumber" />
  21. <button>查询用户信息</button>
  22. </form>
  23. <p></p>
  24. <script>
  25. // 示例:根据id获取对应的用户信息
  26. // 模拟用户数据
  27. const users = [
  28. ["6001", "jack", 20, "male"],
  29. ["8002", "alice", 21, "female"],
  30. ["9003", "lisa", 22, "secret"],
  31. ];
  32. // 获取 输入框,查询按钮,以及存放用于信息的P标签
  33. let idNumber = document.querySelector("#idNumber");
  34. let btn = document.querySelector("button");
  35. let p = document.querySelector("p");
  36. btn.addEventListener(
  37. "click",
  38. (ev) => {
  39. // 禁用默认行为
  40. ev.preventDefault();
  41. // console.log(idNumber.value);
  42. show(idNumber.value);
  43. // 执行完 显示用户信息 这个函数以后,清空输入框并再次获得输入焦点
  44. idNumber.value = "";
  45. idNumber.focus();
  46. },
  47. false
  48. );
  49. // 显示用户信息的函数
  50. function show(idNum) {
  51. // 获取所有用户信息的id号
  52. let ids = [];
  53. users.forEach((user) => {
  54. ids.push(user[0]);
  55. });
  56. // console.log(ids);
  57. // 下面一定要用循环,千万不能用foreach,因为遍历不能中途停止循环,而这里要用到break,continue
  58. // 1. 找到对应的用户信息时,要break,终止循环
  59. // 2. 如果没找到对应的用户信息,就continue,继续执行
  60. for (let i = 0; i < ids.length; i++) {
  61. if (ids[i] == idNum) {
  62. users.forEach((user) => {
  63. if (user[0] == idNum) {
  64. // 解构当前找到的这个用户信息这个数组
  65. let [id, name, age, sex] = user;
  66. p.innerHTML = `用户姓名:${name},用户年龄:${age},用户性别:${sex}`;
  67. }
  68. });
  69. break;
  70. } else {
  71. p.innerHTML = "信息不存在";
  72. continue;
  73. }
  74. }
  75. }
  76. </script>
  77. </body>
  78. </html>

2. 类的原理与实现

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>ES6中的类的原理与实现</title>
  7. </head>
  8. <body>
  9. <script>
  10. // 先看看ES6中的类
  11. class A {
  12. // 构造方法
  13. constructor(name) {
  14. this.name = name;
  15. }
  16. // 原型方法
  17. getName() {
  18. return this.name;
  19. }
  20. }
  21. let obj = new A("xixi");
  22. console.log(obj.getName()); // xixi
  23. // 下面了解一下类的原理与实现
  24. // 在原生js中是没有类的概念的,都是通过“构造函数”来模拟类;
  25. console.dir(function () {});
  26. console.log({});
  27. // 通过上面两行代码,在控制器中看到:
  28. // 1. 一个函数有2个属性:prototype(原型属性) __proto__(原型链属性)
  29. // 2. 一个对象有1个属性:__proto__
  30. // 下面使用构造函数来模拟 类的实现
  31. // 1. 声明一个构造函数
  32. function Demo(name) {
  33. // 属性
  34. this.name = name;
  35. // 方法
  36. this.getName = function () {
  37. return this.name;
  38. };
  39. }
  40. // 2. 实例化类
  41. let a = new Demo("jack");
  42. console.log(a.getName()); // jack
  43. console.log(a); // 在控制台中可以看到,a这个对象从Demo类中继承到了name属性和getName()方法
  44. // 但是为了防止代码的冗余,像一些公共方法通常都会写到它的原型属性上,这样的话,生成的对象也能自动继承方法,如下:
  45. function Demo1(name) {
  46. this.name = name;
  47. }
  48. // 公共方法
  49. Demo1.prototype.getName = function () {
  50. return this.name;
  51. };
  52. let b = new Demo1("alice");
  53. console.log(b.getName()); // alice
  54. console.log(b); // 此时,getName()方法就跑到对象b的__proto__属性中去了,而且以后实例化生成的对象,它的__proto__属性中都会继承这个方法
  55. // 因此,通过函数的原型属性prototype,将它的指针对应一个方法的时候,new出来的对象,也都能继承这个方法,并保存在__proto__原型属性链中
  56. // 基于这一点,构造函数便模拟了类的实现(继承父类中的方法)
  57. // 总结:函数的两大属性
  58. // 1. prototype:当该函数做为构造函数使用时(new 函数名),并且使用此属性声明的方法能对应上新生成的对象的__proto__属性,才有意义
  59. // 2. __proto__:此属性指向对象的公共方法
  60. // 分析总结:
  61. // 1. 函数中为什么也有__proto__属性:因为函数也是属于对象
  62. // 2. 为什么使用prototype声明方法后,生成的对象就在__proto__属性中继承了方法?
  63. // a.在控制台中打开函数的prototype属性,可以看到在prototype属性中也有__proto__属性
  64. // b.__proto__属性:其实是原型链属性,类似生物链把,它应该是在对象的最顶层,就简单理解成父类把
  65. // c.当使用prototype声明方法时,这个方法就会保存在__proto__这个原型链属性中
  66. // d.当实例化这个构造函数生成一个新对象时,对象的__proto__属性必然会继承构造函数的__proto__属性,从而也就继承了方法
  67. </script>
  68. </body>
  69. </html>
上一条:PHP注释下一条:PHP 高级过滤器
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议