走进ES6的世界:基础语法入门指南2

简介: 走进ES6的世界:基础语法入门指南

6、数组的方法介绍

6.1、forEach()

forEach方法用于调用数组的每个元素,并将元素传递给回调函数。数组中的每个值都会调用回调函数。其语法如下:

array.forEach(function(currentValue, index, arr){}, thisValue)

该方法的第一个参数为回调函数,是必传的。它有三个参数:

* currentValue;必须,当前元素

* index:可选,当前元素的索引值

* arr: 可选,当前元素所属的数组对象

let arr=[1, 2, 3, 4, 5]
arr.forEach((item, index, arr) => {
console.log(index + ':' + item)
})

该方法的第二个参数,用来绑定回调函数内部this变量(前提是回调函数不能是箭头函数,因为箭头函数没有this),是可选的;

let arr=[1, 2, 3, 4, 5]
let arr1=[9, 8, 7, 6, 5]
arr.forEach(function(item, index, arr) {
console.log(this[index]) // 9 8 7 6 5
}, arr1)
// this 就是 arr1数组

小结:

* forEach方法不会改变原数组,也没有返回值

* forEach无法使用break, continue跳出循环,使用return 时,效果和在for循环中使用 continue 一致;

* forEach方法无法遍历对象,仅适用于数组的遍历

6.2、map()

map()方法会返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。该方法按照原始数组元素顺序依次处理元素。其语法如下:

array.map(function(currentValue, index, arr){}, thisValue)
1

该方法的第一个参数为回调函数,是必传的。它有三个参数:

* currentValue:必须,当前元素的值

* index: 可选,当前元素的索引值

* arr: 可选,当前元素所属的数组对象

let arr = [1, 2, 3]
arr.map(function(item) {
return item + 1
})
// 输出结果:[2, 3, 4]

同样,它也有第二个参数,用来绑定参数函数内部的this变量。是可选的;

let arr = ['a', 'b', 'c'];
[1, 2].map(function (e) {
return this[e];
}, arr)
// 输出结果: ['b', 'c'] this就是arr

该方法可以链式调用:

let arr = [1, 2, 3]
arr.map(item => item + 1).map(item => item +1)
// 第一个map的输出结果:[2, 3, 4]
// 最终输出结果: [3, 4, 5]

小结:

* map方法不会对空数据进行检测

* map方法遍历数组是会返回一个新数组,不会改变原数组

* map方法无法遍历对象,仅适用于数组的遍历

6.3、filter()

filter()方法用于过滤数组,满足条件的元素会被返回。它的第一个参数是回调函数,所有数组元素依次执行该函数,返回结果为true的元素会被返回,没有符合条件的元素,则返回空数组。其语法如下:

array.filter(function(currentValue,index,arr), thisValue)

该方法的第一个参数是回调函数,是必传的。它有三个参数:

* currentValue:必须,当前元素的值

* index:可选,当前元素的索引值

* arr:可选,当前元素所属的数组对象

const arr = [1, 2, 3, 4, 5]
arr.filter(item => item > 2)
// 输出结果:[3, 4, 5]

同样,它也有第二个参数,用来绑定参数函数内部的this变量。是可选的。

新知识:

可以使用filter()方法来移除数组中的 undefined、null、NAN等值

let arr = [1, undefined, 2, null, 3, false, '', 4, 0]
arr.filter(Boolean)
等价于:
array.filter((item) => {return Boolean(item)})
// 输出结果:[1, 2, 3, 4]

小结:

* filter方法会返回一个新数组,不会改变原数组

* filter方法不会对空数组进行检测

* filter方法仅适用于检测数组

6.4、some()、every()

some() 方法会对数组中的每一项进行遍历,只要有一个元素符合条件,就返回true,且剩余的元素不会再进行检测,否则就返回false。

every() 方法会对数组中的每一项进行遍历,只有所有元素都符合条件时,才返回true,如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。其语法如下:

array.some(function(currentValue,index,arr),thisValue)
array.every(function(currentValue,index,arr), thisValue)

两个方法的第一个参数为回调函数,是必传的,它有三个参数:

* currentValue:必须,当前元素的值

* index: 可选,当前元素的索引值

* arr: 可选,当前元素所属的数组对象

let arr = [1, 2, 3, 4, 5]
arr.some(item => item > 4)
// 输出结果: true
let arr = [1, 2, 3, 4, 5]
arr.every(item => item > 0)
// 输出结果: true

小结:

* 两个方法会返回一个布尔值,且都不会改变原数组

* 两个方法都不会对空数组进行检测

* 两个方法都适用于检测数组

6.5、find(),findIndex(),findLast(),findLastIndex()

数组实例的find()方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

[1, 4, -5, 10].find((n) => n < 0)
// -5

上面代码找出数组中第一个小于 0 的成员。

[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10

上面代码中,find()方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

数组实例的findIndex()方法的用法与find()方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);    // 26

上面的代码中,find()函数接收了第二个参数person对象,回调函数中的this对象指向person对象。

另外,这两个方法都可以发现NaN,弥补了数组的indexOf()方法的不足。

[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0

上面代码中,indexOf()方法无法识别数组的NaN成员,但是findIndex()方法可以借助Object.is()方法做到。

find()和findIndex()都是从数组的0号位,依次向后检查。ES2022 新增了两个方法findLast()和findLastIndex(),从数组的最后一个成员开始,依次向前检查,其他都保持不变。

const array = [
  { value: 1 },
  { value: 2 },
  { value: 3 },
  { value: 4 }
];
array.findLast(n => n.value % 2 === 1); // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1); // 2

上面示例中,findLast()和findLastIndex()从数组结尾开始,寻找第一个value属性为奇数的成员。结果,该成员是{ value: 3 },位置是2号位。

6.6、includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。

if (arr.indexOf(el) !== -1) {
  // ...
}

indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。

[NaN].indexOf(NaN)
// -1

includes使用的是不一样的判断算法,就没有这个问题。

[NaN].includes(NaN)
// true

7、函数的扩展

7.1、函数参数的默认值

7.1.1、基本用法

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World

上面代码检查函数log()的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。

if (typeof y === 'undefined') {
  y = 'World';
}

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

使用参数默认值时,函数不能有同名参数。

// 不报错
function foo(x, x, y) {
  // ...
}
// 报错
function foo(x, x, y = 1) {
  // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

7.1.2、参数默认值的位置

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

// 例一
function f(x = 1, y) {
  return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined]
f(, 1) // 报错
f(undefined, 1) // [1, 1]

上面代码中,有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。

如果传入undefined,将触发该参数等于默认值,null则没有这个效果。

function foo(x = 5, y = 6) {
  console.log(x, y);
}
foo(undefined, null)
// 5 null

上面代码中,x参数对应undefined,结果触发了默认值,y参数等于null,就没有触发默认值。

7.2、箭头函数

7.2.1、基本用法

ES6 允许使用“箭头”(=>)定义函数。
var f = v => v;
// 等同于
var f = v=> v;

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。


var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

var sum = (num1, num2) => { return num1 + num2; }

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

7.2.2、使用注意点

箭头函数有几个使用注意点。

(1)箭头函数没有自己的this对象。

(2)不可以当作构造函数,也就是说,不可以对箭头函数使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

上面三点中,最重要的是第一点。对于普通函数来说,内部的this指向函数运行时所在的对象,但是这一点对箭头函数不成立。它没有自己的this对象,内部的this就是定义时上层作用域中的this(就是定义该函数时所在的作用域指向的对象)。也就是说,箭头函数内部的this指向是固定的,相比之下,普通函数的this指向是可变的。

下面例子是回调函数分别为箭头函数和普通函数,对比它们内部的this指向。

function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
    this.s2++;
  }, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

箭头函数实际上可以让this指向固定化,绑定this使得它不再可变,这种特性很有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。

var handler = {
  id: '123456',
  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false);
  },
  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id);
  }
};

上面代码的init()方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。如果回调函数是普通函数,那么运行this.doSomething()这一行会报错,因为此时this指向document对象。

7.2.3、不适用场合

由于箭头函数使得this从“动态”变成“静态”,下面两个场合不应该使用箭头函数。

第一个场合是定义对象的方法,且该方法内部包括this。

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。

第二个场合是需要动态this的时候,也不应使用箭头函数。

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。

8、对象的扩展

8.1、属性的简洁表示法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};

上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。

除了属性简写,方法也可以简写。

const o = {   
  method() {
    return "Hello!";
  }
};
// 等同于
const o = {
  method() {
    return "Hello!";
  }
};

8.2、对象的新增方法

8.2.1、Object.is()

Object.is是用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

8.2.2、Object.assign()

Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。

注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}


目录
相关文章
|
5天前
|
机器学习/深度学习 数据可视化 数据挖掘
探索Python之美:从基础到进阶的编程之旅
【10月更文挑战第40天】在编程的世界里,Python以其简洁明了的语法和强大的功能库赢得了无数开发者的青睐。本篇文章将带你领略Python的魅力,从基础的语法入门到高级特性的应用,我们一同踏上这段充满智慧与挑战的编程之旅。
|
5月前
|
自然语言处理 编译器 C语言
C++语言入门教程
C++语言入门教程
|
6月前
|
存储 索引 Python
《Python 简易速速上手小册》第2章:Python 基础语法和概念(2024 最新版)
《Python 简易速速上手小册》第2章:Python 基础语法和概念(2024 最新版)
44 0
|
前端开发
走进ES6的世界:基础语法入门指南4
走进ES6的世界:基础语法入门指南
61 0
|
6月前
|
存储 程序员 开发工具
Python 进阶指南(编程轻松进阶):十四、实践项目
Python 进阶指南(编程轻松进阶):十四、实践项目
52 0
|
JSON 前端开发 JavaScript
走进ES6的世界:基础语法入门指南3
走进ES6的世界:基础语法入门指南
49 0
|
JavaScript 前端开发 安全
走进ES6的世界:基础语法入门指南1
走进ES6的世界:基础语法入门指南
78 0
|
存储 安全 编译器
C++入门语法基础
C++入门语法基础
83 0
C++入门语法基础
|
机器学习/深度学习 人工智能 运维
开讲啦:《Python3进阶实战》
想必你已经知道了Python这门编程语言的火爆程度,曾几何时,你可能会见到类似 Python将被纳入高考内容、计算机等级考试新增Python程序设计、人工智能时代,你还不会Python嘛? 等的新闻头条,毫无疑问,人工智能时代,Python将占据不可或缺的地位!
开讲啦:《Python3进阶实战》