1.判断对象的数据类型
const isType = type => target=> `[object ${type}]` === Object.prototype.toString.call(target)
const isArray = isType('Array')
console.log(isArray([]));
- 使用Object.prototype.toString配合闭包 通过传入不同的判断类型来返回不同的判断函数,一行代码,简洁优雅灵活(注意传入type参数时首字母大写)
- 不推荐将这个函数用来检测可能会产生包装类型的基本数据上 因为call始终会将第一个参数进行装箱操作 导致基本类型和包装类型无法区分
- 包装类型与基本类型 基本类型是直接存储变量的值保存在堆栈中
- 封装类型需要通过引用指向实例
- 声明方式的不同,基本类型无需通过new关键字来创建,而封装类型需new关键字
2. 循环实现数组 map 方法
const selfMap = function(fn,context){
const arr = Array.prototype.slice.call(this)
let mapArr = new Array()
for(let i =0;i<arr.length;i++){
//判断稀疏数组的情况
if(!arr.hasOwnProperty(i)) continue; //是否有指定的键(索引)
mapArr[i] = fn.call(context,arr[i],i,this)
}
}
Array.prototype.selfMap = selfMap
[1,2,3].selfMap(num => num * 2)
稀疏数组
-
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。稀疏数组的处理方式是:记录数组一共有几行几列,有多少个不同值;
-
稀疏数组可以简单的看作为是压缩,在开发中也会使用到。比如将数据序列化到磁盘上,减少数据量,在IO过程中提高效率
-
使用方法:将selfMap注入到Array.prototype上
-
值的一提的是 selfMap的第二个参数为第一个参数回调中的this指向,如果第一个参数为箭头函数 那设置的第二个参数this 会因为箭头函数的词法绑定而失效
-
另外就是对稀疏数组的处理 通过hasOwnProperty来判断当前下标下的元素是否存在于数组中
3. 使用 reduce 实现数组 map 方法
const selfMap2 = function(fn,context){
let arr = Array.prototype.slice.call(this)
return arr.reduce((pre,cur,index)=>{
return [...pre,fn.call(context,cur,index,this)]
},[])
}
4. 循环实现数组 filter 方法
const selfFilter = function(fn,context){
let arr = Array.prototype.slice.call(this)
let filterArr = []
for(let i=0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))continue
fn.call(context,arr[i],i,this)&&filterArr.push(arr[i])
}
return filterArr
}
5. 使用 reduce 实现数组 filter 方法
const selfFilter = function(fn,context){
return this.reduce((pre,cur,index)=>{
return fn.call(context,cur,index,this)?[...pre,cur]:[...pre]
},[])
}
6. 循环实现数组的 some 方法
const selfSome = function(fn,context){
let arr = Array.prototype.slice.call(this)
if(!arr.length) return false
for(let i =0;i<arr.length;i++){
if(!arr.hasOwnProperty(i)) continue
let res = fn(context,arr[i],i,this)
if(res)return true
}
return false
}
执行some方法的数组如果是一个空数组,最终始终返回false 而另一个every方法中的数组如果是一个空数组,则始终返回true
7. 循环实现数组的 reduce 方法
Array.prototype.selfReduce = function(fn,initialValue){
let arr = Array.prototype.slice.call(this)
let res
let startIndex
if(initialValue === undefined){
//找到第一个非空单元(真实)的元素和下标
for(let i = 0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))continue
startIndex = i
res = arr[i]
break
}
}else{
res = initialValue
}
for(let i = ++startIndex||0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))continue
res = fn.call(null,res,arr[i],i,this)
}
return res
}
8. 使用 reduce 实现数组的 flat 方法
const selfFlat = function(depth = 1){
let arr = Array.prototype.slice.call(this)
if(depth === 0) return arr
return arr.reduce((pre,cur)=>{
if(Array.isArray(cur)){
return [...pre,...selfFlat.call(cur,depth-1)]
}else{
return [...pre,cur]
}
},[])
}
- 因为selfFlat是依赖this指向的 所以在reduce便利时需要指定selfFlat的this指向
否则会默认指向window 从而发生错误 - 原理通过reduce遍历数组 遇到数组的某个元素仍是数组时 通过ES6的扩展运算符对其进行降维(es5可以使用concat方法) 而这个数组内部可能还嵌套数组 所以需要递归调用selfFlat
同时原生的flat方法支持一个depth参数表示降维的深度 默认为1 即给数组降一层维度 - 传入 Inifity 会将传入的数组变成一个一维数组
- 原理是每递归一次将 depth 参数减 1,如果 depth 参数为 0 时,直接返回原数组
9. 实现 ES6 的 class 语法
function inherit(subType,superType){
// 第一个参数Object.create//方法创建一个新对象,使用现有的对象来提供新对象的__proto__
// 第二个参数是对象的属性描述符以及对应的属性名称 将要为对象添加或修改的属性的具体配置 。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。
subType.prototype = Object.create(superType.prototype,{
constructor:{
enumerable:false,//是否可遍历
configurable:true, //是否可通过delete删除此属性
writable:true, //能否修改属性的值
value:subType//该属性对应的值 为对象添加属性
}
})
// 为现有对象设置一个指定的对象原型 subType的原型为superType
Object.setPrototypeOf(subType,superType)
}
- ES6的class内部是基于寄生组合式继承,它是目前最理想的继承方式 通过Object.create方法创造一个空对象,
- 并将这个空对象继承Object.create方法的参数 再让子类(subType) 的原型对象等于这个空对象,就可以实现子类实例的原型等于这个空对象
- 而这个空对象的原型又等于父类原型对象(superType.prototype)的继承关系
- 而Object.create支持第二个参数,即给生成的空对象定义属性和属性描述符/访问器描述符,
- 我们可以给这个空对象定义一个constructor属性更加符合默认的继承行为,同时它是不可枚举的内部属性(enumerable:false)
- 而ES6的class允许子类继承父类的静态方法和静态属性 而普通的寄生组合式继承只能做到实例与实例之间的继承
- 对于类与类之间的继承需要额外定义方法 这里使用Object.setPrototypeOf将superType设置为subType的原型 从而能够从父类中继承静态方法和静态属性
Object.create方法接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。
var o2;
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});