Instanceof 手写 *
判断一个实例是否是其父类或者祖先类型的实例。
instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的prototype查找失败,返回false。
let myInstanceof = (target,origin) => {while(target){if(target.__proto__ === origin.prototype){return true}target = target.__proto__}return false}
数组的map方法 *
数组的map()方法会返回一个新的数组,这个新的数组中的每个元素对应原数组中 的 对应位置 元素 调用一次 提供的函数后 的 返回值。
**
//用法const a = [1, 2, 3, 4]const b = array1.map(x => x * 2)console.log(b) // [2,4,6,8]
map方法有两个参数,一个是操作数组元素的方法fn,一个是this指向,其中使用fn时可以获取三个参数。
**
//原生实现Array.prototype.myMap = function(fn, thisValue) {let res = []thisValue = thisValue || []let arr = thisfor(let i = 0;i < arr.length;i ++){res.push(fn.call(thisValue,arr[i],i,arr)) //参数分别为this指向,当前数组项,当前索引,当前数组}return res}//实现const a = [1, 2, 3]const b = a.myMap((a, index) => {console.log(`索引${index}的值为`,a + 1)return a + 1})console.log(b) // 2, 3, 4
reduce实现数组的map方法 *
利用数组内置的reduce方法实现map方法,考察对reduce原理的掌握。
Array.prototype.myMap = function(fn,thisValue){var res = [];thisValue = thisValue||[];this.reduce(function(pre,cur,index,arr){return res.push(fn.call(thisValue,cur,index,arr));},[]);return res;}var arr = [2,3,1,5];arr.myMap(function(item,index,arr){console.log(item,index,arr);})
手写数组的reduce方法 *
reduce方法接受一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值,是ES5中新增的又一个数组逐项处理方法。
参数:**
- callback(一个在数组中每一项上调用的函数,接受四个函数:)
- previousValue(上一次调用回调函数时的返回值,或者初始值)
- currentValue(当前正在处理的数组元素)
- currentIndex(当前正在处理的数组元素下标)
- array(调用reduce()方法的数组)
- initialValue(可选的初始值。作为第一次调用回调函数时传给previousValue的值)
**
function reduce(arr, cb, initalValue) {var num = initalValue == undefined ? num = arr[0] : initalValuevar i = initalValue == undefined ? 1 : 0for(i; i < arr.length;i ++){num = cb(num,arr[i],i)}return num}function fn(result, currentValue, index){return result + currentValue}var arr = [2, 3, 4, 5]var b = reduce(arr, fn, 10)var c = reduce(arr, fn)console.log(b) //24
数组扁平化
1、ES6提供的新方法 flat(depth)
let a = [1,[2,3]]a.flat()
2、利用concat
function flatten(arr) {var res = [];for (let i = 0, length = arr.length; i < length; i++) {if (Array.isArray(arr[i])) {res = res.concat(flatten(arr[i])); //concat 并不会改变原数组//res.push(...flatten(arr[i])); //或者用扩展运算符} else {res.push(arr[i]);}}return res;}let arr1 = [1, 2,[3,1],[2,3,4,[2,3,4]]]flatten(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
3、函数柯里化
柯里化定义: 接受一部分参数,返回一个函数接受剩余参数,接受足够参数后,执行原函数。
当柯里化函数接收到足够参数之后,就会执行原函数,如何去确定何时达到足够的参数有两种思路。
1、 通过函数的length属性,获取函数的形参个数,形参的个数就是所需的参数个数。
2、在调用柯里化工具函数时,手动指定所需的参数个数。
/*** 将函数柯里化* @param fn 待柯里化的原函数* @param len 所需的参数个数,默认为原函数的形参个数*/function curry(fn,len = fn.length) {return _curry.call(this,fn,len)}/*** 中转函数* @param fn 待柯里化的原函数* @param len 所需的参数个数* @param args 已接收的参数列表*/function _curry(fn,len,...args) {return function (...params) {let _args = [...args,...params];if(_args.length >= len){return fn.apply(this,_args);}else{return _curry.call(this,fn,len,..._args)}}}let _fn = curry(function(a,b,c,d,e){console.log(a,b,c,d,e)});_fn(1,2,3,4,5); // print: 1,2,3,4,5_fn(1)(2)(3,4,5); // print: 1,2,3,4,5_fn(1,2)(3,4)(5); // print: 1,2,3,4,5_fn(1)(2)(3)(4)(5); // print: 1,2,3,4,5
实现深拷贝
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型。
浅拷贝和深拷贝的区别:**
浅拷贝: 只拷贝引用,新旧对象仍共享同一份地址,修改新对象会影响到旧对象
深拷贝: 拷贝引用和内存地址,修改新对象不影响旧对象。
**
1、转化为Json格式然后再解析
const a = JSON.parse(JSON.stringify(b))
2、
//递归实现深拷贝function deepCopy(newObj,oldObj){for(var k in oldObj){let item = oldObj[k]// 判断是数组、对象、简单类型if(item instanceof Array){newObj[k] = []deepCopy(newObj[k],item)}else if(item instanceof Object){newObj[k] = {}deepCopy(newObj[k],item)}else{newObj[k] = item}}return newObj}
手写call、apply、bind
手写call
Function.prototype.myCall=function(context=window){ // 函数的方法,所以写在Fuction原型对象上if(typeof this !=="function"){ // 这里if其实没必要,会自动抛出错误throw new Error("不是函数")}const obj=context||window //这里可用ES6方法,为参数添加默认值,js严格模式全局作用域this为undefinedobj.fn=this //this为调用的上下文,this此处为函数,将这个函数作为obj的方法const arg=[...arguments].slice(1) //第一个为obj所以删除,伪数组转为数组res=obj.fn(...arg)delete obj.fn // 不删除会导致context属性越来越多return res}//用法:f.call(obj,arg1)function f(a,b){console.log(a+b)console.log(this.name)}let obj={name:1}f.myCall(obj,1,2) //否则this指向windowobj.greet.call({name: 'Spike'}) //打出来的是 Spike
手写apply
Function.prototype.myApply=function(context){ // 箭头函数从不具有参数对象!!!!!这里不能写成箭头函数let obj=context||windowobj.fn=thisconst arg=arguments[1]||[] //若有参数,得到的是数组let res=obj.fn(...arg)delete obj.fnreturn res}function f(a,b){console.log(a,b)console.log(this.name)}let obj={name:'张三'}f.myApply(obj,[1,2]) //arguments[1]
手写bind
this.value = 2var foo = {value: 1};var bar = function(name, age, school){console.log(name) // 'An'console.log(age) // 22console.log(school) // '家里蹲大学'}var result = bar.bind(foo, 'An') //预置了部分参数'An'result(22, '家里蹲大学') //这个参数会和预置的参数合并到一起放入bar中
手动实现new
- 创建一个空对象 obj;
- 将空对象的隐式原型(proto)指向构造函数的prototype。
- 使用 call 改变 this 的指向
- 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。
function Person(name,age){this.name=namethis.age=age}Person.prototype.sayHi=function(){console.log('Hi!我是'+this.name)}let p1=new Person('张三',18)////手动实现newfunction create(){let obj={}//获取构造函数let fn=[].shift.call(arguments) //将arguments对象提出来转化为数组,arguments并不是数组而是对象 !!!这种方法删除了arguments数组的第一个元素,!!这里的空数组里面填不填元素都没关系,不影响arguments的结果 或者let arg = [].slice.call(arguments,1)obj.__proto__=fn.prototypelet res=fn.apply(obj,arguments) //改变this指向,为实例添加方法和属性//确保返回的是一个对象(万一fn不是构造函数)return typeof res==='object'?res:obj}let p2=create(Person,'李四',19)p2.sayHi()[].shift.call(arguments) 也可写成:let arg=[...arguments]let fn=arg.shift() //使得arguments能调用数组方法,第一个参数为构造函数obj.__proto__=fn.prototype//改变this指向,为实例添加方法和属性let res=fn.apply(obj,arg)
