JavaScript高级——函数与对象的补充

简介: JavaScript高级——函数与对象的补充

1. 函数对象的属性

  // 定义对象并给对象添加属性和方法
  var obj = {};
  obj.name = "zgc";
  obj.bar = function () {};
  // console.log(obj);
  // 注意: 在 JS 中函数也属于对象的一种,所以可以给函数添加属性和方法
  function foo(a, b, c) {
    console.log(a, b, c);
  }
  var bar = function (m, n, ...args) {};
  function test(x, y = 0) {}
  // 1. 自定义属性
  foo.message = "Hello";
  console.log("访问foo的属性", foo.message); // Hello
  // 2. 默认函数对象中已经有两个自己的属性了
  // (1) name属性 (了解)
  console.log(foo.name, bar.name); // foo bar
  // 将多个函数放入数组中,可以以name区分
  var fns = [foo, bar];
  for (var fn of fns) {
    console.log(fn.name);
  }
  // (2) length属性, 参数个数的长度
  // 这个长度是指本来因该获取的参数的个数, 即形参的个数(因为函数在调用时参数可能多传或少传, 所以实参的个数未必等于形参的个数)
  // 剩余参数是不会算在length里面的, 赋默认值的参数也不会算在length里面
  console.log(foo.length, bar.length, test.length); // 3,2,1
  foo(1, 2); // 1 2 undefined
  foo(1, 2, 3, 4); // 1 2 3

2. 函数中arguments的使用

  // 1. arguments是一个类数组对象, 并不是真正的数组
  // 2. arguments包含所有的参数, 拥有length属性, 可以通过index获取参数
  // 3. 类数组不能够调用数组的内置方法, 如map, filter等
  function foo(m, n) {
    // console.log(m, n);
    // console.log(arguments);
    for (var i of arguments) {
      console.log(i); // 10 20 30 40
    }
    for (var i in arguments) {
      console.log(i); // 0 1 2 3
    }
  }
  foo(10, 20, 30, 40);
  // 4. 类数组转数组
  // (1) 遍历 arguments每个属性放入一个新数组
  function bar(a, b, c) {
    const newArr1 = [];
    for (var i of arguments) {
      newArr1.push(i);
    }
    console.log(newArr1); // [2, 4, 6]
  }
  bar(2, 4, 6);
  // (2) ...扩展运算符
  function bar(a, b, c) {
    const newArr1 = [...arguments];
    console.log(newArr1); // [2, 4, 8]
  }
  bar(2, 4, 8);
  // (3) Array.from(arguments)
  function bar(a, b, c) {
    const newArr1 = Array.from(arguments);
    console.log(newArr1); // [1, 3, 5]
  }
  bar(1, 3, 5);
  // (4) slice
  function bar(a, b, c) {
    // 注意, slice是实例方法, 不能通过Array之间调用
    // 能通过Array之间调用的都是类方法
    // console.log(typeof Array); // function
    const newArr1 = [].slice.apply(arguments);
    const newArr2 = Array.prototype.slice.apply(arguments);
    console.log(newArr1, newArr2); // [1, 3, 9] [1, 3, 9]
  }
  bar(1, 3, 9);
  // 4. 箭头函数没有 arguments
  const baz = () => {
    // console.log("箭头函数", arguments); 
    // arguments is not defined
  };
  baz();
  function test(a, b) {
    const baz = () => {
      console.log("箭头函数", arguments);
      // 这里的arguments是test的arguments
      // 当箭头函数本身找不到arguments时, 会去它的上层作用域下寻找
    };
    baz();
  }
  test(1, 2);

3. 函数的剩余参数

  // 剩余参数(亦称 rest 参数) 用于获取函数的多余参数,这样就不要使用 arguments 对象了
  // 如果函数的最后一个形参是...为前缀的,且搭配的变量是一个数组,该变量将多余的参数放入数组中
  // 注意1: 和参数对象不同, 剩余参数只包含那些没有对应形参的实参, 而且是真实的数组,可直接使用所有数组方法
  // 注意2: 函数剩余参数之后不能再有其他参数(即 只能是最后一个参数),否则会报错
  // 注意3: 函数的 length 属性,不包括函数剩余参数
  function add(x, y, ...values) {
    let sum = 0;
    console.log(x, y); // 2,4
    console.log(values); // [6, 8]
    for (var val of values) {
      sum += val;
    }
    console.log(sum); // 14
  }
  add(2, 4, 6, 8);
  console.log(add.length); // 2

4. 纯函数

  • 函数式编程: 通常我们对函数作为头等公民的编程方式, 称之为函数式编程
  • 函数可以赋值给变量(函数表达式写法)
  • 函数可以在变量之间来回传递
  • 函数可以作为另一个函数的参数
  • 函数作为另一个函数的返回值
  • 函数存储在另一个数据结构中
  • 函数式编程中有一个非常重要的概念叫做纯函数, JS符合函数式编程的范式 所以也有纯函数的概念
  • 纯函数: 相同的输入,总是会的到相同的输出,并且在执行过程中没有任何副作用。
  • 副作用: 指的是执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响
  • 网络请求
  • 输出数据 console.log()打印数据
  • 修改了 全局变量、参数、外部存储
  • DOM查询、操作
  • Math.random
  • 获取当前时间
  // 纯函数,符合函数在相同的输入值时,需产生相同的输出
  function sum(a, b) {
    return a + b;
  }
  // 不是一个纯函数,因为在我们程序执行的过程中,变量num很可能会发生改变
  let num = 1;
  function add(x) {
    return x + num;
  }
  add(1);
  // 不是一个纯函数
  var address = "北京";
  function printInfo(info) {
    console.log(info.name); // 有输出数据
    info.age = 18; // 对参数进行修改
    address = info.address; // 修改全局变量
  }
  var obj = {
    name: "zgc",
    address: "上海",
  };
  printInfo(obj);
  // 在JavaScript中内置的API也存在有纯函数,我们拿Array对象中的方法来说
  // filter 过滤数组中的元素,它不对原数组进行操作是一个纯函数
  var names = ["张三", "李四", "王五", "赵六"];
  var newNames1 = names.filter((n) => n !== "张三");
  console.log(newNames1); // [ '李四', '王五', '赵六' ]
  console.log(names); // [ '张三', '李四', '王五', '赵六' ]
  // splice 截取数组的时候会对原数组进行操作,所以不是一个纯函数。
  var newNames2 = names.splice(2);
  console.log(newNames2); // [ '王五', '赵六' ]
  console.log(names); // [ '张三', '李四' ]


5. 函数的柯里化(Currying)

  • 柯里化是一种关于函数的高阶技术。它不仅被用于 JavaScript,还被用于其他编程语言。
  • 柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
  • 柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。
  • 柯里化不会调用函数, 它只是对函数进行转换。
  // 普通函数
  function foo(x, y, z) {
    console.log(x + y + z);
  }
  foo(10, 20, 30); // 60
  // 柯里化函数
  function bar(x) {
    return function (y) {
      return function (z) {
        console.log(x + y + z);
      };
    };
  }
  bar(1)(2)(3); // 6
  // 柯里化箭头函数写法
  // const baz = (x) => {
  //   return (y) => {
  //     return (z) => {
  //       console.log(x + y + z);
  //     };
  //   };
  // };
  var baz = (x) => (y) => (z) => console.log(x + y + z);
  baz(1)(3)(5); // 9
  // 封装一个函数: 自动生成柯里化函数
  function currying(fn) {
    function curryFn(...args) {
      if (args.length >= fn.length) {
        // return fn(...args);
        return fn.apply(this, args);
      } else {
        return function (...newArgs) {
          // return curryFn(...args.concat(newArgs));
          return curryFn.apply(this, args.concat(newArgs));
        };
      }
    }
    return curryFn;
  }
  const curryFn1 = currying(foo);
  curryFn1(10, 20)(30); // 60
  curryFn1(10)(20)(30); // 60
  curryFn1(10).call("this", 20)(30); // 60


6. 组合函数

  组合函数是在JS开发过程中一种对函数的使用技巧, 模式
  • 函数组合是指将多个函数按顺序执行,前一个函数的返回值作为下一个函数的参数,最终返回结果。
  • 比如我们现在需要对某一个数据进行函数的调用,执行两个函数fn1和fn2,这两个函数是依次进行的;

           那么如果每次我们都需要进行两个函数的调用,操作上就会显得很重复;

           那么是否可以将这两个函数组合起来,自动依次调用呢?

           这个过程就是对函数的组合,我们称之为 组合函数(Compose Function)

  // 要求: 需要将N个数字分别进行调用double方法乘以2,再调用square方法平方。
  var count = 10;
  // 普通情况下:
  function double(num) {
    return num * 2;
  }
  function square(num) {
    return num * num;
  }
  function add(num) {
    return num + 10;
  }
  var result = square(double(count));
  console.log("普通函数", result);
  // 组合函数情况下:
  function getNum(num) {
    return square(double(count));
  }
  var result = getNum(count);
  console.log("组合函数", result);
  // 封装通用性组合函数:
  function getComposeFn(...fns) {
    // 边界判断:
    // if (fns.length <= 0) return;
    // for (var fn of fns) {
    //   if (typeof fn !== "function") throw new Error("传入的参数必须为函数");
    // }
    return function (...args) {
      console.log(args);
      var result = fns[0].apply(this, args);
      for (var i = 1; i < fns.length; i++) {
        result = fns[i].apply(this, [result]);
      }
      return result;
    };
  }
  var composeFn = getComposeFn(double, square, add);
  const res = composeFn(10);
  console.log("res", res); // 410

7. with & eval(几乎不用)

  // 1. with: 扩展一个语句的作用域链
  // 不建议使用with语句,因为它可能是混淆错误和兼容性问题的根源
  // with (expression) {
  //   statement;
  // }
  // expression: 将给定的表达式添加到在评估语句时使用的作用域链上。表达式周围的括号是必需的。
  // statement: 任何语句。要执行多个语句,请使用一个块语句 ({ ... }) 对这些语句进行分组。
  var obj = {
    userName: "zgc",
    userAge: 18,
  };
  // console.log(userName); // userName is not defined
  // console.log(userAge); //  userAge is not defined
  with (obj) {
    console.log(userName); // zgc
    console.log(userAge); // 18
  }
  // 2. eval: 允许执行一个代码字符串
  // eval是一个特殊的函数, 它可以将传入的字符串当作JS语句执行
  // 调用 eval(code) 会运行代码字符串,并返回最后一条语句的结果
  var x = 10;
  var y = 20;
  var a = eval("x * y;")
  var b = eval("2 + 2;")
  var c = eval("x + 17; console.log(1111);") // 1111
  var res = a + b;
  console.log(res, a, b, c); // 204 200 4 undefined

8. 严格模式

JavaScript 严格模式(strict mode)即在严格的条件下运行。

使用 "use strict" 指令:

  • "use strict" 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。
  • 它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。
  • "use strict" 的目的是指定代码在严格条件下执行。
  • 严格模式下你不能使用未声明的变量。


为什么使用严格模式:

  • 明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
  • 增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。
  • 提高编译器效率,增加运行速度。
  • 为未来新版本的 JavaScript 语法做好铺垫。


"严格模式"体现了Javascript更合理、更安全、更严谨的发展方向,包括IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。 另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。掌握这些内容,有助于更细致深入地理解Javascript,让你变成一个更好的程序员。


严格模式 - JavaScript 教程 - 网道

  // 1. 在js文件下给整个文件开启"严格模式":
  "use strict";
  // 2. 给某个函数开启"严格模式":
  function foo() {
    "use strict";
  }
  // 注意: 严格模式要在文件或者函数的开头使用"use strict";来开启
  // 3. 如 class/module 默认是在严格模式运行的
  // 4. 常见的严格模式限制
  // (1) 无法意外创建全局变量
  // 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。
  // v = 1; // 报错,v未声明
  // for (i = 0; i < 2; i++) {
  //   // 报错,i未声明
  // }
  // (2) 禁止this关键字指向全局对象
  // 正常模式下,函数内部的this可能会指向全局对象,严格模式禁止这种用法,避免无意间创造全局变量。
  // 正常模式
  function f() {
    console.log(this === window);
  }
  f(); // true
  // 严格模式
  function f() {
    "use strict";
    console.log(this === undefined);
  }
  f(); // true
  // (3) 对象不能有重名的属性
  // 正常模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误。
  var o = {
    p: 1,
    p: 2,
  }; // 语法错误
  // (4) 函数不能有重名的参数
  // 正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误。
  function f(a, a, b) {
    // 语法错误
  }



相关文章
|
9天前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
13天前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
13天前
|
JavaScript 前端开发 Java
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
本文介绍了JavaScript中常用的函数和方法,包括通用函数、Global对象函数以及数组相关函数。详细列出了每个函数的参数、返回值及使用说明,并提供了示例代码。文章强调了函数的学习应结合源码和实践,适合JavaScript初学者和进阶开发者参考。
25 2
[JS]同事:这次就算了,下班回去赶紧补补内置函数,再犯肯定被主管骂
|
12天前
|
前端开发 JavaScript 开发者
除了 Generator 函数,还有哪些 JavaScript 异步编程解决方案?
【10月更文挑战第30天】开发者可以根据具体的项目情况选择合适的方式来处理异步操作,以实现高效、可读和易于维护的代码。
|
13天前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
26天前
|
JavaScript 前端开发
JavaScript 函数语法
JavaScript 函数是使用 `function` 关键词定义的代码块,可在调用时执行特定任务。函数可以无参或带参,参数用于传递值并在函数内部使用。函数调用可在事件触发时进行,如用户点击按钮。JavaScript 对大小写敏感,函数名和关键词必须严格匹配。示例中展示了如何通过不同参数调用函数以生成不同的输出。
|
28天前
|
存储 JavaScript 前端开发
JS函数提升 变量提升
【10月更文挑战第6天】函数提升和变量提升是 JavaScript 语言的重要特性,但它们也可能带来一些困惑和潜在的问题。通过深入理解和掌握它们的原理和表现,开发者可以更好地编写和维护 JavaScript 代码,避免因不了解这些机制而导致的错误和不一致。同时,不断提高对执行上下文等相关概念的认识,将有助于提升对 JavaScript 语言的整体理解和运用能力。
|
1月前
|
存储 JavaScript 前端开发
JavaScript 对象的概念
JavaScript 对象的概念
37 4
|
1月前
|
缓存 JavaScript 前端开发
JavaScript中数组、对象等循环遍历的常用方法介绍(二)
JavaScript中数组、对象等循环遍历的常用方法介绍(二)
31 1
|
1月前
|
存储 JavaScript 前端开发
js中函数、方法、对象的区别
js中函数、方法、对象的区别
16 2