for (var i = 0; i < 3; i++) {
var i = 'andy';
console.log('i='+i);
}
结果是:i = andy;(打印一次然后退出循环)
我们看下let语句
for (let i = 0; i < 3; i++) {
let i = 'andy';
console.log(i);
}
结果是:
andy
andy
andy
输出了3次,这表明函数内部的变量i和外部的变量i是分离的;
console.log(aa);// undefiend(变量提升,但是没有值,所以会打印出undefined)
var aa = 2;
console.log(bb);// 引用错误(let不会发生变量提升,说明在声明之前,bb是不存在的,此时会抛出错误);
let bb = 2;
//只要在快级作用域中存在let命令,它所声明的变量就绑定了在这个区域,不再受外部的影响;
var aa = 'andy';
if(aa){
aa = 'jack';// 引用错误
let aa;
}
//因此衍生的一个问题是typeof 操作符的运用是否安全
typeof aa;//referenceError
let aa;
//然而,如果一个变量没有let声明,反而不会出错;
typeof bb;//undefined
在没有let之前,typeof运算符是安全的;现在在es6中则会出现问题;这一设计原则是为了让大家,变量一定要在声明之后使用
否则会报错;
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
var x=x;// 不会报错
let x=x;//使用let声明变量时,只要变量在还没有声明完成前使用,就会报错
function aa(){
let a=2;
var a=1;//报错
}
aa();
因此不能在函数内部重新声明参数;
function aa(name){
let name;//报错
}
function aa(name){
{
let name;//正确
}
}
var mydate = new Date();
function create(){
console.log(mydate);
if(false){
var mydate = 'andy';
}
}
create();// undefined
变量提升导致内部的mydate覆盖外层的mydate变量;
var a = 'andy';
for(var i =0;i
而es6规定了块级作用域
function a(){
let i = 1;
if(true){
let i =2;
console.log(i);//2
}
console.log(i);//1---外层变量不受内部变量的影响
}
a();
而es6也允许块级作用域的嵌套
{
{
{
let name = 'andy';
console.log(name);//andy
}
console.log(name);//为空或者报错(看浏览器)
}
}
(function(){
let name = 'andy';
})();
{
let name = 'andy';
}
{
//函数声明语句
let name = 'andy';
function f(){
return name;
}
}
{
//函数表达式---推荐
let names = 'andy';
let f = function(){
return names;
}
}
提示:es6允许在块级作用域中声明函数,前提是必须又大括号,否则会报错;
if(true){
function f(){
}
}
if(true)
function f(){//报错,如果用es6编译的话,就提示报错
}
const a = 1;
console.log(a);
a = 3;//报错
也就是说,一但声明了常量值,就必须赋值;
const a;//必须赋值,否则会报错
当然了,const和let作用域一样,必须在块级才生效;同时,不可重新赋值;
1,var
2,function
3,let
4,const
5,import
6,class
window.a= 1;
console.log(a);//1
而es6规定,let,const,class声明的全局变量不属于顶层对象;也就是说从es6开始,全局变量逐步与顶层对象的属性脱离;
//以前,为变量赋值,只能指定值;
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
let [x, y, ...z] = ['a'];
console.log(x,y,z);//a,undefiend,[]
当然,如果解构不成功,就会返回undefiend;
let [foo] = [];
let [bar, foos] = [1];
console.log(foo,bar,foos);//undefined,1,undefined
另外,
let [age] = 20;
console.log(age);//报错
而对于Set结构,也同样可以使用数组的结构赋值
let [x,y,z] = new Set(['a','b','c']);
console.log(x,y,z);//a,b,c
重点:
迭代器iterators
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=');//空值
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);//报错
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
实际上,对象的解构赋值的内部机制,先找到同名属性,然后再赋给对应的变量;
let {foo:baz}= {foo:'1',bar:'2'};
console.log(baz);//1
console.log(foo);//报错,foo is undefined
上面foo是匹配模式,bar才是变量;也就是说真正被赋值的是bar,不是foo;
let foo;
let {foo} = {foo:'1'};//报错
//和数组一样,解构也可以用于嵌套结构的对象;
let obj ={
p:[
'hello',
{
y:'world'
}
]
};
let {p:[x,{y}]}= obj;
console.log(x,y);//hello,world
此时,p是模式,不是变量;
({} = [true,false]);
({}='abc');
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
//解构赋值时,如果等号右边是数值和布尔值,则会先转为对象
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;//报错
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]
let [(a)] = [1];
let {x:(y)} = {};
(2)函数参数重不能带圆括号
function f([(a)]){
console.log(a);
}
({m:n}) = {m:42};
([x]) = [5];
let b;
let d;
[(b)] = [2];
console.log(b);
({p:(d)} = {});
console.log(d);
那么,变量的解构赋值都有哪些用途呢?
let x=1;
let y=2;
[x,y] = [y,x];
console.log(x,y);//2,1
(2)从函数返回多个值
//函数只能返回一个值,如果要返回多个值,只能将他们放在数组或对象里返回.
//返回一个数组
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
(3)函数参数的定义
//解构赋值可以很方便的将一组参数与变量名对应起来
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
(4)提取json数据
let myJson = {
name:'andy',
age:28,
school:'香港城市大学',
className:['刘德华','梁朝伟']
};
let {name,age,school,className:names} = myJson;
console.log(name,age,school,names);
因此,可以快速提取json数据的值;
//部署了iterator接口的对象,都可以使用for..of遍历,Map结构原生支持iterator接口,
//配合变量的解构赋值,获取键名和键值就方便多了;
var map = new Map();
map.set('first','hello');
map.set('second','andy');
for(let [key,value] of map){
console.log(key+' is '+value)
}
(7)输入模块的制定方法
//js允许\uxxxx形式表示一个字符,其中'xxxx'表示字符的码点
'\u0061';
//但是,这种表示法只限于\u0000——\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表达。
"\uD842\uDFB7";
"\u20BB7";//' 7'
//上面代码表示,如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript会理解成\u20BB+7。
// 由于\u20BB是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。
//ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}";
ES6为字符串添加了遍历器接口,使得字符串可以被
for...of
循环遍历。
for(let codePoint of 'foo'){
console.log(codePoint);//f,o,o
}
//传统上,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个位置直到字符串结束。
repeat
方法返回一个新字符串,表示将原字符串重复n
次。
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
(3)padStart(),padEnd()
padStart()
用于头部补全,
padEnd()
用于尾部补全。
$('#mydiv').append(`
荣贺,${'成龙'}拿了奥斯卡奖,
对此,我们庆幸,他说,${'很高兴自己是个中国人!'}
`);
// 字符串中嵌入变量
var name = "andy", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);
//模板字符串中嵌入变量,需要将变量名写在${}之中。
//如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
var greeting = `\`You\` World!`;
console.log(greeting);//`You` World!
//如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
$('#mydiv').html(`
-
加油
-
中国!
`);
(5)标签模板
//它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。
// 这被称为“标签模板”功能(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}`;
“标签模板”的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容。
var message =
SaferHTML`${'成龙'} has sent you a message.
`;
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, ">");
s += templateData[i];
}
return s;
}
$('#mydiv').html(message);
(6)String.raw()
//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
//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;
//字符串对象共有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]
(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
。为了逐步减少全局性方法,使得语言逐步模块化。ES6将全局方法
parseInt()
和
parseFloat()
,移植到Number对象上面,行为完全保持不变;// 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
(4)Number.isInteger()Number.isInteger()
用来判断一个值是否为整数。console.log(Number.isInteger(2));//true
console.log(Number.isInteger(2.0));//true
console.log(Number.isFinite(true));//false
console.log(Number.isFinite(false));//false
(5)Number.EPSILON//ES6在Number对象上面,新增一个极小的常量Number.EPSILON
//引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。
console.log(0.1+0.2);//0.30000000000000004
//但是如果这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果。
console.log(5.551115123125783e-17 < Number.EPSILON);//true
//因此,Number.EPSILON的实质是一个可以接受的误差范围。
(6)Math对象的扩展Math.trunc
方法用于去除一个数的小数部分,返回整数部分console.log(Math.trunc(4.1));//4
console.log(Math.trunc(-5,89));//-5
对于非数值,
Math.trunc
内部使用
Number
方法将其先转为数值;console.log(Math.trunc('1234.56'));//1234
console.log(Math.trunc());//NaN
console.log(Math.trunc('sa'));//NaN
Math.sign
方法用来判断一个数到底是正数、负数、还是零;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)
//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']
实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,arguments
对象。
Array.from
都可以将它们转为真正的数组。let li = document.querySelectorAll('li');
Array.from(li).forEach(function(m){
console.log(m);
});
querySelectorAll
方法返回的是一个类似数组的对象,只有将这个对象转为真正的数组,才能使用
forEach
方法。
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]
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]
copyWithin
方法copyWithin
方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三个参数。
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]
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
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]
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'
}
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
Array
构造函数返回的数组都是空位。
undefined
,一个位置的值等于undefined
,依然是有值的。空位是没有任何值,in
运算符可以说明这一点。undefined
。Array.from
方法会将数组的空位,转为undefined
,也就是说,这个方法不会忽略空位。console.log(Array.from([,1,undefined]));//[undefined, 1, undefined]
由于空位的处理规则非常不统一,所以建议避免出现空位。