1、常用的数组方法?哪些会改变原数组?哪些不会?
增
- push() 方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
- unshift() 在数组开头添加任意多个值,然后返回新的数组长度
- splice() 传入三个参数,分别是开始位置、0(要删除的元素数量)、插入的元素,返回空数组
- concat() 首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组,不会影响原始数组
删
- pop() 方法用于删除数组的最后一项,同时减少数组的length 值,返回被删除的项,
- shift() 方法用于删除数组的第一项,同时减少数组的length 值,返回被删除的项
- splice() 传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
- slice() (切割) 传入数组下标, 用于创建一个包含原有数组中一个或多个元素的新数组,不会影响原始数组
改: 即修改原来数组的内容,常用splice
- splice() 传入三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响
查:即查找元素,返回元素坐标或者元素值
- indexOf() 传入要查找的参数,返回要查找的元素在数组中的首个位置,如果没找到则返回 -1
- includes() 传入要查找的参数,返回要查找的元素在数组中的位置,找到返回true,否则false
- find() 返回第一个匹配的元素
排序方法
- reverse() 反转数组,
- sort() 传入一个比较函数,用于判断哪个值应该排在前面
转换方法
- join() 接收一个参数,即字符串分隔符,返回包含所有项的字符串
迭代方法
- every() 对数组每一项都运行传入的测试函数,如果所有元素都返回 true ,返回 true
- some() 对数组每一项都运行传入的测试函数,如果至少有1个元素返回 true ,返回 true
- filter() 对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回
- forEach() 对数组每一项都运行传入的函数,没有返回值
- map() 对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组
总结
会改变原数组的方法
:增删改的函数 和 排序方法都会改变原数组、forEach()、不会改变原数组的方法
:filter()、map()、every()、some()、reduce()、
2、如何实现数组去重?
new Set()
indexOf
hasOwnProperty()
includes()
reduce()+includes()
filter()
具体实现方法:
1、new Set()
var arr = [1,1,'str',9,8,7,4,7,8,{},{}]
function set(arr){
return Array.from(new Set(arr))
}
console.log(set(arr));//[1, 'str', 9, 8, 7, 4, {…}, {…}]
new一个Set,参数为需要驱虫的数组,Set会自动删除重复的元素,再将Set转为数组返回,
优点:效率高,代码简单,思路清晰
缺点:可能有兼容性问题,占用了较多空间,使用的额外空间有一个查询对象和一个新数组。
2、indexOf
原理: 新建一个空数组,for循环原数组,判断新数组中是否没有当前元素,如果没有就push进新数组中,有则跳过。
// 2、indexOf()
var arr = [1,1,'str',9,8,7,4,7,8,{},{}]
function index(arr){
if (!Array.isArray(arr)) {
console.log('类型错误,不是数组');
return
}
var array = [];
for (let i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) === -1) {// 判断索引有没有等于 没有的话 === -1
array.push(arr[i])
}
}
return array
}
console.log(index(arr));// [1, 'str', 9, 8, 7, 4, {…}, {…}]
3、hasOwnProperty()
hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
//hasOwnProperty()
var arr = [1, 'true', true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN,'NaN', 0, 0, 'a', 'a', {}, {},[],[] ];
function hop(arr) {
var obj = {};
return arr.filter(function (item, index, arr) {
console.log(typeof item + item); //item 为1时,返回number1 'true'-->stringtrue
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
console.log(hop(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] //所有的都去重了
4、includes
原理: includes() 判断新数组中是否存在当前遍历的元素,不存在就push进新数组中。
// 5、includes
var arr = [1,1, 4, 8, 12, 16, 15,8, 15,12, 16,{},{},NaN,NaN,[],[]];
function incl(arr){
let array = []
for (let i = 0; i < arr.length; i++) {
if (!array.includes(arr[i])) {
array.push(arr[i])
}
}
return array
}
console.log(incl(arr));//[1, 4, 8, 12, 16, 15, {…}, {…}, NaN, Array(0), Array(0)]
5、reduce() + includes()
原理: 利用reduce遍历和传入一个空数组作为去重后的新数组,然后内部判断新数组中是否存在当前遍历的元素,不存在就插入到新数组中。
// reduce() + includes()
function red(arr){
return arr.reduce((prev,cur)=>{//prev:需要添加的每一项;cur:数组的每一项。
return prev.includes(cur) ? prev:[...prev,cur]
},[])
}
console.log(red(arr));//[1, 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, Array(0), Array(0)]
6、filter()+indexOf()
原理: indexOf会先返回最先找到的数字的索引。
var arr = [1, 4, 8, 12, 16, 15,8, 15,12, 16];
function fil(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item) === index;
});
}
console.log(fil(arr));
优点:可以在去重的时候对元素进行其他操作,可拓展性强
3、对闭包的理解
闭包的概念:有权访问另一个函数作用域中的变量的函数
闭包形成的原理:因为作用域链,当前作用域可以访问上层作用域中的变量。
闭包形成的条件:
- 函数的嵌套
- 内部函数访问外部函数的变量延长外部函数的变量声明周期
闭包的作用:
- 保存变量: 如果当前上下文不被释放(上下文中的某个东西被外部占用即可),则存储的私有变量也不会别释放,可以供其下级上下文中调取使用,相当于把一些值保存起来。
- 保护变量: 划分一个独立的代码执行区域,在这个区域中有自己私有变量存储的空间,保护自己的私有变量不受外界的干扰(操作自己的私有变量和外界没有关系)。
我们把函数执行形成私有上下文,来保护和保存私有变量的机制成为闭包。
闭包的优点:延长局部变量生命周期。
闭包的缺点:因为函数中的变量一直保存在内存中,过多的闭包会导致内存泄漏。
闭包的应用: 模仿块级作用域链、封装私有化变量、保护外部函数的变量能够访问函数定义时所 在的词法作用域。
4、call、apply、bind的作用和区别
作用: 都是改变函数执行时的上下文,简单的来说都是改变函数的this的指向
区别:
- apply
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。
注: 当第一个参数为null、undefined的时候,默认指向window(在浏览器中) - call
call接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以参数序列的形式传入,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。
注: 当第一个参数为null、undefined的时候,默认指向window(在浏览器中) - bind
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入);改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
注: 当第一个参数为null、undefined的时候,默认指向window(在浏览器中)
总结:
- 三者都可以改变函数的this对象指向
- 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
- 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
- bind是返回绑定this之后的函数,apply、call 则是立即执行
5、new时会发生什么?
- 首先创建一个空对象
- 设置原型(为新创建对象添加
__proto__
属性),并将对象的原型设置为prototype对象 - 让这个函数的this,指向这个对象,执行构造函数的代码
- 判断函数返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型对象。
注: 如果没有return语句,就返回新创建的对象
手写new操作符
function newThis(fn,...args){
// 1 首先常见一个空对象
const obj = {};
// 2 设置原型,并将对象的原型设置为函数的prototype对象\
obj.__proto__ = fn.prototype;
// 3 让函数的this指向这个对象
let result = obj.apply(obj);
// 4 判断返回值类型
return result instanceof Object ? result : obj
}