ES6转ES5,javascript--第一季

1,for循环的特别之处,即是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。

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是分离的;

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;
}
//因此衍生的一个问题是typeof 操作符的运用是否安全

typeof aa;//referenceError
let aa;

//然而,如果一个变量没有let声明,反而不会出错;

typeof bb;//undefined
在没有let之前,typeof运算符是安全的;现在在es6中则会出现问题;这一设计原则是为了让大家,变量一定要在声明之后使用
否则会报错;

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,块级作用域

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<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();
而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编译的话,就提示报错

    }

8,const命令

const声明的常量值,不允许改变;
const a = 1;
console.log(a);

a = 3;//报错
也就是说,一但声明了常量值,就必须赋值;
const a;//必须赋值,否则会报错
当然了,const和let作用域一样,必须在块级才生效;同时,不可重新赋值;

9,es6声明变量的6种方法;

1,var
2,function
3,let
4,const
5,import
6,class

10,顶层对象的属性;

顶层对象在浏览器中指的是window,在node环境中指的是global;
顶层对象的属性和全局变量关联;
window.a= 1;
console.log(a);//1
而es6规定,let,const,class声明的全局变量不属于顶层对象;也就是说从es6开始,全局变量逐步与顶层对象的属性脱离;

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
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
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
实际上,对象的解构赋值的内部机制,先找到同名属性,然后再赋给对应的变量;
正在被赋值的是后者,不是前者;
let {foo:baz}= {foo:'1',bar:'2'};
console.log(baz);//1
console.log(foo);//报错,foo is undefined
上面foo是匹配模式,bar才是变量;也就是说真正被赋值的是bar,不是foo;
这种写法,变量的声明和赋值是一体的;对于let和const来说,变量不能重新声明,否则会报错;
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');

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
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。
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];
let {x:(y)} = {};
(2)函数参数重不能带圆括号
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)交互变量的值
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数据的值;
(5)函数参数的默认值
(6)遍历map结构
//部署了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)输入模块的制定方法

18,字符串的扩展

//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
}

(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次。
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()
ES7引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。
padStart() 用于头部补全, padEnd() 用于尾部补全。
(4)模板字符串
$('#mydiv').append(`
    荣贺,<b>${'成龙'}</b>拿了奥斯卡奖,
    对此,我们庆幸,他说,<span>${'很高兴自己是个中国人!'}</span>
`);
模板字符串(template string)是增强版的字符串,用反引号(`)标识。
它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
// 字符串中嵌入变量
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>
`);
(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`<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);
(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

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对象上面,行为完全保持不变;
// 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()用来判断一个值是否为整数。
需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以4和4.0被视为同一个值;
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对象的扩展
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()
//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都可以将它们转为真正的数组。
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]
由于空位的处理规则非常不统一,所以建议避免出现空位。




















 
 
 
 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值