1.原生JS处理数组的常用方法
join()指定的分割符将数组拼接在一起push()像数组尾部添加一个元素pop()删除数组组后一个元素shift()删除数组的第一个元素unshit()像数组的首部添加一个元素slice()查找其中的部分元素 (数组截取)splice()对数组增删改(数组更新)fill()使用特定值填充数组的元素concat()两/多个数组链接在一起filter()遍历筛选数组map()遍历数组forEach()遍历数组indexOf引索方法从开头查找lastIndexof伊索从后向前查找find()返回匹配的值reduce()从前向后遍历数组reduceRight()从后向前遍历every()判断数组中每一项是否都满足条件,只有所有的数据都满足条件才返回truesome()判断数组中是否存在满足条件,只需要一个满足,就会满足返回truefindeIndex()返回匹配的引索include()判断数组中是否包含指定值,返回true/falsereverse()数组反转sort()对数据元素排序copyWithin()target,start,end target 对象之前的不变,然后事开始位置,结束位置,关键数组的长度不会改变只是部分数据被替换toLocalString()Date对象的一个方法,用于根据本地时间把Date对象转换为字符串,用到new Date().toLocaleString() 转化成 2022-12-22T15:00:00toString()直接把数组转化成字符串flat()数组扁平化,去除一/多层数组 flat(num) 去除的层数flatMap()循环去除一层数组entries()返回该对象的key和value 使用arr.entries().next().valuekeys()去数组对象的key值用Array.keys().next().valuevalues()去数组对象的vaule值用Array.values().next().valueArray.from()从字符串生成数组Array.of()与Array() 效果一样 区别在于单个参数Array.of(7) => [7]、Array(7) => 长度为7的空数组
2.vue2中的数组实现
vue2 种被尤大重写的数组
push()、pop()、 shift() 、unshift() 、splice() 、 sort() 、reverse()
源码如下:
vue2 的响应式原理是通过数据劫持object.defineProperty,网上很多说是因为object.defineProperty 监听不到数组的变化,所以vue的响应式有了
$set,这里我看了一下源码每次$set 和 $delete 其实都是调用了一次splice()方法进行了数组更新
下面我们具体介绍一下object.defineProperty的基本使用和vue2是怎样实现响应式的。
object.defineProperty 基本使用方法
Object.defineProperty(obj, prop, descriptor)
参数是3个,obj 是对象,prop是属性 ,descriptor是目标属性持有的特性
具体介绍一下descriptor如下:
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val, //设置属性的值
enumerable: !!enumerable, //目标属性是否可以被枚举
writable: true, // 值是否可以重写
configurable: true, // 目标属性是否可以被删除或是否可以再次修改特性
get:function (){} | undefined, // get/set 是存取器描述
set:function (value){} | undefined // 当使用了get或set方法,不允许使用writable和value这两个属性 get/set不是成对出现,可以单个调用
})
}
简单的例子如下:
// 是可以进行赋值的,可以正常监听到Object:
let userInfo = {
age: "11"
};
Object.defineProperty(obj, 'name', {
configurable: true, // 是否可删除
enumerable: true, // 是否可以枚举
get(){
return val
},
set(val) {
name = val
}
})
console.log(obj.name) // hello world
如果对象是复杂数据,我们看一下是否还可以更新数据
// 复杂数据的例子
let obj = {
name: 'hello world',
vue: {
name: 'hello vue'
},
options: ['watch','props']
}
// 这种复杂的数据显然一个简单函数是实现不了的,我们需要循环+递归才能监听到每个数据的属性
// 封装递归函数
function defineProperty(obj,key,val) {
Object.defineProperty(obj, key, {
enumerable:true,
configurable: true,
get() {
console.log('读',val)
return val
},
set(newval) {
console.log('写',newval)
// 这里做递归,直到val 和newval 相等时候,停止调用
if (val === newval) return
observe(newval)
val = newval
}
})
}
//调用的循环函数
function observe(obj) {
if(typeof obj !== 'object' || obj == null) return
// 循环遍历所有的属性
for (const key in obj) {
defineProperty(obj,key,obj[key])
}
}
observe(userInfo)
obj.age = '12'
options = [1,2,3]
observe(options)
console.log('---------push---------')
options.push(4)
console.log('---------pop---------')
options.pop()
console.log('---------shift---------')
options.unshift('0')
console.log('--------length----------')
options.length = 10
console.log(JSON.stringify(obj))
执行结果可以看出来
object.defineProperty``push()、 pop() 、 length 都没有监听到set 和 get 方法,unshift 方法只把
对数组进行了特殊处判断一下,如下:
const orginalProto = Array.prototype;
const arrayProto = Object.create(orginalProto); // 先克隆一份Array的原型出来
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(method => {
arrayProto[method] = function () {
// 执行原始操作
orginalProto[method].apply(this, arguments)
console.log('监听赋值成功', method)
}
})
响应式结合使用的时候再进行一下observe一下,这里我理解就是把key值具象化,实现了劫持属性1. map()遍历数组,代码如下
function observe(obj) {
if (typeof obj !== 'object' || obj == null) return
if (Array.isArray(obj)) {
obj.__proto__ = arrayProto
for (let i = 0; i < obj.length; i++) {
observer(obj[i])
}
} else {
// 循环遍历所有的属性
for (const key in obj) {
defineProperty(obj,key,obj[key])
}
}
}
3.vue3中的数组实现
vue3 种被尤大重写的数组源码如下:
vue3我们都知道的是响应式原理用Proxy,数据劫持的方式
function reactive(obj) {
if(typeof Obj !== 'object' && obj!=null) {
return obj
}
const observe = new Proxy(state, {
get(target, key, receiver) {
const res = Reflect.get(target, propertyKey,receiver)
console.log('获取key:',key,'res',res)
return res
},
set(target,key,val,receiver) {
const res = Reflect.set(target,key,val,receiver)
console.log('设置key:',key,'res',res)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
console.log('删除key:',key,'res',res)
return res
}
})
return observe
}
Reflect 在这里这个简单介绍一下,这个和proxy是一样的都是ES6 的API