摘要:在函数方面的扩展比较丰富也很实用,本篇概括了这中的精华知识。所以无法成为构造函数,不能使用操作符。参数将扩展运算符作用于参数,即为参数。声明式,直接为函数名。通过构造函数生成的,为。函数的属性,在其描述对象的属性上,为函数名。
ES6在函数方面的扩展比较丰富也很实用,本篇概括了这中的精华知识。
1 箭头函数箭头函数是ES6中定义函数的新形式。
新形式不仅简化了定义方式,更为函数本身减重(其this, argumnets等与之前不同)。
let fn = () => { console.log("fn"); }; fn(); // "fn" 如果只有一个参数,可以省略括号。 (n => { console.log(n); })(1); // 1 如果不带 {} ,意味着直接返回 => 指向的目标。 console.log( (n => 2)(1) ); // 2 注意,指向的目标只能是单体,如果为表达式需要用 () 包裹形成单体。 console.log( (n => (n + 1))(1) ); // 21.1 this
箭头函数没有自己的this,其使用的this是引用外层的(类似闭包)。
因此其里面的this是固定的,在定义的那一刻起就已经确定,不会再变。
非严格模式下。 --- 之前的函数是执行时确定 this 。 window.id = 0; let obj = { id: 1 }; let fn = function () { console.log(this.id); }; fn(); // 0,引用的是 window 。 obj.fn = fn; obj.fn(); // 1,引用的是 obj 。 --- 箭头函数是定义时确定 this 。 window.id = 0; let obj = { id: 1 }; let fn = () => { console.log(this.id); }; fn(); // 0,引用的是 window 。 obj.fn = fn; obj.fn(); // 0,引用的是 window 。
再利用多层箭头函数来说明。
多层箭头函数,this的寻找途径是一层层向上查找的,类似作用域链查找。
所以多层箭头函数在初次获取到this时,全部函数的this便都确定了。
foo.call({id: 1})()(); // id: 1 function foo() { return () => { return () => { console.log("id:", this.id); }; }; } --- 等价于 function foo() { let _this = this; return function() { return function() { console.log("id:", _this.id); } } }
因为没有自身的this。
所以无法成为构造函数,不能使用new操作符。
所以不能用call, apply和bind这些方法改变this的指向。
let Person = () => {}; let p = new Person(); // 报错,Person is not a constructor。 window.id = "000"; let fn = () => { console.log(this.id) }; let fn1 = fn.bind({ id: "111" }); fn1(); // "000"1.2 其它的不同
函数体内没有arguments对象,可以使用rest参数代替。
不能使用yield命令,因此箭头函数不能用作Generator函数(可以使用async函数)。
let fn = (...args) => { console.log(args); // [1, 2] console.log(arguments); // 报错,arguments is not defined。 }; fn(1, 2);2 函数参数 2.1 默认值
可以为参数设定默认值。
当没有传递该参数或值为undefined时,默认值将被使用。
借用此方式,可以简化函数体,并使参数的性质、类型等更加清晰。
--- ES6 之前 function fn(id, conf) { id || requiredParam(); conf = conf || {}; conf.name = conf.name || ""; conf.ago = conf.ago || 0; console.log(id, conf); } --- ES6 之后 function fn( id = requiredParam(), conf = { name: "", ago: 0 } ) { console.log(id, conf); } function requiredParam() { throw Error("Missing parameter."); }2.2 解构赋值
结合解构赋值,默认值设定的功能会更为强大。
关于解构赋值,可参考此链接。
为了直观的显示它的优势,我们将最终的结果分成三步。
1.使用解构赋值,快速声明变量,并赋予相应的属性值。 fn({ id: "0003" }); // 两者都打印出:"0003" undefined --- ES6 之前 function fn(conf) { let id = conf.id; let name = conf.name; console.log(id, name); } --- ES6 之后 function fn({id, name}) { console.log(id, name); } 2.紧接着,为解构中的变量设定默认值。 fn({ id: "0003" }); // 两者都打印出:"0003" "Unnamed" --- ES6 之前 function fn(conf) { let id = conf.id || "0000"; let name = conf.name || "Unnamed"; console.log(id, name); } --- ES6 之后 function fn({ id = "0000", name = "Unnamed" }) { console.log(id, name); } 3.最后,再为此参数设定默认值。 fn(); // 两者都打印出:"0000" "Unnamed" --- ES6 之前 function fn(conf) { conf = conf || { id: "0000", name: "Unnamed" }; let id = conf.id; let name = conf.name; console.log(id, name); } --- ES6 之后 function fn({ id = "0000", name = "Unnamed" } = {}) { console.log(id, name); }
再思考一个问题:是在解构中还是在参数默认值中设定属性的值?
function fn1(x = {}, {a = 1, b = 2} = x) { console.log(a, b, x) } function fn2(x = {a: 1, b: 2}, {a, b} = x) { console.log(a, b, x) } 这两方法的区别在于:变量a, b的默认值的设置地点。 如果要优先确保解析后的变量有默认值,第一种方式更为有效。 fn1(); // 1 2 {} fn2(); // 1 2 {a:1, b:2} fn1({}); // 1 2 {} fn2({}); // undefined undefined {} fn1({ a: 0 }); // 0 2 {a:0} fn2({ a: 0 }); // 0 undefined {a:0}2.3 rest 参数
将扩展运算符作用于参数,即为rest参数。
它会将所有相应的传入参数合并成一个数组,赋值给rest参数。
rest参数只能是最后一个参数,没有正则中所谓的贪婪性,否则会报错。
打印出:"0001" ["m1","m2"]。 fn("0001", "m1", "m2"); function fn(groupId, ...members) { console.log(groupId, members); }2.4 作用域
如果函数参数使用了默认值、解构赋值或扩展运算符,就产生了参数作用域。
执行函数体时,会先默认声明参数变量。
如果存在参数作用域,会先执行它,再到函数体作用域中。
初始化结束后,参数作用域消失,之后函数体会默认声明同名变量指向相应的参数变量。
因为作用域的存在,参数是惰性(调用时)求值的。
let n = 0; fn(); // 1 n = 1; fn(); // 2 function fn(num = (n + 1)) { console.log(num); }
因为默认声明原则,在函数体中声明同名参数相当二次声明。
使用let, const相当重复声明,会报错。
使用var会解绑函数体与参数作用域的关联,变量便成了纯粹的函数体变量。
--- 普通 let x = 0; fn(1); // 2 function fn(x, y = () => { console.log(x) }) { x = 2; y(); } --- 解绑 let x = 0; fn(1); // 1 function fn(x, y = () => { console.log(x) }) { var x = 2; y(); }
如果存在参数作用域,就不能在函数体中显式的设定严格模式,否则报错。
因为函数内部的严格模式,应该同时作用于函数体和参数作用域。
但是只有进入函数体,才能知道是否有显式地声明,而参数体却先于函数体执行。
不过可以变通的,将此函数置于一个处在严格模式的环境中。
报错:Illegal "use strict" directive ... function fn(n = 0) { "use strict"; }3 函数属性 3.1 name
不同形式的函数,其name属性值构建的方式也不相同,下面是个人总结的八种方式。
1.声明式,直接为函数名。 console.log(fn.name); // "fn" function fn() {} 2.命名函数表达式,直接为函数名。 let fn1 = function fn() {}; console.log(fn1.name); // "fn" 3.表达式,为第一次赋值的变量/属性。 let fn = function() {}; console.log(fn.name); // "fn" let fn1 = fn(); console.log(fn.name); // "fn" let obj = { fn: function() {} }; console.log(fn.name); // "fn" 4.没有赋值的匿名表达式,为空。 console.log( (function() {}).name ); // "" 5.通过构造函数生成的,为 anonymous 。 console.log( (new Function()).name ); // "anonymous" 6.通过 bind() 生成的,name 属性值会加上 bound 前缀。 console.log( (function() {}).bind({}).name ); // "bound " console.log( (function fn() {}).bind({}).name ); // "bound fn" 7.如果对象的方法名为 Symbol 值,name 属性返回的是此 Symbol 的描述。 let s1 = Symbol(); let s2 = Symbol("s2"); console.log( ({ [s1]() {} })[s1].name ); // "" console.log( ({ [s2]() {} })[s2].name ); // [s2] 8.getter/setter 函数的 name 属性,在其描述对象的 get/set 属性上,为 get/set 函数名。 let obj = { get name() {} }; Object.getOwnPropertyDescriptor(obj, "name").get.name; // "get name"3.2 length
其本质含义是该函数预期传入的参数个数。
如果参数有默认值或为rest参数,则它以及它之后的参数都不会被计算在内。
基于这点,在参数设计上,一般把可以省略或有默认值的参数设置为尾参数。
console.log( (function(...args) {}).length ); // 0 console.log( (function(a, {b}, c = 5, d) {}).length ); // 23.3 arguments
类数组对象arguments保存的仅仅存储调用方法时传进来的参数。
这意味着,使用默认值的参数、解构参数或rest参数等都不在其中。
(function (name = "Wmaker") { console.log(name, arguments.length); })(); // "Wmaker" 0 (function ({a, b}) { console.log(a, b, arguments.length); })({ a: 1, b: 2 }); // 1 2 1 (function (...arr) { console.log(arr, arguments.length); })(1, 2, 3); // [1, 2, 3] 3
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/95599.html
摘要:本篇概括了中正则表达式新增部分的精华要点最好有的基础。标志使正则处于模式。关于的字符扩展知识,可查看这里。四字节字符处于模式下的正则,可以正确识别位四字节字符。 本篇概括了ES6中正则表达式新增部分的精华要点(最好有ES5的基础)。 1 u 标志 使正则处于Unicode模式。 关于ES6的字符扩展知识,可查看这里。 1.1 四字节字符 处于Unicode模式下的正则,可以正确识别3...
摘要:四字节字符大幅增强了对字节位字符的支持。内部使用编码规则网页通常为。字符固定为字节,字节为位二进制,其码点小于。有些符号的码点大于,需字节表示,即常说的位字符。表示方法新增一种表示字符的方法。用将码点括起,使其可直接表示超过的值。 1 四字节字符 ES6大幅增强了对4字节(32位)字符的支持。 JS内部使用UTF-16编码规则(网页通常为UTF-8)。 1字符固定为2字节,1字节为...
摘要:基础极值采用标准的位双精度格式存储数值。如果数值的精度超过此限度,第位及后面的会被丢弃。数值的极值分为两种可表示的极值和可精确计算的极值浮点型不算。超过精度的数值可正确显示,但由其计算得出的结果可能不准确。整型数值安全区间。 ES6为数值增加了些常量和方法,使计算更为简便安全。本篇概括了这中的精华知识。 1 基础 1.1 极值 JS采用IEEE 754标准的64位双精度格式存储数值。 ...
摘要:所以,最终极的办法是一层一层冻结所有对象。块级作用域使呈现出块级作用域的特征。声明的变量仅存在于当前块级作用域中。在中,严格模式下等价于使用声明,非严格下等价于使用。在中使用声明的变量,为了保持程序的严谨性,不允许被访问。 let和const都是声明变量的新方式。 一般的,由于这两种声明方式的特性,现在项目组的开发规范中都会要求:不使用var而是let或const。 Const co...
摘要:按一定的匹配模式,从数组或对象中解构出相应值,并赋值给变量。和是单值,没有相应的构造函数,不能被解构。等价于报错解构方式左侧的解构符决定如何看待右侧的解构值。解构符意味着视右侧的值为对象,采用对象解构。 按一定的匹配模式,从数组或对象中解构出相应值,并赋值给变量。 let [a] = [3]; // a = 3 let [, a] = [3, [7]]; // a = [7] let ...
阅读 1753·2021-09-26 09:46
阅读 3034·2021-09-22 15:55
阅读 2624·2019-08-30 14:17
阅读 3041·2019-08-26 11:59
阅读 1823·2019-08-26 11:35
阅读 3165·2019-08-26 10:45
阅读 3164·2019-08-23 18:28
阅读 1156·2019-08-23 18:21