在项目中经常会遇到将后端传回来的多个对象,合并成一个对象的问题,我们通常会用到Object.assign()
函数,那我们如何仿写一个这个函数呢?
/**
* 对象混合,将第二个参数开始的对象,混合到第一个对象中
*/
function minxin(obj){
//当只有一个对象的时候返回
if(arguments.length <=1) return obj
//获取参数对象列表,这里我们假设已经按照要求传过来的是对错
var args = Array.from(arguments)
//循环这个列表。
for(var i=1;i<args.length;i++){
for(var prop in args[i]){
//将列表中的每个属性,复制到obj中,也就是我们的第一个参数
obj[prop] = args[i][prop]
}
}
return obj
}
我们经常会有深克隆一个对象的需求,除了loadsh中的克隆方法,你自己会写一个深克隆么?下面呢,是我封装的可以深浅克隆对象的方法
/**
* 克隆一个对象
* @param {boolean} deep 是否深度克隆
*/
function clone(obj, deep) {
if (Array.isArray(obj)) {//如果是数组
if (deep) { //深克隆
var newArr = []
//此时要考虑数组中可能含有对象数据项,所以循环遍历每一个数据项对其克隆
for (var i = 0; i < obj.length; i++) {
return clone(obj[i], true)
}
return newArr
} else {//浅克隆,直接返回一个新的数组即可
return obj.slice()
}
} else if (typeof obj === "object") {//是一个对象
var newObj = {}
for (var key in obj) {
if (obj.hasOwnProperty(key)) { //不克隆原型链上的值
if (deep) {//深克隆也要考虑对象中的属性还是一个对象
newObj[key] = clone(obj[key], true)
} else {
newObj[key] = obj[key]
}
}
}
return newObj
} else { //一个普通值
return obj
}
}
函数防抖也是我们在一个网站中经常会用的到的方法。那么什么是函数防抖呢?其实很简单,就是我们在一段时间内频繁触发同一个事件,可能会十分的影响性能,所以,我们想要在一段时间内只触发一次。这就要用到防抖和节流。举个例子,防抖是频繁触发后,1s内没有再触发时才调用事件处理程序。
/**
* 函数防抖
*/
function debounce(callback, time) {
var timer;
//使用闭包,可以避免污染全局变量
return function () {
clearTimeout(timer) //清除计时器
//重新开始计时
var args = arguments
timer = setTimeout(() => {
callback.apply(null, args)
}, time);
}
}
//测试案例
var handle = debounce(function (a, b) {
console.log(a, b)
}, 1000)
window.onresize = function () {
handle(1, 2)
}
那么和防抖相对应的就是节流了,节流就是在一段时间内只触发一次!
/**
* 函数节流
*/
function throttle(callback, time, immediately) {
if (immediately) {//是否立即执行一次
var t;
return function () {
//之前没有计时 或 距离上次执行的时间已超过规定的值
if (!t || Date.now() - t >= time) {
callback.apply(null, arguments)
//得到的当前时间戳
t = Date.now()
}
}
} else {//如果不立即执行一次,那么这个时间让他延迟多长时间后再执行
var timer;
return function () {
if (timer) return
var args = arguments
timer = setTimeout(() => {
callback.apply(null, args)
timer = null
}, time);
}
}
}
科里化是把一个多参函数,变为单参函数的一个方法,可以说是在JavaScript中最骚的操作之一了,毕竟在函数式编程中,单参函数的调用要比多参函数调用方便的多。
/**
* 柯里化函数
* 在函数式编程中,柯里化最重要的作用是把多参函数变为单参函数
*/
function curry(func) {
//获取参数,把传过来的参数固定
var args = Array.from(arguments).slice(1)
var that = this
return function () {
var curArgs = Array.from(arguments)
var totalArgs = args.concat(curArgs)
if (totalArgs.length >= func.length) {
return func.apply(null, totalArgs)
} else {
totalArgs.unshift(func)
return curry.apply(that, totalArgs)
}
}
}
function f(x, y, z) {
return (x + y) * z;
}
var g = curry(f, 1)
var x = g(2)
console.log(x(3))
柯里化demo
<style>
.container {
border: 2px solid;
padding: 30px;
}
style>
<div class="container">
div>
<script>
function createElement(container, name, props, styles, content) {
var ele = document.createElement(name);
container.appendChild(ele);
for (var prop in props) {
ele[prop] = props[prop];
}
for (var prop in styles) {
ele.style[prop] = styles[prop];
}
if (content) {
ele.innerHTML = content;
}
}
var div = document.querySelector(".container");
var create = curry(createElement, div, "div", {}, {
height: "100px",
background: "#008c8c",
margin: "30px",
color:"#fff"
});
create("第1个内容");
create("第2个内容");
create("第3个内容");
create("第4个内容");
create("第5个内容");
create("第6个内容");
create("第7个内容");
script>
当我们遇到多个函数连续调用,切下一个函数的参数是上一个函数的返回结果,此时就可以用到单参函数管道,他可以简化我们调用的过程,使调用更为简单。
/**
* 函数管道
* 方法一
*/
function pipe() {
//获取管道函数列表
var args = Array.from(arguments);
return function (val) {
//巧妙使用reduce求和函数,将上一个函数的结算结果,当做下一个函数的参数
return args.reduce(function (result, func) {
return func(result);
}, val);
}
}
/**
* 函数管道
* 方法二
*/
function pipe() {
//获取管道函数列表
var args = Array.from(arguments);
return function (val) {
//通过一个循环遍历每一个函数,并将上一个函数的计算结果保存下来
for (var i = 0; i < args.length; i++) {
var func = args[i];
//将上一个函数保留下来的结果,给下一个函数当作参数使用
val = func(val);
}
return val;
}
}
我们都知道函数中的apply、call和bind是可以改变函数中的this指向,那么我们怎么自己来实现一个改变this指向的方法。
Function.prototype.newApply = function(ctx,arr){
//ctx:执行期上下文,即我们要绑定的this指向
var ctx = ctx
//在ctx中创建一个临时变量fn存放当前的this
ctx.fn = this
var result
if(!arr){
result = ctx.fn()
}else{
var args = []
for(var i=0;i<arr.length;i++){
args.push("arr["+i+"]")
}
result = eval('ctx.fn('+args.join(',') + ')')
}
//删除临时变量fn
delete ctx.fn
return result
}
//测试用例
let person = {
name:"zhang"
}
function obj(name,age){
console.log(this.name)
return {
name:name,
age:age
}
}
var newObj = obj.newApply(person,['zhangsan','30'])
console.log(newObj)
我们都知道typeof 可以得到值的类型,但是这个方法并不准确,那么我们自己实现一个type方法
function type(target){
//定义一个输出字典
var template = {
"[object Array]" : "array",
"[object Object]" : "object",
"[object Number]" : "number - object",
"[object String]" : "string - object",
"[object Boolean]" : "boolean - object"
}
if(typeof(target) === null){ //解决null是object的问题
return null;
}
if(typeof(target) == "object"){
var str = Object.prototype.toString(target)
return template[str];
}else{
return typeof(target);
}
}
数组去重的方法千千万,但是这种方法不知道你会不会呢?我们通过一个临时对象的方法来存储数组中的的每一个值
// 数组去重
Array.prototype.unique = function(){
var temp = {},
arr = [],
len = this.length;
for(var i=0;i<len;i++){
if(!temp[this[i]]){ // undefined 取反
temp[this[i]] = "a";
arr.push(this[i]);
}
}
return arr;
}
使用prototype实现继承你再不会,那你还怎么进大厂
/*
* Target是继承者 Origin是被继承者
*/
function inherit(Target,Origin){
//通过创建一个新的F构造函数作为中间层
// 防止继承者修改prototype时对被继承者产生影响
function F(){}
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constuctor = Target;
Target.prototype.uber = Origin.protoype;
}
/*
* yahoo 中提供的圣杯模式
*/
var inherit = (function(){
var F = function(){}; //中间层F 将构造函数F变为私有化变量
return function(Target,Origin){ //返回继承函数
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constuctor = Target;
Target.prototype.uber = Origin;
};
}())