5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数

这篇具有很好参考价值的文章主要介绍了5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

浅拷贝与深拷贝

浅拷贝

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

//实现浅拷贝
function shallowCopy (obj){
    // 只拷贝对象,基本类型或null直接返回
    if(typeof obj !== 'object' || obj === null) {
    	return obj;
    }
    // 判断是新建一个数组还是对象
    let newObj = Array.isArray(obj) ? []: {};
    //for…in会遍历对象的整个原型链,如果只考虑对象本身的属性,需要搭配hasOwnProperty
    for(let key in obj ){
        //hasOwnProperty判断是否是对象自身属性,会忽略从原型链上继承的属性
        if(obj.hasOwnProperty(key)){
        	newObj[key] = obj[key];//只拷贝对象本身的属性
        }
    }
    return newObj;
}
 
//测试
var obj ={
    name:'张三',
    age:8,
    pal:['王五','王六','王七']
}
let obj2 = shallowCopy(obj);
obj2.name = '李四'
obj2.pal[0] = '王麻子'
console.log(obj); //{age: 8, name: "张三", pal: ['王麻子', '王六', '王七']}
console.log(obj2); //{age: 8, name: "李四", pal: ['王麻子', '王六', '王七']}

测试结果:

5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数,前端,javascript,开发语言

深拷贝

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

function deepCopy (obj, map = new WeakMap()){
    // 基本类型或null直接返回
    if(typeof obj !== 'object' || obj === null) {
    	return obj;
    }
    // 判断是新建一个数组还是对象
    let newObj = Array.isArray(obj) ? []: {};
    //利用map解决循环引用
    if (map.has(obj)) {
        return map.get(obj);
    }
    map.set(obj, newObj);//将当前对象作为key,克隆对象作为value
    for(let key in obj ){	
        if(obj.hasOwnProperty(key)){
        	newObj[key] = deepCopy(obj[key], map);	//递归
        }
    }
    return newObj
}
 
// 测试
let obj1 = {
    name : 'AK、哒哒哒',
    arr : [1,[2,3],4],
};
let obj2=deepCopy(obj1)
obj2.name = "哒哒哒";
obj2.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存
 
console.log('obj1',obj1) // obj1 { name: 'AK、哒哒哒', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj2',obj2) // obj2 { name: '哒哒哒', arr: [ 1, [ 5, 6, 7 ], 4 ] }

测试结果:

5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数,前端,javascript,开发语言

函数柯里化

函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

作用:可以参数复用(公共的参数已经通过柯里化预置了)和延迟执行(柯里化时只是返回一个预置参数的新函数,并没有立刻执行,在满足条件后才会执行)。

参数定长的柯里化

思路:通过函数的length属性获取函数的形参个数,形参的个数就是所需参数的个数。维护一个数组,当数组的长度与函数接收参数的个数一致,再执行该函数。

// 实现函数柯里化
function curry(fn) {
  // 返回一个新函数
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args); // 如果参数够了,就执行原函数,返回结果
    } else {
      //返回一个新函数,继续递归去进行柯里化,利用闭包,将当前已经传入的参数保存下来
      return function (...args2) {
        //递归调用 curried 函数
        return curried.apply(this, [...args, ...args2]); //新函数调用时会继续传参,拼接参数
      };
    }
  };
}
 
// 测试
function sum(a, b, c) {
  return a + b + c;
}
var curried = curry(sum);
console.log(curried(1, 2, 3)); //6
console.log(curried(1, 2)(3)); //6
console.log(curried(1)(2, 3)); //6
console.log(curried(1)(2)(3)); //6

测试结果:

5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数,前端,javascript,开发语言

参数不定长的柯里化

题目:如何实现一个方法,使计算结果能够满足如下预期。

add(1, 2, 3) // 6
add(1) // 1
add(1)(2) // 3
add(1, 2)(3) // 6
add(1)(2)(3) // 6
add(1)(2)(3)(4) // 10

思路:利用闭包和递归,如果参数为空,则判断递归结束,求和,返回结果。


function addCurry() {
    // 利用闭包的特性收集所有参数值
    let arr = [...arguments]	
    //返回函数
    return function fn() {
        // 如果参数为空,则判断递归结束,即传入一个()执行函数
        if(arguments.length === 0) {	
	    	return arr.reduce((a, b) => a + b)	// 求和
        } else {
            arr.push(...arguments)
            return fn	//递归
        }
    }
}
 
// 测试
console.log(addCurry(1)());	//1
console.log(addCurry(1)(2)());	//3
console.log(addCurry(1)(2)(3)());	//6
console.log(addCurry(1, 2)(3)());	//6
console.log(addCurry(1, 2, 3)());	//6

上述写法,总是要以空括号()结尾,于是再改进为隐式转换.toString写法,原理:当用 Function的值做计算的时候,会调用toString做隐式转换。注意一些旧版本的浏览器隐式转换会默认执行,新版本不行了。可以利用隐式转换或者alert。

function addCurry() {
    let arr = [...arguments]
    // 利用闭包的特性收集所有参数值
    var fn = function() {
        arr.push(...arguments);
        return fn;//递归
    };
    // 利用 toString 隐式转换,转换的时候再返回结果
    fn.toString = function () {
        return arr.reduce(function (a, b) {
            return a + b;
        });
    }
    return fn;
}
 
//测试
console.log(addCurry(1)(2) == 3) //true 利用隐式转换,自动调用toString方法得到柯里化的结果
//alert(addCurry(1)(2)(3))//6 alert参数只能是字符串,如果其他类型的值,会转换成字符串,会调用toString方法
console.log(addCurry(1).toString());//1 手动调用toString
console.log(addCurry(1, 2)(3).toString());//6 
console.log(addCurry(1, 2)(3)(4)(5).toString());//15 

测试结果:

5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数,前端,javascript,开发语言

数组扁平化

数组扁平化其实就是将多维数组转为一维数组。

ES6中的flat

const arr = [1,[2,[3,[4,5]]],6]
//  arr.flat([depth]) flat的参数代表的是需要展开几层,如果是Infinity的话,就是不管嵌套几层,全部都展开
console.log(arr.flat(Infinity)) //[1,2,3,4,5,6]

递归

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    //如果当前元素还是一个数组
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));//递归拼接
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}
console.log(flatten(arr)); //  [1, 2, 3, 4]

reduce函数迭代

从上面普通的递归函数中可以看出,其实就是对数组的每一项进行处理,那么其实也可以用reduce来实现数组的拼接,从而简化第一种方法的代码。

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  return arr.reduce((total, cur) => {
    return total.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, []); //传递初始值空数组[],就会从数组索引为 0 的元素开始执行
}
console.log(flatten(arr)); //  [1, 2, 3, 4]

split和toString

数组的toString方法可以把数组直接转换成逗号分隔的字符串。如[1, [2, [3, 4]]] => “1,2,3,4”

let arr = [1, [2, [3, 4]]];
function flatten(arr) {
  //先把数组直接转换成逗号分隔的字符串,然后再用 split 方法把字符串重新转换为数组
  return arr.toString().split(",").map(Number); 
}
console.log(flatten(arr)); //  [ 1, 2, 3, 4 ]

数组去重

利用Set。new一个Set,参数为需要去重的数组,Set会自动删除重复的元素,在Array.form将Set转为数组返回

const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log([...new Set(arr)]); //[ 1, 2, 3, 5, 9, 8 ]
console.log(Array.from(new Set(arr))); //[ 1, 2, 3, 5, 9, 8 ]

利用数组的filter()+indexOf去重。利用filter方法,返回arr.indexOf(num)等于index的值。原理就是indexOf会返回先找到的数字的索引。


function unique(arr) {
  return arr.filter((item, index, array) => {
    return array.indexOf(item) === index;
  });
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]

利用Map。新建一个数组和map,如果当前值在map中没有出现过,就加入数组,最后返回数组

const unique = (arr) => {
    const map = new Map();
    const res = [];
    for (let item of arr) {
        if (!map.has(item)) {
            map.set(item, true);
            res.push(item);
        }
    }
    return res;
}
const arr = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
console.log(unique(arr)); // [1, 2, 3, 5, 9, 8]

手写类型判断函数

思路:如果是null,直接返回String(null);基本类型和函数,直接使用typeof;其它引用类型,使用Object.prototype.toString.call。

function getType(value) {
  // 判断数据是 null 的情况
  let type;
  if (value === null) {
    return String(value);
  }
  // 判断数据是基本数据类型的情况和函数的情况,使用typeof
  if (typeof value !== "object") {
    return typeof value;
  } else {
    // 判断数据是引用类型的情况,设当前类型为date
    let valueClass = Object.prototype.toString.call(value); //"[object Date]"
    type = valueClass.split(" ")[1].split(""); //[ 'D', 'a', 't', 'e', ']' ] 截取类型并转换为数组
    type.pop(); //[ 'D', 'a', 't', 'e' ],去掉数组最后的右括号"]"
    return type.join("").toLowerCase(); //[ 'D', 'a', 't', 'e' ] => "Date" => "date" 数组转小写字符串
  }
}

// 测试
console.info(getType(null)); // null
console.info(getType(undefined)); // undefined
console.info(getType(100)); // number
console.info(getType("abc")); // string
console.info(getType(true)); // boolean
console.info(getType(Symbol())); // symbol
console.info(getType({})); // object
console.info(getType([])); // array
console.info(getType(() => {})); // function
console.info(getType(new Date())); // date
console.info(getType(new RegExp(""))); // regexp
console.info(getType(new Map())); // map
console.info(getType(new Set())); // set
console.info(getType(new WeakMap())); // weakmap
console.info(getType(new WeakSet())); // weakset
console.info(getType(new Error())); // error
console.info(getType(new Promise(() => {}))); // promise

测试结果:
5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数,前端,javascript,开发语言文章来源地址https://www.toymoban.com/news/detail-858820.html

到了这里,关于5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 详解js中的浅拷贝与深拷贝

    1.1 栈(stack)和堆(heap) 栈(stack):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈; 堆(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表 1.2 基本数据类型和引用数据类

    2024年02月08日
    浏览(86)
  • Python:列表的浅拷贝与深拷贝

    相关阅读 Python专栏 https://blog.csdn.net/weixin_45791458/category_12403403.html?spm=1001.2014.3001.5482         在python语言中,因为其面向对象的特性,在进行列表拷贝时可能会出现一些意想不到的结果,涉及到列表的浅拷贝和深拷贝相关问题,本文将对其进行总结。         首先我们来

    2024年02月09日
    浏览(35)
  • Pandas数据中的浅拷贝与深拷贝

    pandas库主要有两种数据结构DataFrames和Series。这些数据结构在内部用索引数组和数据数组表示,索引数组标记数据,数据数组包含实际数据。现在,当我们试图复制这些数据结构(DataFrames和Series)时,我们实际上是复制对象的索引和数据,有两种方法可以做到这一点,即浅复

    2024年02月09日
    浏览(37)
  • 前端常见面试题之js基础(手写深拷贝、原型和原型链、作用域和闭包)

    值类型包括 :字符串(string)、数字(number)、布尔值(boolean)、undefined。 引用类型包括 :对象(object)、数组(array)、函数(function)和null。 二者的区别 当你将一个值类型赋给另一个变量时,会复制该值的副本。而当你将一个引用类型赋给另一个变量时,只会复制对

    2024年01月22日
    浏览(47)
  • [开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

    写在前面 :Python和C++中的赋值与深浅拷贝,由于其各自语言特性的问题,在概念和实现上稍微有点差异,本文将这C++和Python中的拷贝与赋值放到一起,希望通过对比学习两语言实现上的异同点,加深对概念的理解。 C++中所谓的 浅拷贝 就是由(系统默认的) 拷贝构造函数对

    2024年02月02日
    浏览(55)
  • 高阶函数和函数柯里化

            高阶函数是指可以接受一个或多个函数作为参数,并且/或者返回一个新函数的函数。换句话说,它可以操作其他函数,使得代码更加抽象和灵活。高阶函数的存在使得函数可以作为一等公民在代码中传递和使用。         如下的函数就是一个高阶函数,他可以传

    2024年02月13日
    浏览(49)
  • JS之函数柯里化

    维基百科中对柯里化 (Currying) 的定义为: In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument. 翻译成中文: 在数学和计算机科学中,柯里化是一种将使

    2024年01月17日
    浏览(35)
  • 【学姐面试宝典】前端基础篇Ⅴ——JS深浅拷贝、箭头函数、事件监听等

    前言 博主主页👉🏻蜡笔雏田学代码 专栏链接👉🏻【前端面试专栏】 今天继续学习前端面试题相关的知识! 感兴趣的小伙伴一起来看看吧~🤞 addEventListener()方法,用于向指定元素添加事件句柄,它可以更简单的控制事件。语 法为: 第一个参数是 事件的类型 (如 “click”

    2024年01月19日
    浏览(44)
  • 【es6】函数柯里化(Currying)

    柯里化(Currying):把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数。 柯里化由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,它是 Moses Schnfinkel 和 Gottlob Frege 发明的。 柯里化的应用最为切底是纯

    2024年02月12日
    浏览(27)
  • 【JavaScript】面试手撕柯里化函数

    🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​ 💫个人格言: \\\"如无必要,勿增实体\\\" 上周我一个学弟,去字节面试实习生。面试官问他有没有用过柯里化,他摇了摇头。 有一说一,确实柯里化在现实中的项目还是用的比较少的。🐶 面试官继续问他是

    2024年03月15日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包