1,for循环的特别之处,即是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。
结果是:i = andy;(打印一次然后退出循环)for (var i = 0; i < 3; i++) { var i = 'andy'; console.log('i='+i); }
我们看下let语句
结果是:for (let i = 0; i < 3; i++) { let i = 'andy'; console.log(i); }
andy
andy
andy
输出了3次,这表明函数内部的变量i和外部的变量i是分离的;2,es6中不存在变量提升
console.log(aa);// undefiend(变量提升,但是没有值,所以会打印出undefined) var aa = 2; console.log(bb);// 引用错误(let不会发生变量提升,说明在声明之前,bb是不存在的,此时会抛出错误); let bb = 2;
3,暂时性封闭性死区
//只要在快级作用域中存在let命令,它所声明的变量就绑定了在这个区域,不再受外部的影响; var aa = 'andy'; if(aa){ aa = 'jack';// 引用错误 let aa; }
在没有let之前,typeof运算符是安全的;现在在es6中则会出现问题;这一设计原则是为了让大家,变量一定要在声明之后使用//因此衍生的一个问题是typeof 操作符的运用是否安全 typeof aa;//referenceError let aa; //然而,如果一个变量没有let声明,反而不会出错; typeof bb;//undefined
否则会报错;4,隐含的容易出错的
function act(x=y,y=2){ console.log(x+y);// 有些浏览器会打印出NAN,有些浏览器会报错 } act(); //参数x默认等于参数y,而此时y没有声明,如果y的值默认是x,就不会报错,因为此时x已经声明过了 function acts(x=2,y=x){ console.log(x+y);// } acts();//4
5,使用let声明变量时,只要变量在海没有声明完成前使用,就会报错;
var x=x;// 不会报错 let x=x;//使用let声明变量时,只要变量在还没有声明完成前使用,就会报错
6,不允许重复声明变量;
因此不能在函数内部重新声明参数;function aa(){ let a=2; var a=1;//报错 } aa();
function aa(name){ let name;//报错 }
function aa(name){ { let name;//正确 } }
7,块级作用域
变量提升导致内部的mydate覆盖外层的mydate变量;var mydate = new Date(); function create(){ console.log(mydate); if(false){ var mydate = 'andy'; } } create();// undefined
而es6规定了块级作用域var a = 'andy'; for(var i =0;i<a.length;i++){ console.log(a[i]); } console.log(i);//4---循环结束后,i变量并没有消失,而是泄漏给了全局
而es6也允许块级作用域的嵌套function a(){ let i = 1; if(true){ let i =2; console.log(i);//2 } console.log(i);//1---外层变量不受内部变量的影响 } a();
{ { { let name = 'andy'; console.log(name);//andy } console.log(name);//为空或者报错(看浏览器) } }
块级作用域的出现,实际上说明了我们之前用到了立即执行函数的退下历史舞台;(function(){ let name = 'andy'; })(); { let name = 'andy'; }
考虑到环境导致的行为差异太大,应该避免在块级作用域中声明函数;如果确实需要也应该写成函数表达式而不是函数声明语句;提示:es6允许在块级作用域中声明函数,前提是必须又大括号,否则会报错;{ //函数声明语句 let name = 'andy'; function f(){ return name; } } { //函数表达式---推荐 let names = 'andy'; let f = function(){ return names; } }
if(true){ function f(){ } }
if(true) function f(){//报错,如果用es6编译的话,就提示报错 }
8,const命令
const声明的常量值,不允许改变;也就是说,一但声明了常量值,就必须赋值;const a = 1; console.log(a); a = 3;//报错
当然了,const和let作用域一样,必须在块级才生效;同时,不可重新赋值;const a;//必须赋值,否则会报错
9,es6声明变量的6种方法;
1,var 2,function 3,let 4,const 5,import 6,class
10,顶层对象的属性;
顶层对象在浏览器中指的是window,在node环境中指的是global;顶层对象的属性和全局变量关联;而es6规定,let,const,class声明的全局变量不属于顶层对象;也就是说从es6开始,全局变量逐步与顶层对象的属性脱离;window.a= 1; console.log(a);//1
11,数组的结构赋值
//以前,为变量赋值,只能指定值; let a =1; let b=2; let c=3; console.log(a,b,c);//1,2,3
//而es6,可以这样写--可以从数组中提取值,按照对应位置,对变量赋值。
let [a,b,c] = [1,2,3]; console.log(a,b,c);
这种就是模式匹配,只要左边和右边的值对应,就会产生一一对应;let [a,[b,c]] = ['andy',[2,3]]; console.log(a,b,c);//andy,2,3
let [,,name] = [1,2,'andy']; console.log(name);//andy
let [school,...name] = ['香港城市大学','andy','lucy','jack']; console.log(school,name);//香港城市大学,Andy,lucy,jack
当然,如果解构不成功,就会返回undefiend;let [x, y, ...z] = ['a']; console.log(x,y,z);//a,undefiend,[]
另外,let [foo] = []; let [bar, foos] = [1]; console.log(foo,bar,foos);//undefined,1,undefined
而对于Set结构,也同样可以使用数组的结构赋值let [age] = 20; console.log(age);//报错
重点: 迭代器iteratorslet [x,y,z] = new Set(['a','b','c']); console.log(x,y,z);//a,b,c
Iterator接口的意思就是说:符合Iterator接口的对象里需要有一个叫next的函数,这个函数需要返回一个对象,并且这个对象符合IteratorResult接口规范;IteratorResult接口里面定义了两个属性,一个是done,代表迭代是否已经完成。另一个属性value代表迭代过程中产生的值。
之所以可以采用数组的结构赋值,是因为只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值;12,es6中的默认值
let [x,y = 'b'] = ['a']; console.log(x,y);//a,b let [bar = true] = []; console.log('bar='+bar); let [a,b='2'] = ['1',undefined]; console.log(a,b);//1,2 let [m,n]= [1,2]; console.log(m,n);//1,2 //es6内部使用严格相等运算符(===),判断一个位置是否有值; //所以一个数组成员不严格等于undefiend,默认值就不会生效; let [m1 = '1'] = [undefined]; console.log(m1);//1 let [m2 = '2'] = [null]; console.log('m2=');//空值
上面中一个数组成员为null,默认值就不会生效;因为null不严格等于undefiend;但是,如果默认值是一个表达式,只有在用到到时候才会调用;
function f(){ return 1; } let [x=f()] = [2]; console.log(x);//2--x能取到值,所以不用吊用f()函数 let [y=f()]=[]; console.log(y);//1--y不能取到值,掉用了f()函数
默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [x1=1,y1=x1] =[]; console.log(x1,y1);//1,1 let [x2=2,y2=x2]=[2]; console.log(x2,y2);//2,2 let [x3=3,y3=x3]=[1,2]; console.log(x3,y3);//1,2 let [x4=y4,y4=4]=[]; console.log(x4,y4);//报错
13,对象的解构赋值;
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;let {x1,y1} = {x1:'1',y1:'2'}; console.log(x1,y1);//1,2
而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let {x2,y2} = {y2:'1',x2:'2'}; console.log(x2,y2);//2,1
如果,变量名和属性名不一致,可以写成这样:let {x3,y3} = {x3:'1',y4:'3'}; console.log(x3,y3);//1,undefiend--变量没有对应的同名属性,导致取不到值,最后undefiend
let {foo:baz}= {foo:'1',bar:'2'}; console.log(baz);//1
实际上,对象的解构赋值的内部机制,先找到同名属性,然后再赋给对应的变量;let obj = {foo:'1',bar:'2'}; let {foo:f,bar:b} = obj; console.log(f,b);//1,2
正在被赋值的是后者,不是前者;上面foo是匹配模式,bar才是变量;也就是说真正被赋值的是bar,不是foo;let {foo:baz}= {foo:'1',bar:'2'}; console.log(baz);//1 console.log(foo);//报错,foo is undefined
这种写法,变量的声明和赋值是一体的;对于let和const来说,变量不能重新声明,否则会报错;let foo; let {foo} = {foo:'1'};//报错
此时,p是模式,不是变量;//和数组一样,解构也可以用于嵌套结构的对象; let obj ={ p:[ 'hello', { y:'world' } ] }; let {p:[x,{y}]}= obj; console.log(x,y);//hello,world
同时,解构允许,等号左边的模式之中,不放置任何变量名;因此会写出非常奇怪的赋值表达式;虽然没有任何意义,但是是合法的;({} = [true,false]); ({}='abc');
14,字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。let [a,b,c,d,e] = 'andys'; console.log(a,b,c,d,e);//a,n,d,y,s
//类数组的对象都有一个length属性,因此还可以对这个属性解构赋值。 let {length:len} = 'hello'; console.log(len);//5
15,数值和布尔值的解构赋值
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。//解构赋值时,如果等号右边是数值和布尔值,则会先转为对象 let {toString:s} = 132; console.log(s===Number.prototype.toString);//true let {toString:b} = true; console.log(b===Boolean.prototype.toString);//true
由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
let {foo:x} = undefined;//报错
16,函数参数的解构赋值
function f([x,y]){//--在传入参数的时候,数组参数就被解构成变量 console.log(x+y); } f([1,2]);//3
//函数的参数的解构也可以使用默认值 //通过对这个对象解构,得到x和y的值,如果解构失败,则使用默认值 function f({x=0,y=0} = {}){ console.log([x,y]); } f({x:3,y:4});//[3,4] f({x:3});//[3,0] f({});//[0,0] f();//[0,0]
17,圆括号的问题
如果模式中出现圆括号怎么处理。ES6的规则是,只要有可能导致解构的歧义,就不得使用圆括号;
注意:以下 三种情况不要使用圆括号(1)声明变量的时候
let [(a)] = [1];
(2)函数参数重不能带圆括号let {x:(y)} = {};
function f([(a)]){ console.log(a); }
(3)赋值语句中,不能将整个模式或者嵌套模式中的一层,放在圆括号之中;
({m:n}) = {m:42}; ([x]) = [5];
其实,可以使用圆括号的只有一中情况:赋值语句的非模式部分,可以使用圆括号
那么,变量的解构赋值都有哪些用途呢?let b; let d; [(b)] = [2]; console.log(b); ({p:(d)} = {}); console.log(d);
(1)交互变量的值(2)从函数返回多个值let x=1; let y=2; [x,y] = [y,x]; console.log(x,y);//2,1
(3)函数参数的定义//函数只能返回一个值,如果要返回多个值,只能将他们放在数组或对象里返回. //返回一个数组 function f(){ return ['a','b','c']; } let [a,b,c] = f(); console.log(a,b,c); //返回一个对象 function m(){ return{ foo:1, bar:2 } } let {foo,bar} = m(); console.log(foo,bar);//1,2
(4)提取json数据//解构赋值可以很方便的将一组参数与变量名对应起来 function f([a,b,c]){ console.log(a,b,c); } f([1,2,3]);//1,2,3 function m({x,y,z}){ console.log(x,y,z); } m({y:2,z:1,x:3});//3,2,1
因此,可以快速提取json数据的值;let myJson = { name:'andy', age:28, school:'香港城市大学', className:['刘德华','梁朝伟'] }; let {name,age,school,className:names} = myJson; console.log(name,age,school,names);
(5)函数参数的默认值(6)遍历map结构
//部署了iterator接口的对象,都可以使用for..of遍历,Map结构原生支持iterator接口,
(7)输入模块的制定方法//配合变量的解构赋值,获取键名和键值就方便多了; var map = new Map(); map.set('first','hello'); map.set('second','andy'); for(let [key,value] of map){ console.log(key+' is '+value) }
18,字符串的扩展
ES6为字符串添加了遍历器接口,使得字符串可以被//js允许\uxxxx形式表示一个字符,其中'xxxx'表示字符的码点 '\u0061'; //但是,这种表示法只限于\u0000——\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表达。 "\uD842\uDFB7"; "\u20BB7";//' 7' //上面代码表示,如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript会理解成\u20BB+7。 // 由于\u20BB是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。 //ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。 "\u{20BB7}";
for...of
循环遍历。for(let codePoint of 'foo'){ console.log(codePoint);//f,o,o }
(1),includes(),startsWith(),endsWith();
//传统上,js只有indexof方法可以用来确定一个字符串是否包含在另一个字符串中 //ES6又提供了三种新方法 /** includes():返回布尔值,表示是否找到了参数字符串。 startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。 endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。 */ var s = 'andy'; console.log(s.includes('y'));//true console.log(s.startsWith('a'));//true console.log(s.endsWith('y'));//true //这三个方法都支持第二个参数,表示开始搜索的位置。 var m= 'andy'; console.log(m.includes('y',1));//true console.log(m.startsWith('a',0));//true console.log(m.endsWith('y',3));//false //上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。 // 它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
(2)repeat()repeat
方法返回一个新字符串,表示将原字符串重复n
次。(3)padStart(),padEnd()var m ='x'.repeat(3); console.log(m);//xxx //参数如果是小数,会被取整。 console.log('andy'.repeat(2.6));//andyandy //如果repeat的参数是负数或者Infinity,会报错。 //console.log('andy'.repeat(-1));//报错 /** * 但是,如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。 * 0到-1之间的小数,取整以后等于-0,repeat视同为0。 * 参数NaN等同于0。 * 如果repeat的参数是字符串,则会先转换成数字。 * */ console.log('andy'.repeat('2'));//andyandy
ES7引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
(4)模板字符串$('#mydiv').append(` 荣贺,<b>${'成龙'}</b>拿了奥斯卡奖, 对此,我们庆幸,他说,<span>${'很高兴自己是个中国人!'}</span> `);
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
(5)标签模板// 字符串中嵌入变量 var name = "andy", time = "today"; console.log(`Hello ${name}, how are you ${time}?`); //模板字符串中嵌入变量,需要将变量名写在${}之中。 //如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。 var greeting = `\`You\` World!`; console.log(greeting);//`You` World! //如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。 $('#mydiv').html(` <ul> <li> <span>加油</span> </li> <li> <span>中国!</span> </li> </ul> `);
“标签模板”的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容。//它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。 // 这被称为“标签模板”功能(tagged template)。 alert('123'); //等同于 alert`456`; //标签模板其实不是模板,而是函数调用的一种特殊形式。 // “标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。 //但是,如果模板字符里面有变量,就不是简单的调用了, // 而是会将模板字符串先处理成多个参数,再调用函数。 let a= 1; let b=2; tag`hello ${a+b} world ${a*b}`; //等同于 tag(['hello ',' world',''],3,2); //上面代码中,模板字符串前面有一个标识名tag,它是一个函数。 // 整个表达式的返回值,就是tag函数处理模板字符串后的返回值。 //函数tag依次会接收到多个参数。 function tag(stringArr, value1, value2){ // ... } // 等同于 function tag(stringArr, ...values){ // ... } //tag函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分, // 也就是说,变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间,以此类推。 // //tag函数的其他参数,都是模板字符串各个变量被替换后的值。 // 由于本例中,模板字符串含有两个变量,因此tag会接受到value1和value2两个参数。 // //tag函数所有参数的实际值如下。 // //第一个参数:['Hello ', ' world ', ''] //第二个参数: 15 //第三个参数:50 //也就是说,tag函数实际上以下面的形式调用。 tag(['Hello ', ' world ', ''], 15, 50); var a = 5; var b = 10; function tag(s, v1, v2) { console.log(s[0]); console.log(s[1]); console.log(s[2]); console.log(v1); console.log(v2); return "OK"; } tag`Hello ${ a + b } world ${ a * b}`; let a=2; let b=3; function tag(s,n1,n2){ console.log(s[0]); console.log(s[1]); console.log(s[2]); console.log(n1); console.log(n2); console.log('ok!') } tag(['hello ',' world',''],5,6); tag`hello ${a+b} world ${a*b}`;
(6)String.raw()var message = SaferHTML`<p>${'成龙'} has sent you a message.</p>`; function SaferHTML(templateData) { var s = templateData[0]; for (var i = 1; i < arguments.length; i++) { var arg = String(arguments[i]); s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); s += templateData[i]; } return s; } $('#mydiv').html(message);
//String.raw方法,往往用来充当模板字符串的处理函数, //返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字符串。 console.log(String.raw`Hi\n${2+3}!`);//Hi\n5! //String.raw方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,
//方便下一步作为字符串来使用。 //String.raw方法也可以作为正常的函数使用。 //这时,它的第一个参数,应该是一个具有raw属性的对象,且raw属性的值应该是一个数组。 console.log(String.raw({raw:'andy'},1,2,3));//a1n2y3d console.log(String.raw({raw:['a','n','d','y']},1,2,3));//a1n2y3d
19,正则的使用
(1)RegExp构造函数
//RegExp构造函数 //es5中regexp有两种写法 //第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。 var reg = new RegExp('andy','i'); var reg = /andy/i; //第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。 var reg = new RegExp(/andy/i); var reg = /andy/i; //但是,此时,es5不允许使用第二个参数添加修饰符,否则会报错;如下: var reg = new RegExp(/andy/,'i'); //ES6改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。 //而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。 var reg = new RegExp(/andy/ig,'i').flags;//---此时i会覆盖ig;(2)字符串的正则方法
//字符串对象共有4个方法,可以使用正则表达式:match()、replace()、search()和split()。 //ES6将这4个方法,在语言内部全部调用RegExp的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp对象上。 //String.prototype.match 调用 RegExp.prototype[Symbol.match] //String.prototype.replace 调用 RegExp.prototype[Symbol.replace] //String.prototype.search 调用 RegExp.prototype[Symbol.search] //String.prototype.split 调用 RegExp.prototype[Symbol.split]
20,数值的扩展
(1)二进制和八进制的写法es6提供了二进制和八进制新的写法,分别使用前缀0b(或者0B)和0o(或者0O)表示;
(2)Number.isFinite(),Number.isNaN()为number对象提供了两个方法;它们与传统的全局方法//Number.isFinite()用来检查一个数值是否为有限的(finite) console.log(Number.isFinite(15));//true console.log(Number.isFinite(true));//false console.log(Number.isFinite('10'));//false //Number.isNaN()用来检查一个值是否为NaN。 console.log(Number.isNaN(0));//fasle console.log(Number.isNaN(NaN));//true console.log(Number.isNaN('10'));//false
isFinite()
和isNaN()
的区别在于,传统方法先调用Number()
将非数值的值转为数值,
再进行判断,而这两个新方法只对数值有效,非数值一律返回false
。
(3)Number.parseInt(), Number.parseFloat()为了逐步减少全局性方法,使得语言逐步模块化。ES6将全局方法parseInt()
和parseFloat()
,移植到Number对象上面,行为完全保持不变;(4)Number.isInteger()// ES5的写法 console.log(parseInt('12.34')); // 12 console.log(parseFloat('123.45#33')); // 123.45 // ES6的写法 console.log(Number.parseInt('12.34')); // 12 console.log(Number.parseFloat('123.45#33')); // 123.45
Number.isInteger()
用来判断一个值是否为整数。
需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以4和4.0被视为同一个值;(5)Number.EPSILONconsole.log(Number.isInteger(2));//true console.log(Number.isInteger(2.0));//true console.log(Number.isFinite(true));//false console.log(Number.isFinite(false));//false
(6)Math对象的扩展//ES6在Number对象上面,新增一个极小的常量Number.EPSILON //引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。 console.log(0.1+0.2);//0.30000000000000004 //但是如果这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果。 console.log(5.551115123125783e-17 < Number.EPSILON);//true //因此,Number.EPSILON的实质是一个可以接受的误差范围。
ES6在Math对象上新增了与数学相关的方法。所有这些方法都是静态方法,只能在Math对象上调用;
Math.trunc() --Math.trunc
方法用于去除一个数的小数部分,返回整数部分对于非数值,console.log(Math.trunc(4.1));//4 console.log(Math.trunc(-5,89));//-5
Math.trunc
内部使用Number
方法将其先转为数值;
对于空值和无法截取整数的值,返回NaN;console.log(Math.trunc('1234.56'));//1234 console.log(Math.trunc());//NaN console.log(Math.trunc('sa'));//NaN
Math.sign
方法用来判断一个数到底是正数、负数、还是零;
- 参数为正数,返回+1;
- 参数为负数,返回-1;
- 参数为0,返回0;
- 参数为-0,返回-0;
- 其他值,返回NaN。
Math.cbrt
方法用于计算一个数的立方根;对于非数值,
Math.cbrt
方法内部也是先使用Number
方法将其转为数值。console.log(Math.cbrt('8'));//2 console.log(Math.cbrt('andy'));//NaN
三角函数方法
Math.sinh(x) 返回x的双曲正弦(hyperbolic sine) Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine) Math.tanh(x) 返回x的双曲正切(hyperbolic tangent) Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine) Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine) Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)
21,数组的扩展
(1)Array.from()实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,//Array.from() //Array.from方法用于将两类对象转为真正的数组: //类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。 let arr = { '0':'a', '1':'b', '2':'c', length:3 }; let newArr = Array.from(arr); console.log(newArr);//['a','b','c']
以及函数内部的arguments
对象。Array.from
都可以将它们转为真正的数组。
Nodelist对象let li = document.querySelectorAll('li'); Array.from(li).forEach(function(m){ console.log(m); });
querySelectorAll
方法返回的是一个类似数组的对象,只有将这个对象转为真正的数组,才能使用forEach
方法。
arguments对象function createArr(n1,n2,n3){ var arr = Array.from(arguments); console.log(arr); } createArr(1,2,3);//[1,2,3]
let nameset = new Set(['a','b']); console.log(Array.from(nameset));//['a','b'] console.log(Array.from('andy'));//["a", "n", "d", "y"] //上面Set结构和字符串都具有iterator接口,因此可以被Array.from都能将其转化为数组;
//当然,如果参数本身就是一个数组,Array.from会返回一个一模一样的数组 console.log(Array.from([1,2,3,4]));//[1, 2, 3, 4]
//有一点,扩展运算法(...)也可以将某些数据结构转换为数组 //arguments对象 function num1(){ var args = [...arguments]; } //nodelist对象 [...document.querySelectorAll('li')]; //扩展运算符背后调用的是遍历接口(symbol.iterator),如果一个对象没有 //部署这个接口,则无法转换;Array.from还支持类似数组的对象.类似数组对象,本质上只有一点 //就是具备length属性;因此,任何具有length属性的对象,都可以通过Array.from方法转换为数组, //而此时扩展运算符就无法转换; console.log(Array.from({length:2}));//[undefined, undefined]
//Array.from还可以接受第二个参数,方法类似于map,用来对每个元素进行处理 //将处理后的值放入返回的数组; Array.from(arr,x => x*x); //等同于 Array.from(arr).map(x => x*x); console.log(Array.from([1,2,3,4], (x) => x*x));//[1, 4, 9, 16]
//下面的例子是取出一组DOM节点的文本内容 let _doc = document, _div = _doc.getElementsByClassName('names')[0], _b = _div.getElementsByTagName('b'); //map() let _names = Array.prototype.map.call(_b, s => s.textContent); console.log(_names);//["刘德华", "张学友", "郭富城", "黎明"] //Array.from() let _names1 = Array.from(_b,s => s.textContent); console.log(_names1);//["刘德华", "张学友", "郭富城", "黎明"]
//下面的例子会把数组中布尔值false转换为0 console.log(Array.from([1,,3,4],(n) => n || 0));//[1, 0, 3, 4]
22,Array.of
方法用于将一组值,转换为数组。console.log(Array.of(1,20,23));//[1, 20, 23] console.log(Array.of(34));//[34] console.log(Array.of(3).length);//1
//这个方法主要的目的,是为了弥补数组构造函数Array()的不足,因为参数个数的不同, //会导致Array()的行为差异; console.log(Array());//[] console.log(Array(2));//[] console.log(Array(1,2,3));//[1,2,3] //上面代码中,Array方法没有参数、一个参数、三个参数时,返回结果都不一样。 //只有当参数个数不少于2个时,Array()才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度
Array.of
基本上可以用来替代Array()
或new Array()
,并且不存在由于参数不同而导致的重载。它的行为非常统一。
console.log(Array.of());//[] console.log(Array.of(2));//[2] console.log(Array.of(1,2));//[1,2] console.log(Array.of(undefined));//[undefined]
23,数组实例的
数组实例的copyWithin
方法copyWithin
方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三个参数。
- target(必需):从该位置开始替换数据。
- start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
- end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
这三个参数都应该是数值,如果不是,会自动转为数值。
数组实例的console.log([1,2,3,4,5].copyWithin(0,3));//[4, 5, 3, 4, 5] //将3号位置替换到0位 console.log([1,2,3,4,5].copyWithin(0,3,4));//[4, 2, 3, 4, 5]
24,数组实例的find
方法find
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
。
console.log([1,2,-3,-4,5].find((n) => n<0));//-3 [1,2,5,20].find(function(value,index,arr){ return value > 10; });//20 //上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。 //数组实例的findIndex方法的用法与find方法非常类似, //返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。 console.log([34,22,-5,100].findIndex((n) => n<0));//2 //最后,这两个方法都可以发现NaN,弥补了数组方法的indexOf方法的不足; console.log([NaN].indexOf(NaN));//-1 console.log([NaN].findIndex(y => Object.is(NaN,y)));//0
25,
fill
方法使用给定值,填充一个数组。console.log(['a','b',1].fill('8'));//["8", "8", "8"] console.log(new Array(3).fill(8));//[8,8,8] //上面代码表明,fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。 //fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置 console.log([1,3,4,6].fill(8,1,3));//[1,8,8,6]
26,
entries()
,keys()
和values()
——用于遍历数组
ES6提供三个新的方法——entries()
,keys()
和values()
——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
for(let i of ['a','b',1].keys()){
console.log(i);//0,1,2
}
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);//0 'a' 1 'b'
}
27,includes()
console.log([1,2,3,4].includes(2));//true
console.log([1,2,3,4].includes(2,2));//false
//该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,
//如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始
//没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。
//indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,
//所以要去比较是否不等于-1,表达起来不够直观。
//二是,它内部使用严格相当运算符(===)进行判断,这会导致对NaN的误判。
console.log([NaN].indexOf(NaN));//-1
console.log([NaN].includes(NaN));//true
28,数组的空位
数组的空位指,数组的某一个位置没有任何值。比如,
Array
构造函数返回的数组都是空位。
注意,空位不是
undefined
,一个位置的值等于undefined
,依然是有值的。空位是没有任何值,in
运算符可以说明这一点。
ES6则是明确将空位转为
undefined
。Array.from
方法会将数组的空位,转为undefined
,也就是说,这个方法不会忽略空位。console.log(Array.from([,1,undefined]));//[undefined, 1, undefined]
由于空位的处理规则非常不统一,所以建议避免出现空位。