ES6转ES5, javascript---第二季之函数的扩展
来源:互联网 发布:王者荣耀1元60点卷淘宝 编辑:程序博客网 时间:2024/04/29 18:13
1,函数的扩展
(1)函数参数的默认值
在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function create(x,y){ y = y || 'andy'; console.log(x,y);}create('Hello');//Hello andycreate('Hello','China');//Hello Chinacreate('Hello','');//Hello andyES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function create(x,y='andy'){ console.log(x,y);}create('Hello');//Hello andycreate('Hello','China');//Hello Chinacreate('Hello','');//Hello
function createNums(x=0,y=0){ this.x = x; this.y = y;}var nums = new createNums();console.log(nums);//{x:0,y:0}参数变量是默认声明的,所以不能用
let
或const
再次声明。function createNums(x=0){ let x = 5; const x= 8;}上面代码中,参数变量
x
是默认声明的,在函数体中,不能用let
或const
再次声明,否则会报错。使用参数默认值时,函数不能有同名参数。
let x = 9;function foo(p = x+1){ console.log(p);}foo();//10x = 10;foo();//11//如果参数默认值是变量,那么参数就不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的
参数默认值可以与解构赋值的默认值,结合起来使用。
function createNums({x,y=2}){ console.log(x,y);}createNums({});// undefined, 2createNums({x:1});//1 2createNums({x:1,y:5});//1 5//createNums();//Cannot read property 'x' of undefined//上面代码使用了对象的解构赋值默认值,而没有使用函数参数的默认值。只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值而生成。//如果函数foo调用时参数不是对象,变量x和y就不会生成,从而报错。如果参数对象没有y属性,y的默认值2才会生效。(2)参数默认值的位置
//通常情况下,定义了默认值的参数,应该是函数的尾参数。//因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。function foo(x=1,y){ console.log([x,y]);}foo();//[1, undefined]foo(1);//[1, undefined]foo(3);//[3, undefined]//foo(,2);//报错foo(undefined,2);//[1, 2]//如果传入undefined,将触发该参数等于默认值,null则没有这个效果。foo(null,5);//[null, 5](3)函数的length属性
指定了默认值以后,函数的length
属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length
属性将失真。
console.log((function(a){}).length);//1console.log((function(x=1){}).length);//0console.log((function(x,y=1){}).length);//1//这是因为length属性的含义是,该函数预期传入的参数个数。//某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,rest参数也不会计入length属性。console.log((function(...args){}).length);//0//如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。console.log((function(x=0,b,c){}).length);//0console.log((function(x,y=1,z,g){}).length);//1
(4)作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。
等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
var x = 1;function foo(x,y=x){ console.log(x,y);}foo(3);//3 3//上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。//在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是3。let y = 1;function foo1(z=y){ let y = 2; console.log(z);}foo1();//1//上面代码中,函数f调用时,参数z = y形成一个单独的作用域。//这个作用域里面,变量y本身没有定义,所以指向外层的全局变量y。函数调用时,函数体内部的局部变量y影响不到默认值变量y。//如果此时,全局变量x不存在,就会报错function foo2(y=x){ let x = 2; console.log(y);}foo2();//下面这种写法也会报错var x =2;function f(x=x){ //...to do}f();//上面代码中,参数x = x形成一个单独作用域。实际执行的是let x = x,由于暂时性死区的原因,这行代码会报错”x 未定义“。(5)rest参数
ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments
对象了。
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values){ let sum = 0; for(var val of values){ sum += val; } return sum;}console.log(add(1,2,3));//6//上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。//函数的length属性,不包括 rest 参数。console.log((function(a){}).length);//1console.log((function(a,...b){}).length);//1console.log((function(...b){}).length);//0//console.log((function(a,...b,c){}).length);//注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。(6)扩展运算符
扩展运算符(spread)是三个点(...
)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列
console.log(...[1,2,3]);//1 2 3console.log(1,...[2,3,4],5);//1 2 3 4 5console.log([...document.getElementsByTagName('b')]);//[b, b, b, b]//该运算符主要用于函数调用function push(array,...items){ array.push(...items);}function add(x,y){ return x+y;}var nums = [1,2];console.log(add(...nums));//3//上面代码中,array.push(...items)和add(...numbers)这两行,都是函数的调用,//它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列替换数组的apply方法
//由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了//es5function f(x,y,z){ // console.log();}var args = [1,2,3];f.apply(null,args);//es6function f1(x,y,z){ //...}var arg = [1,2,3];f(...arg);//下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法// es5console.log(Math.max.apply(null,[1,2,3]));//3//es6console.log(Math.max(...[1,2,3,4]));//4//等同于console.log(Math.max(1,2,3,4));//4//另一个例子是通过push函数,将一个数组添加到另一个数组的尾部//es5var arr1 = [1,2,3];var arr2 = [4,5,6];Array.prototype.push.apply(arr1,arr2);console.log(arr1);//[1, 2, 3, 4, 5, 6]//es6var arr3 = [7,8,9];arr2.push(...arr3);console.log(arr2);//[4, 5, 6, 7, 8, 9]//上面代码的ES5写法中,push方法的参数不能是数组,所以只好通过apply方法变通使用push方法。//有了扩展运算符,就可以直接将数组传入push方法(7)扩展运算符的应用
合并数组
var arr1 = [1,2];var arr2 = [3,4];var arr3 = [5,6];//es5var arr4 = arr1.concat(arr2,arr3);console.log(arr4);//[1, 2, 3, 4, 5, 6]//es6console.log([...arr1,...arr2,...arr3]);//[1, 2, 3, 4, 5, 6]与解构赋值相结合
扩展运算符可以与解构赋值结合起来,用于生成数组。
const [n1,...rest] = [1,2,3];console.log(n1);//1console.log(rest);//[2,3]const [n2,...values] = [];console.log(n2);//undefinedconsole.log(values);//[]const [n3,...v] = ['foo'];console.log(n3);//fooconsole.log(v);//[]//果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错const [...n4,n5] = [1,2,3,4,5];//报错const [n6,...n7,n8] = [1,2,3];//报错字符串
扩展运算符还可以将字符串转为真正的数组
console.log([...'andy']);//["a", "n", "d", "y"]//上面的写法,有一个重要的好处,那就是能够正确识别32位的Unicode字符console.log('x\uD83D\uDE80y'.length);//4console.log([...'x\uD83D\uDE80y'].length);//3实现了Iterator接口的对象
任何Iterator接口的对象,都可以用扩展运算符转为真正的数组
var nodelist = document.getElementsByTagName('b');var array = [...nodelist];console.log(array);//[b, b, b, b]//上面代码中,getElementsByTagName方法返回的是一个nodeList对象。//它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList对象实现了Iterator接口//对于那些没有部署Iterator接口的类似数组的对象,扩展运算符就无法将其转为真正的数组let arraylike = { '0':'a', '1':'b', length:2}let arr = [...arraylike];//上面代码中,arrayLike是一个类似数组的对象,但是没有部署Iterator接口,扩展运算符就会报错。//这时,可以改为使用Array.from方法将arrayLike转为真正的数组
Map和Set结构,Generator函数
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构
let map = new Map([ [1,'a'], [2,'b'], [3,'c']]);let arr = [...map.keys()];console.log(arr);//[1, 2, 3]
(8)严格模式
从ES5开始,函数内部可以设定为严格模式
function doSomething(a, b) { 'use strict'; // code}《ECMAScript 2016标准》做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,
那么函数内部就不能显式设定为严格模式,否则会报错这样规定的原因是,函数内部的严格模式,同时适用于函数体代码和函数参数代码。
但是,函数执行的时候,先执行函数参数代码,然后再执行函数体代码。
这样就有一个不合理的地方,只有从函数体代码之中,才能知道参数代码是否应该以严格模式执行,
但是参数代码却应该先于函数体代码执行
// 报错function doSomething(a, b = a) { 'use strict'; // code}两种方法可以规避这种限制。第一种是设定全局性的严格模式,这是合法的。
'use strict';function doSomething(a, b = a) { // code}第二种是把函数包在一个无参数的立即执行函数里面。const doSomething = (function () { 'use strict'; return function(value = 42) { return value; };}());(9)name属性
function foo(){}console.log(foo.name);//foo//需要注意的是,ES6 对这个属性的行为做出了一些修改。//如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。var f = function(){}//es5console.log(f.name);//''//es6console.log(f.name);//f//如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字const n1 = function bar(){}console.log(n1.name);//bar//Function构造函数返回的函数实例,name属性的值为anonymous。console.log((new Function).name);//anonymous//bind返回的函数,name属性值会加上bound前缀。function a1(){}console.log(a1.bind({}).name);//bound a1(10)箭头函数
ES6允许使用“箭头”(=>
)定义函数var f = v =>v;//等同于var f = function(v){ return v;};如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分var f1 = ()=>5;//等同于var f1 = function(){ return 5;};var sum = (n1,n2) => n1+n2;//等同于var sum = function(n1,n2){ return n1+n2;}如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回var sum = (n1,n2) => {return n1+n2};//由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。var getid = id => ({id:id,name:'andy'});console.log(getid(1));//{id: 1, name: "andy"}//箭头函数可以与变量解构结合使用const full = ({n1,n2}) => n1 +' '+ n2;//等同于function full(person){ return person.n1 +' '+person.n2;}//箭头函数更加简洁const square = n => n*n;console.log(square(5));//25箭头函数的一个用处是简化回调函数//正常函数写法var n = [1,2,3,4].map(function(x){ return x*x;});console.log(n);//[1, 4, 9, 16]//箭头函数写法var n1 = [1,2,3].map(x => x*x);console.log(n1);//[1, 4, 9]//下面是rest参数与箭头函数结合的例子const num = (...nums) => nums;console.log(num(1,2,3,4,5));//[1, 2, 3, 4, 5]const arr = (n1,...n) => [n1,n];console.log(arr(1,2,3,4));//[1,[2,3,4]]箭头函数有几个使用注意点。
(1)函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。(2)不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。(3)不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。(4)不可以使用
yield
命令,因此箭头函数不能用作Generator函数。上面四点中,第一点尤其值得注意。
this
对象的指向是可变的,但是在箭头函数中,它是固定的。//箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域function f(){ setTimeout(() => { console.log('id:',this.id); },100)}var id = 2;f.call({id:3});//箭头函数可以让this指向固定化,这种特性很有利于封装回调函数。下面是一个例子,DOM事件的回调函数封装在一个对象里面var handler = { id : '1', init:function(){ document.addEventListener('click', event => this.doSomething(event.type),false); }, doSomething:function(type){ console.log('Handing'+type+'for'+this.id); }}//上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。//另外,由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向
0 0
- ES6转ES5, javascript---第二季之函数的扩展
- ES6转ES5,javascript---第三季之浅谈对象
- ES6转ES5,javascript--第一季
- es6之函数的扩展
- 浅谈JavaScript、ES5、ES6
- 浅谈JavaScript、ES5、ES6
- 浅谈JavaScript、ES5、ES6
- 浅谈JavaScript、ES5、ES6
- 浅谈JavaScript、ES5、ES6
- ES6 学习笔记之《函数的扩展》
- ES6系列之---字符串的扩展函数
- ES6系列之---对象的扩展函数
- 搭建自己的ES6转ES5环境
- es6函数的扩展
- ES6-函数的扩展
- ES6--函数的扩展
- JavaScript ES6与ES5对比
- JavaScript、ES5、ES6有什么
- POJ 1502 单源最短路(Dijkstra算法)
- php、java实现工厂方法模式
- STL(版本介绍)_vecor_源码解析_vs2013(粗略) ----- po学校
- App 后台架构设计方案 设计思想与最佳实践
- wireshark抓包详细图文教程,以及三次握手详解
- ES6转ES5, javascript---第二季之函数的扩展
- linux /proc/loadavg(平均负载)
- PHP默认识别的数据类型是application/x-www.form-urlencoded标准的数据类型
- git 分支 合并
- javac编译带包的java源文件
- 判断单链表是否有环
- linux命令-查找
- Node.js核心模块方法
- android studio导入项目出错