admin 管理员组

文章数量: 1087678

JS对象 与 数组

使用数组存储不同类型的数据

以下是最简单的数组(Array)示例: 这是一个一维数组(one-dimensional array),它只有一层,或者说它里面没有包含其它数组。 可以观察到,这个数组中只包含了布尔值(booleans)、字符串(strings)、数字(numbers)以及 JavaScript 中的其他数据类型:

let simpleArray = ['one', 2, 'three', true, false, undefined, null];
console.log(simpleArray.length);

调用 console.log 显示 7。

所有数组都有一个表示长度的属性,我们可以通过 Array.length 来访问它。 下面是一个关于数组的更复杂的例子。 这是一个多维数组 (multi-dimensional Array),或者说是一个包含了其他数组的数组。 可以注意到,在它的内部还包含了 JavaScript 中的对象(objects)结构。 我们会在后面的小节中讨论该数据结构,但现在你只需要知道数组能够存储复杂的对象类型数据。

let complexArray = [[{one: 1,two: 2},{three: 3,four: 4}],[{a: "a",b: "b"},{c: "c",d: "d"}]
];

使用 push() 和 unshift() 为数组添加元素

数组的长度与数组能包含的数据类型一样,都是不固定的。 数组可以包含任意数量的元素,可以不限次数地往数组中添加元素或者从中移除元素。 总之,数组是可变的(mutable)。 在本挑战中,我们要学习两种修改数组的方法:Array.push()Array.unshift()

这两个方法都接收一个或多个元素作为参数,并会将参数中的元素添加到该数组中。 push() 方法会将元素插入到数组的末尾,而 unshift() 方法会将元素插入到数组的开头。 请看以下例子:

let twentyThree = 'XXIII';
let romanNumerals = ['XXI', 'XXII'];romanNumerals.unshift('XIX', 'XX');

romanNumerals 的值就变成了 ['XIX', 'XX', 'XXI', 'XXII']

romanNumerals.push(twentyThree);

romanNumerals 的值现在就变成了 ['XIX', 'XX', 'XXI', 'XXII', 'XXIII']。 请注意这里,我们也可以使用变量作为参数,这让我们在动态修改数组数据时更加灵活。


使用 pop() 和 shift() 从数组中删除元素

push() 和 unshift() 都有一个与它们作用相反的函数:pop() 和 shift()。 与插入元素相反,pop() 会从数组的末尾移除一个元素,而 shift() 会从数组的开头移除一个元素。 pop() 和 shift() 与 push() 和 unshift() 的关键区别在于,用于删除元素的方法不接收参数,而且每次只能删除数组中的一个元素。

让我们来看以下的例子:

let greetings = ['whats up?', 'hello', 'see ya!'];greetings.pop();

greetings 值为 ['whats up?', 'hello']

greetings.shift();

greetings 值为 ['hello']

这些用于删除数组元素的方法会返回被删除的元素:

let popped = greetings.pop();

greetings 值为 [],popped 值为 hello。


使用 splice() 删除元素

在之前的挑战中,我们已经学习了如何用 shift() 和 pop() 从数组的开头或末尾移除元素。 但如果我们想删除数组中间的一个元素, 或者想一次删除多个元素,该如何操作呢? 这时候我们就需要使用 splice() 方法了, splice() 可以让我们从数组中的任意位置连续删除任意数量的元素。

splice() 最多可以接受 3 个参数,但现在我们先关注前两个。 splice() 接收的前两个参数是整数,表示正在调用 的splice() 数组中的元素的索引或位置。 别忘了,数组的索引是从 0 开始的,所以我们要用 0 来表示数组中的第一个元素。 splice() 的第一个参数代表从数组中的哪个索引开始移除元素,而第二个参数表示要从数组中的这个位置开始删除多少个元素。 例如:

let array = ['today', 'was', 'not', 'so', 'great'];array.splice(2, 2);

这里我们移除 2 个元素,首先是第三个元素(索引为 2)。 array 会有值 ['today', 'was', 'great']

splice() 不仅会修改调用该方法的数组,还会返回一个包含被移除元素的数组:

let array = ['I', 'am', 'feeling', 'really', 'happy'];let newArray = array.splice(3, 2);

newArray 值为 ['really', 'happy']


使用 splice() 添加元素

还记得在上个挑战中我们提到 splice() 方法最多可以接收 3 个参数吗? 第三个参数可以是一个或多个元素,这些元素会被添加到数组中。 这样,我们能够便捷地将数组中的一个或多个连续元素换成其他的元素。

const numbers = [10, 11, 12, 12, 15];
const startIndex = 3;
const amountToDelete = 1;numbers.splice(startIndex, amountToDelete, 13, 14);
console.log(numbers);

第二个 12 已被删除,我们在同一索引处添加 13 和 14。 numbers 数组现在将会是 [ 10, 11, 12, 13, 14, 15 ]。

在上面的代码中,数组一开始包含了若干数字。 接着,我们调用 splice() 方法,索引为 (3) 的地方开始删除元素,删除的元素数量是 (1)。然后,(13, 14) 是在删除位置插入的元素。 可以在 amountToDelete 后面传入任意数量的元素(以逗号分隔),每个都会被插入到数组中。


使用 slice() 复制数组元素

接下来我们要介绍 slice() 方法。 slice() 不会修改数组,而是会复制,或者说提取(extract)给定数量的元素到一个新数组
slice() 只接收 2 个输入参数:第一个是开始提取元素的位置(索引),第二个是提取元素的结束位置(索引)。 提取的元素中不包括第二个参数所对应的元素。 如下示例:

let weatherConditions = ['rain', 'snow', 'sleet', 'hail', 'clear'];let todaysWeather = weatherConditions.slice(1, 3);

todaysWeather 值为 [‘snow’, ‘sleet’],weatherConditions 值仍然为 ['rain', 'snow', 'sleet', 'hail', 'clear']

在上面的代码中,我们从一个数组中提取了一些元素,并用这些元素创建了一个新数组。


使用展开运算符复制数组

slice() 可以让我们从一个数组中选择一些元素来复制到新数组中,而 ES6 中又引入了一个简洁且可读性强的语法:展开运算符(spread operator),它能让我们方便地复制数组中的所有元素。 展开语法写出来是这样:…

我们可以用展开运算符来复制数组:

let thisArray = [true, true, undefined, false, null];
let thatArray = [...thisArray];

thatArray 等于 [true, true, undefined, false, null]。 thisArray 保持不变, thatArray 包含与 thisArray 相同的元素。

function copyMachine(arr, num) {let newArr = [];while (num >= 1) {// 只修改这一行下面的代码newArr.push([...arr])// 只修改这一行上面的代码num--;}return newArr;
}console.log(copyMachine([true, false, true], 2));

使用展开运算符合并数组

展开语法(spread)的另一个重要用途是合并数组,或者将某个数组的所有元素插入到另一个数组的任意位置。 我们也可以使用 ES5 的语法连接两个数组,但只能让它们首尾相接。 而展开语法可以让这样的操作变得极其简单:

let thisArray = ['sage', 'rosemary', 'parsley', 'thyme'];let thatArray = ['basil', 'cilantro', ...thisArray, 'coriander'];

thatArray 会有值 ['basil', 'cilantro', 'sage', 'rosemary', 'parsley', 'thyme', 'coriander']

使用展开语法,我们就可以很方便的实现一个用传统方法会写得很复杂且冗长的操作。


使用 map 方法从数组中提取数据

让我们从一些简单的数组函数开始,这些函数是数组对象原型上的方法。 在本练习中,我们来了解下数组的 map 方法(即 Array.prototype.map())。

请记住,map方法是迭代数组中每一项的方式之一。 在对每个元素应用回调函数后,它会创建一个新数组(不改变原来的数组)。 它这样做时没有改变原始数组。

当调用回调函数时,传入了三个参数。 第一个参数是当前正在处理的数组项。 第二个参数是当前数组项的索引值,第三个参数是在其上调用 map 方法的数组。

看下在 users 上使用 map 方法的例子,返回了一个新数组只包含了用户的名字。 为了简化,例子里只使用了回调函数的第一个参数。

const users = [{ name: 'John', age: 34 },{ name: 'Amy', age: 20 },{ name: 'camperCat', age: 10 }
];const names = users.map(user => user.name);
console.log(names);

控制台将显示值 [ ‘John’, ‘Amy’, ‘camperCat’ ]。

watchList 数组保存了包含一些电影信息的对象。 在 watchList 上使用 map,将一个新的对象数组赋值给 ratings 变量。 新数组中的每个电影都只能有一个值为电影名称的 title 键,和一个值为 IMDB 评级的 rating 键。

var ratings = [];
ratings = watchList.map(watch => {let obj = {}obj.title = watch.Titleobj.rating = watch.imdbRatingreturn obj
})// 只修改这一行上面的代码console.log(JSON.stringify(ratings));

使用 filter 方法从数组中提取数据

另一个有用的数组方法是 filter()(即 Array.prototype.filter())。

filter 接收一个回调函数,将回调函数内的逻辑应用于数组的每个元素,新数组包含根据回调函数内条件返回 true 的元素。 换言之,它根据传递给它的函数过滤数组。 和 map 一样,filter 不会改变原始数组。

回调函数接收三个参数。 第一个参数是当前正在被处理的元素。 第二个参数是这个元素的索引,第三个参数是在其上调用 filter 方法的数组。

看下在 users 上使用 filter 方法的例子,返回了一个包含了 30 岁以下的用户新数组。 为了简化,例子里只使用了回调函数的第一个参数。

const users = [{ name: 'John', age: 34 },{ name: 'Amy', age: 20 },{ name: 'camperCat', age: 10 }
];const usersUnder30 = users.filter(user => user.age < 30);
console.log(usersUnder30); 

控制台将显示值 [ { name: 'Amy', age: 20 }, { name: 'camperCat', age: 10 } ]


map,filter实例

传递一个包含实数的数组给函数时,函数应返回一个新的数组,只包含正整数(小数不是整数)的平方值, 例如 [-3, 4.8, 5, 3, -3.2] 这样一个包含实数的数组。

const squareList = arr => {// 只修改这一行下面的代码let middle = arr.filter(item => {if (Number.isInteger(item) && item > 0) {return item} })arr = middle.map(item => Math.pow(item,2))return arr;// 只修改这一行上面的代码
};const squaredIntegers = squareList([-3, 4.8, 5, 3, -3.2]);
console.log(squaredIntegers);

使用 reduce 方法分析数据

reduce()(即Array.prototype.reduce()),是 JavaScript 所有数组操作中最常用的方法。 几乎可以用reduce方法解决所有数组处理问题。

reduce方法是处理数组更通用的方式,而且filter和map方法都可以当作是reduce的特殊实现。 reduce方法遍历数组中的每个项目并返回单个值(即字符串、数字、对象、数组)。 这是通过在每次迭代中调用一个回调函数来实现的。

回调函数接受四个参数。 第一个参数称为叠加器,它是上一次迭代中回调函数的返回值,第二个参数是当前正在处理的数组元素,第三个参数是该参数的索引,第四个参数是在其上调用 reduce 方法的数组。

除了回调函数,reduce 还有一个额外的参数做为叠加器的初始值。 如果没有第二个参数,会跳过第一次迭代,第二次迭代给叠加器传入数组的第一个元素。

见下面的例子,给 users 数组使用 reduce 方法,返回所有用户数组的和。 为了简化,例子仅使用了回调函数的第一个参数和第二个参数。

const users = [{ name: 'John', age: 34 },{ name: 'Amy', age: 20 },{ name: 'camperCat', age: 10 }
];const sumOfAges = users.reduce((sum, user) => sum + user.age, 0);
console.log(sumOfAges);

这里控制台将显示值 64。

在另一个例子里,查看如何返回一个包含用户名称做为属性,其年龄做为值的对象。

const users = [{ name: 'John', age: 34 },{ name: 'Amy', age: 20 },{ name: 'camperCat', age: 10 }
];const usersObj = users.reduce((obj, user) => {obj[user.name] = user.age;return obj;
}, {});
console.log(usersObj);

控制台将显示值 { John: 34, Amy: 20, camperCat: 10 }


使用 join 方法将数组组合成字符串

join 方法用来把数组中的所有元素放入一个字符串。 并通过指定的分隔符参数进行分隔。

举个例子:

const arr = ["Hello", "World"];
const str = arr.join(" ");

str 的值应该是字符串 Hello World。


使用 every 方法检查数组中的每个元素是否符合条件

every 方法用于检测数组中所有元素是否都符合指定条件。 如果所有元素满足条件,返回布尔值 true,反之返回 false。

举个例子,下面的代码检测数组 numbers 的所有元素是否都小于 10:

const numbers = [1, 5, 8, 0, 10, 11];numbers.every(function(currentValue) {return currentValue < 10;
});

every 方法在这里会返回 false。

function checkPositive(arr) {// 只修改这一行下面的代码if(arr.every(item => item > 0)) {return true} else {return false}// 只修改这一行上面的代码
}checkPositive([1, 2, 3, -4, 5]);

使用 some 方法检查数组中是否有元素是否符合条件

some 方法用于检测数组中任何元素是否满足指定条件。 如果有一个元素满足条件,返回布尔值 true,反之返回 false。

举个例子,下面的代码检测数组numbers中是否有元素小于 10:

const numbers = [10, 50, 8, 220, 110, 11];numbers.some(function(currentValue) {return currentValue < 10;
});

some 方法将返回 true。


使用 sort 方法按字母顺序给数组排序

sort 方法可以根据回调函数对数组元素进行排序。

举个例子:

function ascendingOrder(arr) {return arr.sort(function(a, b) {return a - b;});
}ascendingOrder([1, 5, 2, 3, 4]);

这将返回值 [1, 2, 3, 4, 5]。

function reverseAlpha(arr) {return arr.sort(function(a, b) {return a === b ? 0 : a < b ? 1 : -1;});
}reverseAlpha(['l', 'h', 'z', 'b', 's']);

这将返回值 [‘z’, ‘s’, ‘l’, ‘h’, ‘b’]。

JavaScript 的默认排序方法是 Unicode 值顺序排序,有时可能会得到意想不到的结果。 因此,建议提供一个回调函数来指定如何对数组项目排序。 这个回调函数通常叫做 compareFunction,它根据 compareFunction 的返回值决定数组元素的排序方式: 如果两个元素 a 和 b,compareFunction(a,b) 返回一个比 0 小的值,那么 a 会在 b 的前面。 如果两个元素 a 和 b,compareFunction(a,b) 返回一个比 0 大的值,那么 b 会在 a 的前面。 如果两个元素 a 和 b,compareFunction(a,b) 返回等于 0 的值,那么 a 和 b 的位置保持不变。


将键值对添加到对象中

对象(object)本质上是键值对(key-value pair)的集合。 或者说,一系列被映射到唯一标识符的数据就是对象;习惯上,唯一标识符叫做属性(property)或者键(key);数据叫做值(value)。 让我们来看一个简单的例子:

const tekkenCharacter = {player: 'Hwoarang',fightingStyle: 'Tae Kwon Doe',human: true
};

上面的代码定义了一个叫做 tekkenCharacter 的“铁拳”游戏人物对象。 它有三个属性,每个属性都对应一个特定的值。 如果我们想为它再添加一个叫做 origin 的属性,可以这样写:

tekkenCharacter.origin = 'South Korea';

上面的代码中,我们使用了点号表示法。 如果我们现在输出 tekkenCharacter 对象,便可以看到它具有 origin 属性。 接下来,因为这个人物在游戏中有着与众不同的橘色头发, 我们可以通过方括号表示法来为它添加这个属性,像这样:

tekkenCharacter['hair color'] = 'dyed orange';

如果要设置的属性中存在空格,或者要设置的属性是一个变量,那我们必须使用方括号表示法(bracket notation)来为对象添加属性。 在上面的代码中,我们把属性(hair color)放到引号里,以此来表示整个字符串都是需要设置的属性。 如果我们不加上引号,那么中括号里的内容会被当作一个变量来解析,这个变量对应的值就会作为要设置的属性, 请看这段代码:

const eyes = 'eye color';tekkenCharacter[eyes] = 'brown';

执行以上所有示例代码后,对象会变成这样:

{player: 'Hwoarang',fightingStyle: 'Tae Kwon Doe',human: true,origin: 'South Korea','hair color': 'dyed orange','eye color': 'brown'
};

使用方括号访问属性名称

在关于对象的第一个挑战中,我们提到可以在一对方括号中用一个变量作为属性名来访问属性的值。 假设一个超市收银台程序中有一个 foods 对象, 并且有一个函数会设置 selectedFood;如果我们需要查询 foods 对象中,某种食物是否存在, 可以这样实现:

let selectedFood = getCurrentFood(scannedItem);
let inventory = foods[selectedFood];

上述代码会先读取 selectedFood 变量的值,并返回 foods 对象中以该值命名的属性所对应的属性值。 若没有以该值命名的属性,则会返回 undefined。 有时候对象的属性名在运行之前是不确定的,或者我们需要动态地访问对象的属性值。在这些场景下,方括号表示法就变得十分有用。

let foods = {apples: 25,oranges: 32,plums: 28,bananas: 13,grapes: 35,strawberries: 27
};function checkInventory(scannedItem) {// 只修改这一行下面的代码return foods[scannedItem]// 只修改这一行上面的代码
}console.log(checkInventory("apples"));

输出25


使用 delete 关键字删除对象属性

现在我们来看看如何从一个对象中移除一个键值对。

我们再来回顾一下上一个挑战中的 foods 对象。 如果我们想移除 apples 属性,可以像这样使用 delete 关键字:

delete foods.apples;

检查对象是否具有某个属性

JavaScript 为我们提供了两种不同的方式来实现这个功能: 一个是通过 hasOwnProperty() 方法,另一个是使用 in 关键字。 假如我们有一个 users 对象,为检查它是否含有 Alan 属性,可以这样写:

users.hasOwnProperty('Alan');
'Alan' in users;

这两者结果都应该为 true。


使用 Object.keys() 生成由对象的所有属性组成的数组

我们可以给 Object.keys() 方法传入一个对象作为参数,来生成包含对象所有键的数组这会返回一个由对象中所有属性(字符串)组成的数组。 需要注意的是,数组中元素的顺序是不确定的。

let users = {Alan: {age: 27,online: false},Jeff: {age: 32,online: true},Sarah: {age: 48,online: false},Ryan: {age: 19,online: true}
};function getArrayOfUsers(obj) {// 只修改这一行下面的代码return Object.keys(obj)// 只修改这一行上面的代码
}console.log(getArrayOfUsers(users));
[ 'Alan', 'Jeff', 'Sarah', 'Ryan' ]

本文标签: JS对象 与 数组