[javascript基础]-数组

356 阅读12分钟

是什么

数组每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。

  1. 数组是值的有序集合
  2. 固定长度
  3. 可以存放不同的数据结构
  4. 一段线性分配的内存.

目录

  • 数组-类数组
  • 数组-复制
  • 数组-内置方法
  • 数组-其他类型转换

1. 数组-类数组

  1. 是一个对象
  2. 拥有一个 length 属性
  3. 不具有数组的 push()、pop()、shift()、forEach() 等方法。
  4. 方便处理复杂的数据结构
var array = ['name', 'age', 'sex'];
var arrayLike = {
    0: 'name',
    1: 'age',
    2: 'sex',
    length: 3
}

转数组

Array.from() 可以将两类对象转为真正的数组:

  1. 类数组对象 (Arguments对象 和 NodeList对象)
  2. 可遍历(iterable)对象(包括ES6新增的数据结构 Set 和 Map)
Array.prototype.slice.call(arrayLike) // ["name", "age", "sex"] 
Array.prototype.join.call(arrayLike,'&'); // name&age&sex
Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"]
Array.from(arrayLike); // ["name", "age", "sex"]
Array.prototype.concat.apply([], arrayLike) // ["name", "age", "sex"]

2. 数组-复制

var numbers = [1,2,3,'hello','javascript',33,'world', {"name":"wxh","age":33}];

// 复制数组
var _array = [];

// 方法一(浅拷贝)
// for (var i = 0; i < numbers.length; i++) {
// 	_array[i] = numbers[i]
// };

// 方法二(浅拷贝)
// _array = numbers.slice();

// 方法三(浅拷贝)
// _array = numbers.map((item) => item);

// 方法四(浅拷贝)
// _array = numbers.filter((item) => true);

// 方法五(浅拷贝)
// var _array = [...numbers];

// 方法六(浅拷贝)
// _array = numbers.reduce((prev,all)=> {
// 	prev.push(all);
// 	return prev;
// },[]);


// 方法七(浅拷贝)
// _array = Array.from(numbers);

// 方法八(浅拷贝)
// _array = numbers.concat([])


// 方法九(深拷贝)
_array = JSON.parse(JSON.stringify(numbers))


console.log(_array)

3. 数组-内置方法

slice

截取数组中的几个元素 组成新的数组

  1. slice(start,end)表示从下标start开始到下标end(不包括end)进行截取,得到的是一个新数组,不改变原数组。
  2. 当start为负值时表示从倒数第几个元素开始往后截取
  3. 不填end的话就表示从倒数第几个元素开始截取,一直截取到数组末尾元素。
// 简单复制,浅拷贝
var _array = [1,2,3,4,5]
var copyArray = _array.slice();

// 获取从 N 开始的子数组
// 希望弹出数组的第一个元素并使用它,返回剩余的数组,但希望在不修改原始数组的情况下执行此操作。
function useone (arr) {
  const usedItem = arr[0]
  return arr.slice(1)
}

// 获取从末尾 N 开始的子数组,负索引使删除任意数量的元素变得简单
const sliceArr = _array.slice(-3)
console.log('sliceArr ',sliceArr) //[3,4,5]

// 获取数组的前n个
const first4 = _array.slice(0, 4) // [1,2,3,4]

// 获取数组中某段子数组
function getSegement(arr, begin, length) {
  return arr.slice(begin, begin + length);
}
console.log(getSegement(_array,1,3)) // [2,3,4]

// 类似数组的对象转换
Array.prototype.slice.call(arguments)

// 修改数组中的特定索引
// slice在函数上下文中一个强大而常见的用法是替换数组中特定项的值。从本质上讲,这很简单,只需要分配新值,但是在函数世界中,不能修改原始数组。相反,可以将slice与扩展运算符一起使用,以返回一个相同但对于要更新的​​索引的新数组:
function replaceIdx(arr,index,newVal) {
	return [
		...arr.slice(0,index),
		newVal,
		...arr.slice(index+1)
	]
}

// 偏函数应用
var partial = function() {
  const fn = arguments[0];
  const args = Array.prototype.slice.call(arguments, 1);

  // Return a function that calls fn
  return function() {
    var remainingArgs = Array.prototype.slice.call(arguments);
    return fn.apply(this, args.concat(remainingArgs));
  }
}

// 将任意长度多余的参数强制转换为数组
function myFunc(a, b) { 
  const extraArgs = Array.prototype.slice.call(arguments, 2); 
}
myFunc(1, 2, 3, 4, 5, 6, 7, 8) // 得到a == 1,b === 2,extraArgs=== [3,4,5,6,7,8]

splice

splice()方法有三个参数,分别表示从哪个下标开始,删几个元素。可以实现增加,删除,替换数组元素的功能。arr.splice(-5,5)表示从倒数第五个元素开始,删五个元素。巧妙的是该方法的返回值是删除的元素集合。同时该方法改变了原数组。原数组变成了除了删除的元素剩下的元素集合。

var numbers = [1,2,3,'hello','javascript',33,'world', {"name":"wxh","age":33}];

// 复制数组
var _array = [];


console.log(numbers.splice(0,2,'a','b','c')) // [ 1, 2 ]
console.log(numbers) // [ 'a','b','c', 3,'hello','javascript',33,'world',{ name: 'wxh', age: 33 } ]

forEach

//标准
forEach(callback[,thisArg])

//简单示例
Array.forEach(function(item, index, array){
    //回调函数内容
}, args);

// 扩展
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function (callback, thisArg) {
        for (var i = 0; i < this.length; i++) {
            //当thisArg为undefined时,JS引擎会将window作为其调用者
            if (this[i])
                callback.call(thisArg, this[i], i, this);
        }
    }
}

filter

3个参数同forEach,args也同forEach,唯一不同的是,函数有回调函数里有return返回值。

  1. 简单来说,该方法返回值是一个数组。
  2. 初始时,这个数组是空数组,该方法会通过回调函数遍历整个数组(指Array这个),
  3. 假如当前的元素返回值为true(或者可以隐式转换为true的,比如一个长度大于0的字符串),
  4. 那么会将当前元素添加到被返回的数组中。

例如:[1, 2, 3], 回调函数的return是item > 1,  当第一个元素1时,1>1为false,因此不会添加到返回的数组中, 而2和3 >1显然是true,因此会被添加到数组中。最终,返回值是[2,3]。

//标准
filter(callback[,thisArg])

//简单示例
Array.filter(function(item, index, array){
    //回调函数内容
}, args);

// 扩展
if (!Array.prototype.filter) {
    Array.prototype.filter = function (callback, thisArg) {
        var temp = [];
        for (var i = 0; i < this.length; i++) {
            if (this[i]) {
                if (callback.call(thisArg, this[i], i, this)) {
                    //如果callback返回true,则该元素符合过滤条件,将元素压入temp中
                    temp.push(this[i]);
                }
            }
        }
        return temp;
    }
}

map

3个参数同forEach,args也同forEach,唯一不同的是,函数有回调函数里有return返回值。 简单来说,该方法的返回值也是一个数组(类filter);

  1. 和filter的区别在于,filter是将原数组元素,选择性加入到新数组中。
  2. map是将原数组的每个元素,进行处理后,放到新数组中。

例如:[1,2,3]作为原数组,map回调函数内的代码为: return item + 10; 那么就相当于将1+10放到数组中,然后将2+10放到数组中,再将3+10放到数组中。 结果为:[11, 12, 13] 当然,也可以写更复杂的逻辑,比如if(item>3)时+10,然后else if(item>2)时+5,否则else -10 那么结果就是[-9, 7, 13]

//标准
map(callback[,thisArg])

//简单示例
Array.map(function(item, index, array){
    //回调函数内容
}, args);

// 扩展
if (!Array.prototype.map) {
    Array.prototype.map = function (callback, thisArg) {
        var temp = [];
        for (var i = 0; i < this.length; i++) {
            if (this[i]) {
                var newItem = callback.call(thisArg, this[i], i, this);
                temp[i] = newItem//将callback返回的新元素压入temp中
            }
        }
        return temp;
    }
}

reduce

首先看回调函数,他有四个参数, item是当前元素,别的地方写的是currentValue表示当前值,为了方便理解,我这里写item和上面统一风格 index是当前元素的索引; Array是整个数组(可以通过这个修改源数组); 上面3个都很好理解。

第一个参数previousValue是核心。他表示上一次执行回调函数时的返回值。 例如,有数组[1, 2, 3, 4] 当我遍历到第二个元素时,回调函数的previousValue的值是1,item的值为2, return我写为:return previousValue + item 那么当遍历到第三个元素时,回调函数的previousValue的值则为3(因为1+2),item的值为3 当遍历到第四个元素时,previous的值则为6(因为3+3), 最终reduce的返回值为10(因为6+4)

那么问题来了,为什么没提到遍历第一个元素? 原因是,当reduce没有第二个参数时,遍历从数组的第二个元素开始, 第一次执行回调函数的previousValue的值是数组第一个元素的值

当reduce存在第二个参数时(哪怕是null或者undefined),遍历都将从第一个元素开始; 第一次执行回调函数时(遍历第一个元素),previousValue的值是第二个参数的值,而item是第一个元素的值

所以在使用的时候需要注意,如果需要执行和数组元素个数一样次数的回调函数,那么必须设置reduce的第二个参数; 如果不设置,那么回到函数次数执行的次数,将比数组元素个数少1。

//标准
reduce(callback[,initialValue])

//简单示例
arr.reduce(function (previousValue, item, index, Array) {
    return xxx;    //xxx表示省略
});

// 扩展
if (!Array.prototype.reduce) {
    Array.prototype.reduce = function (callback, initialValue) {
        var previousValue = initialValue || this[0];//如果不指定intialValue,则默认为数组的第一个元素
        //如果不指定initialValue(即第二个参数),i从1(第二个元素)开始遍历,否则就从0(第一个元素)开始遍历
        for (var i = initialValue ? 0 : 1; i < this.length; i++) {
            //previousValue 累加每一次返回的结果
            if (this[i])
                previousValue = callback(previousValue, this[i], i, this.toString());
        }
        return previousValue;
    }
}

reduceRight

//标准
reduceRight(callback[,initialValue])

//简单示例
arr.reduceRight(function (previousValue, item, index, Array) {
    return xxx;    //xxx表示省略
});
// 扩展
if (!Array.prototype.reduceRight) {
    Array.prototype.reduceRight = function (callback, initialValue) {
        var previousValue = initialValue || this[this.length - 1];//如果不指定intialValue,则默认为数组的第一个元素
        //如果不指定initialValue(即第二个参数),i从1(第二个元素)开始遍历,否则就从0(第一个元素)开始遍历
        for (var i = (initialValue ? this.length - 1 : this.length - 2); i > -1; i--) {
            //previousValue 累加每一次返回的结果
            if (this[i])
                previousValue = callback(previousValue, this[i], i, this);
        }
        return previousValue;
    }
}

every

返回值是true或者false 初始情况下是true; 然后遍历数组,有一个不满足,则为false,并且终止遍历过程。 回调函数的this依然默认指向window,或者是every的第二个参数。

空数组的every返回结果是true。

//标准
every(callback, thisArg);

//简单示例
arr.every(function(item, index, array){
    return item > xx;
});

if (!Array.prototype.every) {
    Array.prototype.every = function (callback, thisArg) {
        var result = true;
        for (var i = 0; i < this.length; i++) {
            if (this[i]) {
                if (!callback.call(thisArg ? thisArg : window, this[i], i, this)) {
                    result = false;
                    break;
                }
            }
        }
        return result; //所有元素都符合条件,返回true
    }
}

indexOf

用于查找第一个参数是否在数组中; 如果不在,返回-1; 如果在,返回在数组中遇见的第一个的下标; 例如:[1,2,3,2].indexOf(2)的返回值是1,虽然第二个和第四个元素都是,但是先遇见第二个,而第二个的下标是1

如果indexOf有第二个参数,那么从数组中第二个参数所指向的下标位置开始往后找; 例如:[1,2,3,2].indexOf(2,2)的返回值是3,因为开始下标是2(即第三个元素3),因此从第三个开始,遇见的第一个2的下标是2; 判断时含第二个参数所指向的数组元素

//标准
arr.indexOf(searchElement, fromIndex);

//简单示例
[1,2,3].indexOf(2);    //1(数组的第二个元素)
[1,2,3].indexOf(4);    //-1(未找到,注意,-1不是false,隐式转换后他的值为true)

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (searchElement, fromIndex) {
        var result = -1;
        for (var i = fromIndex ? fromIndex : 0; i < this.length; i++) {
            if (this[i]) {
                if (searchElement === this[i]) {
                    result = i;
                    break;
                }
            }
        }
        return result; //所有元素都符合条件,返回true
    }
}

lastIndexOf

//标准
arr.lastIndexOf(searchElement, fromIndex);

//简单示例
[1,2,1].lastIndexOf(1);    //2
[1,2,1].lastIndexOf(1, 1);    //0

if (!Array.prototype.lastIndexOf) {
    Array.prototype.lastIndexOf = function (searchElement, fromIndex) {
        var result = -1;
        for (var i = (fromIndex ? fromIndex : this.length - 1); i > -1; i--) {
            if (this[i]) {
                if (searchElement === this[i]) {
                    result = i;
                    break;
                }
            }
        }
        return result; //所有元素都符合条件,返回true
    }
}

from

Array.from() 从类数组对象或者可迭代对象中创建一个新的数组实例。

console.log(Array.from([1,2,3],x=>x*x)); // 2,4,9

Array.isArray() 用来判断某个变量是否是一个数组对象。

console.log(Array.isArray(Array.from([1,2,3]))); // true

Array.of() 根据一组参数来创建新的数组实例,支持任意的参数数量和类型。

console.log(Array.of(1, 2, 3));// [1,2,3]

concat

连接两个或更多的数组,并返回结果

var arr = [ 1, 2, 3 ];
var arr2= arr.concat("4", "5", "6");   //["1", "2", "3", "4", "5", "6"];

join

把数组的所有元素放入一个字符串并通过指定的分隔符进行分隔

arr.join("+");   //"1+2+3";

reverse

反转数组中元素的顺序。

arr.reverse(); 
console.log(arr);   // [3, 2, 1];

sort

数组排序 按照字符串的方式来排序。

toString

把数组转换为字符串,并返回结果

find

返回传入一个测试条件,符合条件的数组第一个元素,当数组中的元素在测试条件时返回true时, find() 返回符合条件的元素,之后的值不会再调用执行函数。如果没有符合条件的元素返回 undefined

const pets = [
  { type: 'Dog', name: 'Max'},
  { type: 'Cat', name: 'Karl'},
  { type: 'Dog', name: 'Tommy'},
];

var pet = pets.find(pet => pet.type ==='Dog' && pet.name === 'Tommy');
console.log(pet); // { type: 'Dog', name: 'Tommy' }

some

用于检测数组中的元素是否满足指定条件,如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测,如果没有满足条件的元素,则返回false。

let arr = [1, 2, 3, 4, 5]
console.log(arr.some(item => item === 2)); // true

// ES5循环实现 some 方法
const selfSome = function (fn, context) {
    let arr = Array.prototype.slice.call(this) // 复制数组原型对象
    // 空数组直接返回 false,数组的 every 方法则相反返回 true
    if(!arr.length) return false
    for (let i = 0; i < arr.length; i++) {
        if(!arr.hasOwnProperty(i)) continue;
        let res = fn.call(context,arr[i],i,this)
        if(res)return true
    }
    return false
}
// 挂载
Array.prototype.selfSome ||(Object.defineProperty(Array.prototype, 'selfSome', {
    value: selfSome,
    enumerable: false,
    configurable: true,
    writable: true
}))

console.log(arr.selfSome(item => item === 2)) // true

4. 数组-其他类型转换

数组与字符串

var arr = ['wxh','cjk','dbt','cjk']
console.log(arr.join(",")) // 以 逗号 进行分割
Array.from(new Set(arr)) // ['wxh','cjk','dbt']  // 数组去重

var str = "wxh,cjk,dbt"
console.log(str.split(","))
console.log(Array.from(str)) // ['w', 'x', 'h', ',',....]

数组与对象

let arrayLike = {
'0': 'wxh',
'1': 'cjk',
'2': 'dbt',
length: 3
};
// ES5 的写法
var arr1 = [].slice.call(arrayLike); // ['wxh', 'cjk', 'dbt']

// ES6 的写法
let arr2 = Array.from(arrayLike); // ['wxh', 'cjk', 'dbt']

// NodeList 对象
let arr3 = document.querySelectorAll('p');
Array.from(arr3).forEach(function (item) {
  console.log(item);
});

// arguments 对象
function foo() {
  var args = Array.from(arguments);

}

二维数组字符转换为数组

const arr = 
"[['语文','89.5','80','20','45','100'],['数学','89.5','80','45','45','100'],['英语','89.5','80','38','45','100'],['总分','89.5','80','97','45','100'],] "

console.log(eval(scores))

5. 数组-最大值与最小值

  1. 排序
  2. 循环比较
  3. reduce
  4. apply
  5. 扩展运算符

排序

var arr = [6, 4, 1, 8, 2, 11, 23];

arr.sort(function(a,b){return a - b;});
console.log(arr[arr.length - 1])

循环比较

var arr = [6, 4, 1, 8, 2, 11, 23];

var result = arr[0];
for (var i = 1; i < arr.length; i++) {
    result =  Math.max(result, arr[i]);
}
console.log(result);

reduce

var arr = [6, 4, 1, 8, 2, 11, 23];

function max(prev, next) {
    return Math.max(prev, next);
}
console.log(arr.reduce(max));

apply

var arr = [6, 4, 1, 8, 2, 11, 23];
console.log(Math.max.apply(null, arr))

扩展运算符

var arr = [6, 4, 1, 8, 2, 11, 23];
console.log(Math.max(...arr))

6. 数组去重复

双层循环去重

var array = [1, 1, '1', '1'];

function unique(array) {
    // res用来存储结果
    var res = [];
    for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
        for (var j = 0, resLen = res.length; j < resLen; j++ ) {
            if (array[i] === res[j]) {
                break;
            }
        }
        // 如果array[i]是唯一的,那么执行完循环,j等于resLen
        if (j === resLen) {
            res.push(array[i])
        }
    }
    return res;
}

console.log(unique(array)); // [1, "1"]

Object 键值对去重复

利用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。

var array = [{value: 1}, {value: 1}, {value: 2}];
function unique(array) {
    var obj = {};
    return array.filter(function(item, index, array){
        console.log(typeof item + JSON.stringify(item))
        return obj.hasOwnProperty(typeof item + JSON.stringify(item)) ? false : (obj[typeof item + JSON.stringify(item)] = true)
    })
}

console.log(unique(array)); // [{value: 1}, {value: 2}]

set 去重复

var array = [1, 2, 1, 1, '1'];
function unique(array) {
   return Array.from(new Set(array));
}
console.log(unique(array)); // [1, 2, "1"]

function unique(array) {
    return [...new Set(array)];
}

map 去重复

function unique (arr) {
    const seen = new Map()
    return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}

7. 数组-创建

[...Array(3).keys()] // [0, 1, 2]

// 数组操作
let arr = [1,2,3,4,5,2,3,4];
let str = "a1b2c3d4e5";

// filter : 不改变原数组, 返回删除后的结果
console.log(arr.filter((item) => item>2)) // 3,4,5,3,4
// 返回相等数据(要过滤掉的数据)
console.log(arr.filter((item) => item.text==text)) 
// 返回不相等数据(要保留的数据)
console.log(arr.filter((item) => item.text!=text))

// every : 检测数组中的数据是否都满足条件
console.log(arr.every((item) => item>2)) // false

// some: 检测数组中的数据是否有满足的,有就返回 true
console.log(arr.some((item) => item>2)) // true

// forEach: 可以改变数组,无返回值,返回数组的每一项
arr.forEach((item,index) => {
  console.log(item,index)
});

// map: 不改变原数组,将原数组改变后,组成新数组返回
console.log(arr.map((item) => item * 2));

// 复制数组
console.log([...arr]);

// slice: 不改变原数组,截取数组中的几个元素 组成新的数组,不包括end
console.log('slice',arr.slice(0,2));

// splice: 改变了原数组,实现增加,删除,替换数组元素,表示从哪个下标开始,删几个元素,返回删除数组元素
console.log('splice',arr,arr.splice(0,2,'s','s'));


// substring: 截取字符串, 1开始, 截取到位置 2 ,并不包含位置 
console.log(str,str.substring(0,2));

// substr: 截取字符串,0开始,截取2个
console.log(str,str.substr(0,2))

// 数组去重复
var uniqueFruits = Array.from(new Set(fruits));
[...new Set(fruits)]

对象转数组

var friends = [
    { name: ‘John’, age: 22 },
    { name: ‘Peter’, age: 23 },
    { name: ‘Mark’, age: 24 },
    { name: ‘Maria’, age: 22 },
    { name: ‘Monica’, age: 21 },
    { name: ‘Martha’, age: 19 },
]
var friendsNames = Array.from(friends, ({name}) => name);
console.log(friendsNames); // returns [“John”, “Peter”, “Mark”, “Maria”, “Monica”, “Martha”]

8. 数组转对象

var fruits = [“banana”, “apple”, “orange”, “watermelon”];
var fruitsObj = { …fruits };
console.log(fruitsObj); // returns {0: “banana”, 1: “apple”, 2: “orange”, 3: “watermelon”, 4: “apple”, 5: “orange”, 6: “grape”, 7: “apple”}

9. 数组操作

用数据填充数组

var newArray = new Array(10).fill(“1”);
console.log(newArray); // returns [“1”, “1”, “1”, “1”, “1”, “1”, “1”, “1”, “1”, “1”, “1”]

合并数组

var fruits = [“apple”, “banana”, “orange”];
var meat = [“poultry”, “beef”, “fish”];
var vegetables = [“potato”, “tomato”, “cucumber”];
var food = […fruits, …meat, …vegetables];
console.log(food); // [“apple”, “banana”, “orange”, “poultry”, “beef”, “fish”, “potato”, “tomato”, “cucumber”]

查找两个数组的交集

1. filter 函数
2. includes 函数

var numOne = [0, 2, 4, 6, 8, 8];
var numTwo = [1, 2, 3, 4, 5, 6];

var duplicatedValues = […new Set(numOne)].filter(item => numTwo.includes(item));
console.log(duplicatedValues); // returns [2, 4, 6]

从数组中删除假值

JavaScript 中,假值是 false, 0""null, NaN, undefinedvar mixedArr = [0, “blue”, “”, NaN, 9, true, undefined, “white”, false];
var trueArr = mixedArr.filter(Boolean);
console.log(trueArr); // returns [“blue”, 9, true, “white”]

数组混淆

// 随机更改数组元素顺序,混淆数组
(arr) => arr.slice().sort(() => Math.random() - 0.5)

获取URL的查询参数

function queryParam(){
	let q={};
	location.search.replace(/([^?&=]+)=([^&]+)/g,(_,k,v)=>q[k]=v);
	return q;
}

生成随机ID

// 生成长度为11的随机字母数字字符串
Math.random().toString(36).substring(2);
Math.random().toString(36).substring(2);
// hg7znok52x

日历过去7天的数组

// 创建过去七天的数组
[...Array(7).keys()].map(days => new Date(Date.now() - 86400000 * days));

类数组对象转数组

// 类数组对象转数组:
var arr = [].slice.call(arguments)

获取调用函数名

function where() {
	return where.caller.name;
}
function doWhere() {
	var name = where();
	console.info(name); // where
}
doWhere();

星级评分

function getRating(rating) {
    if(rating > 5 || rating < 0) throw new Error('数字不在范围内');
    return '★★★★★☆☆☆☆☆'.substring(5 - rating, 10 - rating );
}
console.log(getRating(3));

多行字符串格式化

var myString = (function () {/*
   <div id="someId">
     some content<br />
     <a href="#someRef">someRefTxt</a>
    </div>        
*/}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];
console.log(myString);

对象属性访问 a.b.c.d

// babel-node code1.js
// fun-1
let a = {
	b: {
		c: {
			d: "hello-d"
		}
	}
}

let obj = a && a.b && a.b.c && a.b.c.d;

console.log("基本==>",obj);

// fun-2
let _ = require('lodash');
// import _ from 'lodash' // 引入 es6 语法
const r = _.get(a,'b.c.d');

console.log("lodash==>",r)

// fun-3
// let r = a?.b?.c?.d
// console.log(r)

 // fun-4 循环
 function get1 (obj,path) {
 	for(var i = 0; i < path.length; i++) {
		if (obj !== null && obj !== undefined) {
			obj = obj[path[i]]
		} else {
			break
		}
	}
	return obj;
 }

 console.log("循环==>",get1(a,['b','c','d']))

 // fun-5 递归
 function get2 (obj,path) {
 	if(path.length) {
 		if(obj) {
 			var key = path.shift();
 			return get2(obj[key],path);
 		}else {
 			return obj;
 		}
 	}else {
 		return obj
 	}
 }

 console.log("递归==>",get2(a,['b','c','d']))

 // fun-6 reduce
 function get3(obj,path) {
 	return path.reduce(function(obj,curr) {
 		if(obj!=null && obj!=undefined) {
 			return obj[curr];
 		}else{
 			return obj
 		}
 	},obj);
 }
  console.log("reduce==>",get3(a,['b','c','d']))

数组-浅拷贝

1. 浅拷贝:浅拷贝只是复制了内存地址,如果原地址中的对象改变了,浅拷贝出来的对象也会相应改变。 
2. 深拷贝:开辟了一块新的内存存放地址和地址指向的对象,原地址的任何对象改变了,深拷贝出来的对象不变。

// 方法一
var arr = ["foo","osr","weixin"];
function copy(arr) {
  var temp = [];
  for(var i = 0;i<arr.length;i++) {
    temp.push(arr[i])
  }
  return temp;
}

var test = copy(arr)
console.log(test)

// 方法二
var concatTest = arr.concat();

// 方法三
var sliceTest = arr.slice();

console.log(sliceTest)
console.log(concatTest)

然而如果第一级数组元素是对象或数组,无法修改内部的数值,上面三种方式都失效
var arr = [
  {number:1},
  {number:2},
  {number:3} 
];
var concatTest = arr.concat();
concatTest[0].number = 10;
console.log(concatTest); [1,2,3]

对象-浅拷贝

var obj = {
    name: "张三",
    job: "学生"
}
// 方法一
function copyObj(obj) {
   if (typeof obj !== 'object') {
        return;  
   } 
   // 根据obj的类型判断是新建一个数组还是对象
   var newObj = obj instanceof Array ? [] : {};
   for(let item in obj) {
     // 这一步做一个判定防止拷贝了原型链上的东西
       if (obj.hasOwnProperty(key)) {
           newObj[key] = obj[key];
       }
   }
   return newObj;
}
var copyobj = copyObj(obj)
copyobj.name = "李四";
console.log(copyobj); // {name: '李四', job:: '学生'}
console.log(obj); // {name: '张三', job:: '学生'}

// 方法二
// ES6的Object.assign Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的target 
// 用法: Object.assign(target, source1, source2); 所以 copyObj = Object.assign({}, obj); 这段代码将会把obj中的一级属性都拷贝到 {}中,然后将其返回赋给copyObj
var copyobj = Object.assign({},obj)
copyobj.name = '李四'
console.log(copyobj) // {name: '李四', job:: '学生'}
console.log(obj)    // {name: '张三', job:: '学生'}

// 方法三
// ES6扩展运算符,扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
var copyobj = {...obj}
copyobj.name = '李四'
console.log(copyobj)
console.log(obj)

对象-深拷贝

// 方法一
let deep_arr = JSON.parse(JSON.stringify(arr));

JSON.stringify()和JSON.parse() 复制对象

1. 它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object2. 能正确处理的对象只有Number, String, Boolean, Array, 扁平对象。

// 方法二 递归实现
var deepClone = function(obj) {
    if (typeof obj !== 'object') {
        return;    
    }
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}

类数组

1. 不是函数
2. 有 length 属性
3. 且 length 属性值是不大于 Number.MAX_SAFE_INTEGER 的自然数

function isArrayLike(value) {
  return value != null && typeof value !== 'function' && 
    typeof (value.length) === 'number' && value > -1 && value % 1 == 0
}

给一个数组做去重处理

1. new Set()会将结果转换成对象 , Set()没法去重元素是引用对象的数组
let tempArr = new Set([1,2,3,3,4,4,5])
// => {1,2,3,4,5} 

2. _.uniqWith()
import _ from 'lodash';
<script>
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
</script>

//=> [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]

获取数组中的指定元素

// findIndex() 使用 findIndex()帮我们先获取到所需元素的索引值,拿到索引
let testArr = [{name:'鸣人',age:16},{name:'佐助',age:17},{name:'卡卡西',age:26}]
let index = testArr.findIndex(item => { return item.age > 16 });
// => 1 lodash
let testArr = [{name:'鸣人',age:16},{name:'佐助',age:17},{name:'卡卡西',age:26}]
let index = _.findIndex(testArr, {name:'佐助'});
// => 1

// 方法2: find
let testArr = [{name:'鸣人',age:16},{name:'佐助',age:17},{name:'卡卡西',age:27},{name:'佐助',age:17}]
let result = testArr.find(item => { return item.name == '佐助'});
// => { name:'佐助',age:17 }

用 Array.includes 替代 Array.indexOf

// Array新增的includes方法,它直接返回true或false表示数组是否包含元素。这样是不是直观多了?
'use strict';
const characters = [  '钢铁侠',  '黑寡妇',  '浩克',  '美国队长',  '浩克',  '雷神'];
console.log(characters.indexOf('浩克'));// 2
console.log(characters.indexOf('蝙蝠侠'));// -1
console.log(characters.includes('浩克'));// true
console.log(characters.includes('蝙蝠侠'));// false

用 Array.find 替代 Array.filter

// 为了在对象数组中查找符合条件的对象,以前的做法是用filter筛选,它会返回符合条件的数组。但我们的目的是查找具体的单个对象,所以使用find更合适:

const characters = [
  { id: 1, name: '钢铁侠' },
  { id: 2, name: '黑寡妇' },
  { id: 3, name: '美国队长' },
  { id: 4, name: '美国队长' },
];

function getCharacter(name) {
  return character => character.name === name;
}

console.log(characters.filter(getCharacter('美国队长')));
// [
//   { id: 3, name: '美国队长' },
//   { id: 4, name: '美国队长' },
// ]

console.log(characters.find(getCharacter('美国队长')));
// { id: 3, name: '美国队长' }

用 Array.some 替代 Array.find

// 有时候我们需要判断对象数组中符合条件的对象是否存在,虽然用find也可以达到目的,但还是不够直接,最好能直接返回true或false。Array.some就是干这个的。

'use strict';

const characters = [
  { id: 1, name: 'ironman', env: 'marvel' },
  { id: 2, name: 'black_widow', env: 'marvel' },
  { id: 3, name: 'wonder_woman', env: 'dc_comics' },
];

function hasCharacterFrom(env) {
  return character => character.env === env;
}

console.log(characters.find(hasCharacterFrom('marvel')));
// { id: 1, name: 'ironman', env: 'marvel' }

console.log(characters.some(hasCharacterFrom('marvel')));
// true

用 Array.reduce 替代 Array.filter 和 Array.map

// Array.reduce这货看上去不那么好理解,但熟悉之后你会发现它非常好用。它实际上是一个叠加器,它遍历整个数组,把每个元素传入累加器函数执行一次,并把执行结果作为下一次迭代的输入参数。你可以在叠加器里做任何事,比如数字求和、数组和字符串拼接、对象属性操作等。

比如这样一个需求,在城市数组里筛选属于广东省的,然后给每个对象加上一个地区属性。以往的做法是先用filter筛选出结果数组,再用map构造新的对象数组。这样其实遍历了两次数组,性能不够好。使用Array.reduce可以实现一次遍历就完成了:

'use strict';

const cities= [
  { city: '广州市', province: '广东省' },
  { city: '深圳市', province: '广东省' },
  { city: '石家庄市', province: '河北省' },
];

console.log(
  cities
    .filter(city=> city.province=== '广东省')
    .map(city=> Object.assign({}, city, { region: '华南区' }))
);
// [
//   { city: '广州市', province: '广东省', region: '华南区' },
//   { city: '深圳市', province: '广东省', region: '华南区' }
// ]

console.log(
  cities
    .reduce((acc, city) => {
      return city.province=== '广东省'
        ? acc.concat(Object.assign({}, city, { region: '华南区' }))
        : acc;
    }, [])
)
// [
//   { city: '广州市', province: '广东省', region: '华南区' },
//   { city: '深圳市', province: '广东省', region: '华南区' }
// ]

前端小白,👋