ES6随笔--各数据类型的扩展(3)--函数

ES6随笔--各数据类型的扩展(3)--函数

1. 函数参数的默认值

可以在函数参数中指定默认值,函数内不能再用letconst声明;

使用参数默认值时,不能出现同名参数;

参数默认值惰性求值,每次调用函数会重新计算参数默认值表达式;

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101

可以与解构赋值默认值结合使用:

function ({x, y = 5} = {}) {
    console.log(x, y);
}
function(); // undefined 5
// 以下对比默认值设置不同方式
      function bar({x, y} = {x:0, y:0}) {
        console.log(x, y);
      }
      function bar1({x=0, y=0} = {}) {
        console.log(x, y);
      }
      bar();     //0  0
      bar1();    //0  0
      bar({x:2, y:3});    //2  3
      bar1({x:2, y:3});   //2  3
      bar({x:2});   // 2  undefined
      bar1({x:2});  // 2  0
      bar({y:2});   // undefined   2
      bar1({y:2});  // 0  2
      bar({z:4});   // undefined  undefined
      bar1({z:4});   // 0  0
      bar({});   // undefined  undefined
      bar1({});   // 0  0

设置了默认值的参数一般是函数的尾参数,该参数可以省略;如果位于中间,则无法只省略该参数继续为后面的参数赋值,必须要设置为undefined,否则直接省略而为后面的参数赋值会报错;

undefined可以触发默认值;

函数的length属性中不包含指定了默认值的参数。

指定了参数默认值的函数,函数进行声明初始化时该参数会形成单独的作用域;函数执行完毕后该作用域消失;

// 函数内部 let 声明的变量与参数变量不同作用域
      var x = 1;
      function foo(y = x) {
        let x = 2;
        console.log(y);
      }

      foo() // 1
// 参数的作用域与全局和函数内部都不同;
      var x = 1;
      function foo(x, y = x) {
        x = 2;
        console.log(y);
      }
      foo(); // undefined
      foo(2); // 2
      console.log(x); // 1
// 参数设置默认值时指向全局变量x, 函数内部修改了全局变量,但并不影响参数y--不在同一个作用域;
      var x = 1;
      function foo(y = x) {
        x = 2;
        console.log(y);
      }
      foo(); // 1
      console.log(x); // 2

// 暂时性死区报错(参数默认值相当于 let x = x,此时会忽略全局里的定义)
      var x = 1;
      function foo(x = x) {
        console.log(x);
      }

      foo() // ReferenceError: x is not defined

函数的参数默认值可以用来设置哪些参数不能省略;

2. rest函数

形式为...varialbleName,获取函数的多余参数放入数组中;

// 改写 push 方法的例子
      function push(array, ...items) {
        items.forEach(function(item) {
          array.push(item);
          console.log(item);
        });
        return array.length;
      }
      var a = [];
      console.log(push(a, 1, 2, 3)); // 1 2 3 3

rest参数只能是最后一个参数;函数的length属性,不包含rest函数;

3. 严格模式

只要函数参数使用了默认值,解构赋值或者扩展运算符,函数内部就不能显式设定严格模式;

替代的办法 1) 使用全局性的严格模式;2) 使用立即执行的无参函数,使用严格模式并返回该函数;

4. name 属性

函数的name属性,返回该函数的函数名;

如果把匿名函数赋值给一个变量,匿名函数的name属性返回该变量名(ES5 则为空字符串);

如果把一个具名函数赋值给一个变量,该函数的name属性返回原本的函数名。

      const f = function () {};
      console.log(f.name); // f
      const f1 = function func() {};
      console.log(f1.name); // func

Function构造函数返回的函数实例, name属性返回anonymous;

使用bind()返回的函数,name属性会自动在函数名前面加bound

5. 箭头函数

使用箭头=>定义函数;funcName(args) => {code block}function funcName(args) = {code block}相同;

注意:

1. 箭头函数中的this绑定函数定义时所在的作用域,而不是运行时的作用域;

2. 不能使用new命令;

3. 不可以使用argumets对象,可以使用rest参数代替;

4. 不可以使用yield命令,因此箭头函数不能用作Generator函数;

箭头函数本身没有自己的this,在定义时指向外部作用域的对象;也不能通过call,bind,apply改变this的指向。

      var f = function () {console.log(this.id);}
      var f1 = () => {console.log(this.id)};
      f.call({id:11});  // 11
      f.call({id:22});  // 22
      f1.call({id:11}); // undefined
      f1.call({id:22}); // undefined
// 大括号被解释为代码块,所以需要返回一个对象的情况下需要在大括号外加小括号;(再加一层大括号无效);
      var f = (x) => {a:x};
      console.log(f(3)); // undefined
      var f = (x) => ({a:x});
      console.log(f(3)); // a : 3

嵌套函数;

部署管道机制;

改写λ演算;

6. 双冒号运算符(提案)

用来取代bind, apply, call的作用,绑定对象作用域;

// 将左边对象,作为上下文环境(即this 对象),绑定到右边的函数上
object::function

// 双冒号左边为空,右边为一个对象的方法,代表将此方法绑定到该对象上;
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

7. 尾调用优化

尾调用(Tail Call)函数的最后一步是调用另一个函数。

// 这种情况不属于尾调用,相当于调用g(x)后返回undefined;‘return g(x)’算是一种尾调用。
      function f(x){
        g(x);
      }

在函数内部调用其他函数时,所有当前被调用而未结束的函数的调用帧会形成调用栈;

尾调用由于是最后一步操作,所以不需要保留外部函数的调用帧,因为调用位置、内部变量等信息都不会再用到。

尾调用优化,即只保留最后调用函数的调用帧。但是需要做到内部调用函数不再使用外部函数的内部变量;

函数尾调用自身,即尾递归;

      //尾调用 Fibonacci数列案例
      function fib(n) {
        if (n<=1) return 1;
        return fib(n-1)+fib(n-2);
      }
      function fib1(n, ac1=1, ac2=1) {
        if (n<=1) return ac2;
        return fib1(n-1, ac2, ac1+ac2);
      }
      console.log(fib(10)); //~20ms 是优化函数运行时间至少10倍;
      console.log(fib1(10));  //~1ms
      console.log(fib(100));   // 无法计算,栈溢出
      console.log(fib1(100));   // <10ms

尾递归优化只能在严格模式下开启,正常模式无法执行;

尾递归优化在非严格模式下无效;实现方法可以使用循环代替:

// 非严格模式下实现尾调用优化--循环
// 递归函数
        function test(x, y) {
          if (y > 0) {
            return test(x+1, y-1);
          } else {
            return x;
          }
        }
// 蹦床函数
        function trampoline(fn) {
            while (fn && fn instanceof Function) {
              fn = fn();
            }
            return fn;
        }
//改写递归函数
        function test1(x, y) {
          if (y > 0) {
            return test1.bind(null, x+1, y-1);
          } else {
            return x;
          }
        }
// 通过蹦床函数调用递归函数
        var a1 = trampoline(test1(1, 100000));
        console.log(a1); // 100001

8. 函数参数的尾逗号

ECMA2017允许函数的最后一个参数后面有逗号;

附加个人随记:bind方法

bind可以直接指定绑定的对象,以及对应的参数,返回绑定后的函数,后面加括号才会执行;

function testbind(str) {
  return str + this;
}
var a1 = {name:"Bai"};
console.log(testbind.bind(a1));
// ? testbind(str) {
//   return str + this;
// }
console.log(testbind.bind(a1,"huahua"));
// ? testbind(str) {
//   return str + this;
// }
console.log(testbind()); //undefined[object Window]
console.log(testbind.bind(a1,"huahua")()); //huahua[object Object]
console.log(testbind.bind(a1)("huahua")); //huahua[object Object]


ES6随笔--各数据类型的扩展(3)--函数

原文地址:https://www.cnblogs.com/muTing/p/9191616.html

时间: 2024-11-05 23:24:29

ES6随笔--各数据类型的扩展(3)--函数的相关文章

ES6随笔--各数据类型的扩展(1) --字符串和正则

ES6随笔--基本数据类型的扩展 字符串 Unicode表示法 \uxxxx表示的Unicode如果码数超过了0xFFFF的范围,只要放进大括号内就能识别: codePointAt() 用codePointAt()可以准确识别码数超出0xFFFF范围的Unicode字符: for...of循环能够正确识别32位的UTF-16字符: String.fromCodePoint() 与codePointAt()作用刚好相反:根据传入的Unicede码数返回对应的字符: 遍历 for...of循环可以循

ES6随笔--各数据类型的扩展(2)--数值

ES6随笔--各数据类型的扩展(2)--数值 二进制和八进制表示 二进制0b(或0B)表示:八进制0o(或0O)表示: 这两种字符串转换成十进制数值,使用Number()方法: Number('0b111') // 7 Number.isFinite(), Number.isNaN(); Number.isFinite()的参数如果不是数值,一律返回false, Infinity/-Infinity/NaN都返回false: Number.isNaN()对不是NaN的参数一律返回false; 这

es6之各种数据类型的扩展

一. 字符串的扩展 为字符串添加了Iterator,可以被for...of遍历 includes.startsWith.endsWith都会返回布尔值,且支持第二个参数(开始搜索的位置),endsWith针对前n个字符,其他两个都是从第n个到结束 repeat返回一个新字符串,参数为重复的次数(小数会向下取整,负数或Infnity会报错,0到-1等同于0,字符串会转为数字) padStart.padEnd在头部或尾部补全,第一个参数是字符串最小长度,第二个参数是用来补齐的字符串 模板字符串`${

ES6 - Note3:数组、对象与函数的扩展

一.数组的扩展,ES6在数组扩展了一些API,以实现更多的功能 1.Array.from:可以将类数组和可遍历的数据结构转换成真正的数组,如下所示 var a = { '0':1,'1':1,length:2 }; var arr = Array.from(a); console.log(arr) Array [ 1, 1 ] ---------------ES5的实现----------- var arr = [].slice.call(a); console.log(arr) Array [

初入ES6随笔

初入ES6随笔 var c = a => console.log(a) let a1 = 1; //只在块儿状作用域生效的变量声明 const a2 = 1; //常量声明,值不可改变,快儿状作用域 解构赋值语法 let [a3, b3, c3] = [1, 2, 3]; //数组解构赋值 let [a4 = 0, ...b4] = [1, 2, 3, 4] //数组解构赋值 a4 = 1 b4 = [2,3,4] 允许默认值; let {a5, b5} = {a5: "1",

ES6关于Unicode的相关扩展

前面的话 JS中的字符串类型是由引号括起来的一组由16位Unicode字符组成的字符序列.在过去,16位足以包含任何字符,直到Unicode引入了扩展字符集,编码规则不得不进行变更.本文将详细介绍ES6关于Unicode的相关扩展 概述 Unicode的目标是为世界上每一个字符提供唯一标识符,唯一标识符称为码位或码点(code point).而这些码位是用于表示字符的,又称为字符编码(character encode) 在ES6之前, JS 的字符串以 16 位字符编码(UTF-16)为基础.每

ES6随笔--声明变量

ES6随笔--声明变量 let命令 所声明的变量只在其所处代码块内有效: {let a = 10}; alert(a); // ReferenceError: a is not defined {let a = 10; alert(a);} //10 在for循环中,for关键字后面跟的设置循环变量语句和后面循环体内部语句是父作用域和子作用域的关系: var arr = []; for (let i = 0; i < 5; i++) { arr[i] = function () { let i

iOS Foundation 框架概述文档:常量、数据类型、框架、函数、公布声明

iOS Foundation 框架概述文档:常量.数据类型.框架.函数.公布声明 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的漂亮人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. Foundation 框架概述文档:常量.数据类型.框架.函数.

MSSQL报错:参数数据类型 text 对于 replace 函数的参数 1 无效的解决办法

Ms - sql 数据库批量替换字符串 MSSQL报错:参数数据类型 text 对于 replace 函数的参数 1 无效的解决办法 update ContentInfo set spcContent=replace(cast(spcContent as varchar(max)),'http://www.buy5188.com/','http://www.epowerchina.com.cn/')