JavaScript中面试常考数组方法和对象方法

65 阅读10分钟

JavaScript数组方法和对象方法

创建数组

Array.from

console.log(Array.from('foo'))

Array.of

Array.of(7);       // [7]
Array.of(1, 2, 3); // [1, 2, 3]
//Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为 7 的空数组
Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]

数组的遍历

for循环

var arr = [1, 2, 3, 4, 5, 6]
for(var i = 0; i < arr.length; i++) {
    console.log(arr[i])
}
// 1 2 3 4 5 6

for...in...

输出的 key 是数组索引

var arr = ['h', 'e', 'l', 'l', 'o']
for(let key in arr) {
    console.log(key)
}
// 0 1 2 3 4

for...of...

不能循环对象,因为任何数据结构只要部署 Iterator接口,就可以完成遍历操作,有些数据结构原生具备 Iterator 接口,比如Array、Map、Set、String等,而 Iterator 接口是部署在数据结构的Symbol.iterator属性上的,而对象Object恰恰是没有Symbol.iterator属性的,所以无法被for..of遍历

var arr = ['h', 'e', 'l', 'l', 'o']
for(var key of arr) {
    console.log(key)
}
// h, e, l, l, o

Array.prototype.forEach()

对数组的每个元素执行一次给定的函数

语法:arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

callback:为数组中每个元素执行的函数,该函数接收一至三个参数

currentValue:数组中正在处理的当前元素。

index:可选,数组中正在处理的当前元素的索引。

array: 可选,forEach()方法正在操作的数组。

thisArg:可选,当执行回调函数 callback时,用作this的值。

function logArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element);
}
// 注意索引 2 被跳过了,因为在数组的这个位置没有项
[2, 5, , 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
function Counter() {
  this.sum = 0;
  this.count = 0;
}
Counter.prototype.add = function(array) {
  array.forEach(function(entry) {
    this.sum += entry;
    ++this.count;
  }, this);
};
const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 === (1 + 1 + 1)
obj.sum;
// 16 === (2 + 5 + 9)

Array.prototype.filter()

创建一个新数组,其包含通过所提供函数实现的测试的所有元素

返回值:一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组

function isBigEnough(element, index) {
  console.log('a[' + index + '] = ' + element)
  return element >= 10
}
var filtered = [12, 5, 8].filter(isBigEnough)
// filtered is [12]
// a[0] = 12
// a[1] = 5
// a[2] = 8

Array.prototype.map()

创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成

map 不修改调用它的原数组本身(当然可以在 callback 执行时改变原数组) 回调函数不返回值时,最后新数组的每个值都为undefined

function isBigEnough(element, index) {
  console.log('a[' + index + '] = ' + element)
  return element * 2
}
var filtered = [1, 2, 3].map(isBigEnough)
console.log(filtered)
// filtered is [2, 4, 6]

Array.prototype.find()

返回数组中第一个满足所提供测试函数的元素的值,否则返回 undefined。

function isBigEnough(element, index) {
  console.log('a[' + index + '] = ' + element)
  return element >= 10
}
var filtered = [1, 2, 3].find(isBigEnough)
// filtered is undefined
// a[0] = 1
// a[1] = 2
// a[2] = 3

Array.prototype.findIndex()

返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1

function isBigEnough(element, index) {
  console.log('a[' + index + '] = ' + element)
  return element >= 10
}
var filtered = [1, 2, 3].findIndex(isBigEnough)
// filtered is -1

Array.prototype.every()

every()方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

语法:arr.every(callback(element[, index[, array]])[, thisArg])

参数:与forEach相同

返回值:如果回调函数的每一次返回都为true,返回 true,否则返回 false

function logArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element)
  return true
}
// 注意索引 2 被跳过了,因为在数组的这个位置没有项
console.log([2, 5, , 9].every(logArrayElements))
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
// true

Array.prototype.some()

测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。

function isBigEnough(element, index) {
  console.log('a[' + index + '] = ' + element)
  return element > 2
}
var filtered = [1, 2, 3].some(isBigEnough)
// filtered is true

Array.prototype.reduce()

对数组中的每个元素按序执行一个由您提供的 reducer函数,每一次运行 reducer会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

参数:

callbackFn:一个 “reducer” 函数,包含四个参数

  • previousValue:上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]。
  • currentValue:数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]。
  • currentIndex:数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。
  • array:用于遍历的数组。

initialValue:可选,作为第一次调用 callback函数时参数 previousValue的值。若指定了初始值 initialValue,则 currentValue则将使用数组第一个元素;否则 previousValue 将使用数组第一个元素,而 currentValue将使用数组第二个元素

// 未指定initialValue
const array = [15, 16, 17, 18, 19];
function reducer(previous, current, index, array) {
  const returns = previous + current;
  console.log(`previous: ${previous}, current: ${current}, index: ${index}, returns: ${returns}`);
  return returns;
}
array.reduce(reducer)
// previous: 15, current: 16, index: 1, returns: 31
// previous: 31, current: 17, index: 2, returns: 48
// previous: 48, current: 18, index: 3, returns: 66
// previous: 66, current: 19, index: 4, returns: 85
// 指定initialValue
const array = [15, 16, 17, 18, 19];
function reducer(previous, current, index, array) {
  const returns = previous + current;
  console.log(`previous: ${previous}, current: ${current}, index: ${index}, returns: ${returns}`);
  return returns;
}
array.reduce(reducer, 10)
// previous: 10, current: 15, index: 0, returns: 25
// previous: 25, current: 16, index: 1, returns: 41
// previous: 41, current: 17, index: 2, returns: 58
// previous: 58, current: 18, index: 3, returns: 76
// previous: 76, current: 19, index: 4, returns: 95

Array.prototype.keys()、Array.prototype.values()、Array.prototype.entries()

返回一个包含数组中每个索引键的Array Iterator对象

let arr = ['a', 'b', 'c']
for (let index of arr.keys()) {
console.log(index);
}
// 0 1 2
for (let item of arr.values()) {
console.log(item);
}
// 'a' 'b' 'c'
for (let [index, item] of arr.entries()) {
console.log(index, item);
}
// 0 'a'
// 1 'b'
// 2 'c'

操作数组(增删改查)

Array.prototype.push()

将一个或多个元素添加到数组的末尾,并返回该数组的新长度

const animals = ['pigs', 'goats', 'sheep'];
const count = animals.push('cows');
console.log(count) // 4

使用 apply()添加第二个数组的所有元素

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];
// 将第二个数组融合进第一个数组
// 相当于 vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);
console.log(vegetables);
// ['parsnip', 'potato', 'celery', 'beetroot']

Array.prototype.pop()

从数组中删除最后一个元素,并返回该元素的值。此方法会更改数组的长度。

const plants = ['broccoli', 'kale', 'tomato'];
console.log(plants.pop());
// expected output: "tomato"
console.log(plants);
// expected output: Array ["broccoli", "kale"]

Array.prototype.shift()

从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度

const array1 = [1, 2, 3];
const firstElement = array1.shift();
console.log(array1);
// expected output: Array [2, 3]
console.log(firstElement);
// expected output: 1

Array.prototype.unshift()

将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)

const array1 = [1, 2, 3];
console.log(array1.unshift(4, 5));
// expected output: 5
console.log(array1);
// expected output: Array [4, 5, 1, 2, 3]

Array.prototype.splice()

通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组

语法:array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]
​
months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]

Array.prototype.reverse()

将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组

const array1 = ['one', 'two', 'three'];
console.log(array1);
// expected output: Array ["one", "two", "three"]
const reversed = array1.reverse();
console.log(reversed);
// expected output: Array ["three", "two", "one"]
// Careful: reverse is destructive -- it changes the original array.
console.log(array1);
// expected output: Array ["three", "two", "one"]

Array.prototype.sort()

用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列时构建的

const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]

语法:arr.sort([compareFunction])

参数:

  • compareFunction可选

    用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的 Unicode 位点进行排序

  • firstEl第一个用于比较的元素

  • secondEl第二个用于比较的元素

Array.prototype.at()

Array.prototype.includes()

用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false

const array1 = [1, 2, 3];
console.log(array1.includes(2));
// expected output: true
const pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));

Array.prototype.indexOf()

const beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];
console.log(beasts.indexOf('bison'));
// expected output: 1
// start from index 2
console.log(beasts.indexOf('bison', 2));
// expected output: 4
console.log(beasts.indexOf('giraffe'));
// expected output: -1

数组的合并

Array.prototype.concat()

用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组

const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2);
console.log(array3);
// expected output: Array ["a", "b", "c", "d", "e", "f"]

数组的填充截取

Array.prototype.fill()

用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引

语法:arr.fill(value[, start[, end]])

[1, 2, 3].fill(4);               // [4, 4, 4]
[1, 2, 3].fill(4, 1);            // [1, 4, 3]
[1, 2, 3].fill(4, 1, 2);         // [1, 4, 3] 
[1, 2, 3].fill(4, 1, 1);         // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3);         // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2);       // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN);     // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5);         // [1, 2, 3]
Array(3).fill(4);                // [4, 4, 4]
[].fill.call({ length: 3 }, 4);  // {0: 4, 1: 4, 2: 4, length: 3}
// Objects by reference.
var arr = Array(3).fill({}) // [{}, {}, {}];
// 需要注意如果 fill 的参数为引用类型,会导致都执行同一个引用类型
// 如 arr[0] === arr[1] 为 true
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]

Array.prototype.slice()

返回一个新的数组对象,这一对象是一个由 begin和 end决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变

const animals = ['ant','bison','camel','duck','elephant']
console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]
console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]
console.log(animals.slice(-2));
// expected output: Array ["duck", "elephant"]
​
console.log(animals.slice(2, -1));
// expected output: Array ["camel", "duck"]
console.log(animals.slice());
// expected output: Array ["ant", "bison", "camel", "duck", "elephant"]

数组转字符串

Array.prototype.join()

将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符

const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join());
// expected output: "Fire,Air,Water"
console.log(elements.join(''));
// expected output: "FireAirWater"
console.log(elements.join('-'));
// expected output: "Fire-Air-Water"

数组的扁平化

Array.prototype.flat()

会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]

JavaScript对象方法

构造对象

Object.create()

用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)

语法:Object.create(proto, propertiesObject)

参数:

proto:新创建对象的原型对象

propertiesObject:可选,如果该参数被指定且不为 undefined,则该传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符

const person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
}
const me = Object.create(person);
me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

Object.assign()

Object.assign() 方法将所有可枚举(Object.propertyIsEnumerable()返回 true)和自有(Object.hasOwnProperty()返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }// 原型链上的属性和不可枚举属性不能被复制
const obj = Object.create({ foo: 1 }, { // foo is on obj's prototype chain.
  bar: {
    value: 2  // bar is a non-enumerable property.
  },
  baz: {
    value: 3,
    enumerable: true  // baz is an own enumerable property.
  }
});
​
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }

对象检测

in关键字

该方法可以判断对象的自有属性和继承来的属性是否存在

let user = {name: "leo"};
"name" in user;            //true,自有属性存在
"age"  in user;            //false
"toString" in user;     //true,是一个继承属性

Object.prototype.hasOwnProperty()

该方法只能判断自有属性是否存在,对于继承属性会返回 false

const object1 = {};
object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));
// expected output: true
console.log(object1.hasOwnProperty('toString'));
// expected output: false
console.log(object1.hasOwnProperty('hasOwnProperty'));
// expected output: false

Object.prototype.isPrototypeOf()

用于测试一个对象是否存在于另一个对象的原型链上

function Foo() {}
function Bar() {}
function Baz() {}
Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true

遍历对象

Object.keys()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键名

Object.values()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值

Object.entries()

返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组

let user = { name: "leo", age: 18};
Object.entries(user);
// [["name","leo"],["age",18]]

Object.getOwnPropertyNames()

返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组

let user = { name: "leo", age: 18};
Object.getOwnPropertyNames(user);
// ["name", "age"]var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]
​
// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

对象属性配置

Object.defineProperty()

  • writable — 如果为 true,则值可以被修改,否则它是只可读的。
  • enumerable— 如果为 true,则会被在循环中列出,否则不会被列出。
  • configurable — 如果为 true,则此属性可以被删除,这些特性也可以被修改,否则不可以
let user = {
  name: "John",
};
Object.defineProperty(user, "toString", {
  enumerable: false
})
// 现在我们的 toString 消失了:
for (let key in user) alert(key); // name

Object.defineProperties()

直接在一个对象上定义新的属性或修改现有属性,并返回该对象

var obj = {};
Object.defineProperties(obj, {
  'property1': {
    value: true,
    writable: true
  },
  'property2': {
    value: 'Hello',
    writable: false
  }
})