跟chatGPT一起复习前端 —— 手写方法

69 阅读13分钟

前言

又到一波面试准备的时刻,整理了一波前端相关要点内容,配合chatGPT完成要点内容的整理输出,有纠正错误和需要补充的小伙伴可以在这里留言,及时更新。

javascript基础

实现数组flat方法

function flatten(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}

这个函数会遍历数组,对于每个元素都进行判断,如果是数组,则递归调用自身,将返回的结果合并到结果数组中;否则直接将该元素添加到结果数组中。这样就可以实现数组的展平了。

实现数组filter方法

function filter(arr, callback) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    if (callback(arr[i])) {
      result.push(arr[i]);
    }
  }
  return result;
}

这个函数会遍历数组,对于每个元素都调用指定的回调函数进行测试,如果测试通过,则将该元素添加到结果数组中。这样就可以实现数组的筛选了。

实现数组sort方法

实现一个简单的冒泡排序算法。以下是代码:

Array.prototype.sort = function() {
  for (var i = 0; i < this.length; i++) {
    for (var j = 0; j < this.length - i - 1; j++) {
      if (this[j] > this[j + 1]) {
        var temp = this[j];
        this[j] = this[j + 1];
        this[j + 1] = temp;
      }
    }
  }
}

这个算法使用了两个嵌套的循环来比较和交换数组中的元素。它的时间复杂度为 O(n^2),因此对于大型数组来说可能不是最优的选择。但是,它很容易理解和实现,对于小型数组来说是一个不错的选择。

实现数组map方法

function map(arr, fn) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push(fn(arr[i], i, arr));
  }
  return result;
}

这个函数会遍历数组,对于每个元素都调用传入的函数,将返回的结果添加到结果数组中。这样就可以实现数组的映射了。

实现数组reduce方法

function reduce(arr, fn, initialValue) {
  var result = initialValue;
  for (var i = 0; i < arr.length; i++) {
    result = fn(result, arr[i], i, arr);
  }
  return result;
}

这个函数会遍历数组,对于每个元素都调用传入的函数,将返回的结果作为下一次调用的第一个参数。这样就可以实现数组的缩减了。

实现类数组转化为数组

var arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3};
var arr = Array.prototype.slice.call(arrayLike);
console.log(arr); // ['a', 'b', 'c']

这个函数会将类数组对象转化为数组对象。它通过调用Array.prototype.slice()方法并传入类数组对象作为参数来实现。

实现字符串翻转方法

有很多方法可以实现字符串翻转。下面是一些例子:

  1. 使用split()、reverse()和join()方法
var str = "abcdef";
console.log(str.split("").reverse().join("")); // "fedcba"
  1. 使用for循环
var str = "abcdef";
var reversedStr = "";
for (var i = str.length - 1; i >= 0; i--) {
  reversedStr += str[i];
}
console.log(reversedStr); // "fedcba"
  1. 使用递归
function reverseString(str) {
  if (str === "") {
    return "";
  } else {
    return reverseString(str.substr(1)) + str.charAt(0);
  }
}
console.log(reverseString("abcdef")); // "fedcba"

函数柯理化

函数柯里化是一种将接受多个参数的函数转换为接受单一参数的函数并返回新函数的技术。这个技术可以让我们更方便地创建可复用的函数。下面是一个例子:

function add(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = add(5);
console.log(add5(3)); // 8
console.log(add5(10)); // 15

在这个例子中,我们定义了一个add()函数,它接受一个参数x并返回一个新函数,该新函数接受一个参数y并返回x + y的结果。我们可以使用add()函数创建一个新函数add5,该函数将5添加到其参数中。然后,我们可以使用add5()函数将5添加到任何数字中。

实现Object.creat

Object.create = function(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
};

这个方法的实质是新建一个空的构造函数F,然后让F.prototype属性指向参数对象obj,最后返回一个F的实例,从而实现让该实例继承obj的属性。这与new一个实例对象是不一样的。Object.create创造的实例,私有属性是空的,但是会继承目标对象的属性。

实现instanceof方法

function myInstanceof(obj, constructor) {
  while (obj !== null) {
    if (obj.__proto__ === constructor.prototype) {
      return true;
    }
    obj = obj.__proto__;
  }
  return false;
}

这个方法的实质是检查一个对象的原型链是否包含指定构造函数的prototype属性。如果包含,返回true,否则返回false。

实现new操作符

function myNew() {
  let obj = new Object();
  let Constructor = [].shift.call(arguments);
  obj.__proto__ = Constructor.prototype;
  let ret = Constructor.apply(obj, arguments);
  return typeof ret === 'object' ? ret : obj;
}

这个方法的实质是创建一个新对象,将该对象的__proto__属性指向构造函数的prototype属性,然后将构造函数的this指向该对象,并执行构造函数。如果构造函数返回一个对象,则返回该对象,否则返回新创建的对象。

实现防抖函数

防抖函数是一种可以限制函数执行频率的方法,它可以在一段时间内忽略连续的重复操作,只在最后一次操作后执行回调函数。防抖函数的实现方法是利用 clearTimeout 和 setTimeout 来实现。下面是一个防抖函数的例子:

function debounce (callback,delay) {
  var timer = null;
  return function () {
    var args = arguments;
    var that = this;
    clearTimeout (timer);
    timer = setTimeout (function () {
      callback.apply (that, args);
    }, delay);
  };
};

你可以将需要防抖的函数作为参数传递给 debounce 函数,然后将返回的函数绑定到事件上。例如:

function dd () {
  console.log ('dd');
}
window.onresize = debounce (dd,500);

这样,当你调整窗口大小时,dd 函数将在 500 毫秒后执行。

实现节流函数

节流函数是一种可以限制函数执行频率的方法,它可以在一定时间间隔内(例如 3 秒)只执行一次,在这段时间内无视后来产生的函数调用请求。节流函数的实现方法有多种,常用的方法有时间戳和定时器两种方式。下面是一个节流函数的例子:

function throttle (callback,delay) {
  var timer = null;
  return function () {
    var args = arguments;
    var that = this;
    if (!timer) {
      timer = setTimeout (function () {
        callback.apply (that, args);
        timer = null;
      }, delay);
    }
  };
};

你可以将需要节流的函数作为参数传递给 throttle 函数,然后将返回的函数绑定到事件上。例如:

function dd () {
  console.log ('dd');
}
window.onresize = throttle (dd,500);

这样,当你调整窗口大小时,dd 函数将在每隔 500 毫秒后执行。

实现call函数

call 方法是 JavaScript 中的一个内置函数,它可以在一个特定的上下文中调用一个函数。call 方法的第一个参数是要绑定到函数上下文的对象,后面的参数是传递给函数的参数。下面是一个 call 函数的例子:

Function.prototype.call = function (context) {
  context = context ? Object (context) : window;
  context.fn = this;
  var args = [...arguments].slice (1);
  var result = context.fn (...args);
  delete context.fn;
  return result;
};

你可以将需要绑定到上下文中的函数作为参数传递给 call 函数,然后将需要绑定到函数上下文中的对象作为第一个参数传递给 call 函数。例如:

var person = {
  name: '张三',
};
var obj = {
  name: '李四',
};
function greeting () {
  console.log ('你好,' + this.name);
}
greeting.call (person); // 输出:你好,张三
greeting.call (obj); // 输出:你好,李四

实现apply

apply() 方法用于调用函数,它的第一个参数是作为函数体内 this 对象的值,第二个参数是一个数组或类数组对象,它们将作为参数传递给被调用的函数。

以下是一个简单的 apply() 方法实现:

Function.prototype.myApply = function (context) {
  if (typeof this !== "function") {
    throw new TypeError ("Error");
  }
  let result = null ;
  context = context || window ;
  context.fn = this ;
  if (arguments [ 1 ]) {
    result = context.fn (...arguments [ 1 ]);
  } else {
    result = context.fn ();
  }
  delete context.fn;
  return result;
};

实现bind函数

bind() 方法会创建一个新函数。 当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

以下是一个简单的 bind() 函数实现:

Function.prototype.bind2 = function (context) {
  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);
  return function () {
    var bindArgs = Array.prototype.slice.call(arguments);
    self.apply(context, args.concat(bindArgs));
  };
};

你可以在这个函数中传入一个 context 参数,它会作为 this 的值。除此之外,你还可以传入其他参数,它们会被当做原函数的参数传入。这个函数返回了一个新函数,你可以调用它。

实现深拷贝

深拷贝是指在拷贝的时候,需要将当前要拷贝的对象内的所有引用类型的属性进行完整的拷贝,也就是说拷贝出来的对象和原对象之间没有任何数据是共享的,所有的东西都是自己独占的一份。

JavaScript实现深拷贝有多种方法。其中一种是使用JSON对象的parse和stringify方法来实现深拷贝,这是一种简单的方法。另外,可以使用第三方库如jQuery的$.extend()方法来实现深拷贝。还有一种自己写的方法,需要考虑如下几个因素:对象类型、数组类型、Date类型、RegExp类型、Function类型、Symbol类型等等。

这里给出一个利用JSON对象处理函数的简易深拷贝方法:const obj = { } JSON.parse(JSON.stringify(obj))。这个方法适用于90%以上的应用场景。

能够适应100%的应用场景的深拷贝方法,建议使用递归的方式实现深拷贝:

function deepClone(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  let result;
  if (Array.isArray(obj)) {
    result = [];
  } else {
    result = {};
  }
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      result[key] = deepClone(obj[key]);
    }
  }
  return result;
}

这个方法可以适用于大部分的场景,但是需要注意一些细节问题,比如循环引用等。

实现浅拷贝

以下是 JavaScript 实现浅拷贝的代码示例:

let obj1 = { a: 1, b: 2 };
let obj2 = Object.assign({}, obj1);

这个方法可以用于简单的对象和数组。

实现Ajax请求

以下是 JavaScript 实现 Ajax 请求的代码示例:

let xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};
xhr.send();

这个方法可以用于简单的请求,但是需要注意一些细节问题,比如跨域等。

实现Promise

以下是 JavaScript 实现 Promise 的代码示例:

function Promise(fn) {
  var state = 'pending';
  var value;
  var deferred = null;

  function resolve(newValue) {
    if (newValue && typeof newValue.then === 'function') {
      newValue.then(resolve);
      return;
    }
    value = newValue;
    state = 'resolved';

    if (deferred) {
      handle(deferred);
    }
  }

  function reject(reason) {
    state = 'rejected';
    value = reason;

    if (deferred) {
      handle(deferred);
    }
  }

  function handle(handler) {
    if (state === 'pending') {
      deferred = handler;
      return;
    }

    var handlerCallback;
    if (state === 'resolved') {
      handlerCallback = handler.onResolved;
    } else {
      handlerCallback = handler.onRejected;
    }

    if (!handlerCallback) {
      if (state === 'resolved') {
        handler.resolve(value);
      } else {
        handler.reject(value);
      }
      return;
    }

    var ret;
    try {
      ret = handlerCallback(value);
    } catch (e) {
      handler.reject(e);
      return;
    }

    handler.resolve(ret);
  }

  this.then = function(onResolved, onRejected) {
    return new Promise(function(resolve, reject) {
      handle({
        onResolved: onResolved,
        onRejected: onRejected,
        resolve: resolve,
        reject: reject
      });
    });
  };

  fn(resolve, reject);
}

这个方法可以用于简单的异步操作,但是需要注意一些细节问题,比如错误处理等。

Promise.all实现

Promise.all() 方法接收一个 promise 对象的数组作为参数,当这个数组里的所有 promise 对象全部变为 resolve 或 有 reject 状态出现的时候,它才会去调用.then 方法,它们是并发执行的。

以下是一个简单的实现方式:

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let result = [];
    let count = 0;
    promises.forEach((promise, index) => {
      promise.then((value) => {
        result[index] = value;
        count++;
        if (count === promises.length) {
          resolve(result);
        }
      }).catch((error) => {
        reject(error);
      });
    });
  });
}

Promise.race实现

Promise.race() 方法接收一个 promise 对象的数组作为参数,当这个数组里的任意一个 promise 对象变为 resolve 或 有 reject 状态出现的时候,它就会去调用.then 方法,它们是并发执行的。

以下是一个简单的实现方式:

function promiseRace(promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then((value) => {
        resolve(value);
      }).catch((error) => {
        reject(error);
      });
    });
  });
}

数据处理

实现数组数据去重

JavaScript 数组去重的方法有很多种

// 利用Set
function unique(arr) {
  return Array.from(new Set(arr));
}

// 利用indexOf
function unique(arr) {
  var res = [];
  for (var i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) {
      res.push(arr[i]);
    }
  }
  return res;
}

// 利用includes
function unique(arr) {
  var res = [];
  for (var i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) {
      res.push(arr[i]);
    }
  }
  return res;
}

// 利用filter+indexOf
function unique(arr) {
  return arr.filter(function(item, index, arr) {
    return arr.indexOf(item) === index;
  });
}

// 利用Map
function unique(arr) {
  const map = new Map();
  return arr.filter((item) => !map.has(item) && map.set(item, true));
}

实现数组数据扁平化

JavaScript 中实现数组扁平化的方法有很多,以下是其中一些:

  • 利用栈结构来实现扁平化。先创建一个空数组 result 和一个初始栈 stack ,将数组参数 arr 展开后压入栈中。在栈非空的情况下,取出栈顶元素 next ,如果 next 是数组,则将其展开后压入栈中,否则将 next 添加到结果数组 result 中。
  • 利用递归来实现扁平化。遍历数组 arr ,如果当前元素是数组,则递归调用函数 flatDeep,否则将当前元素添加到结果数组 result 中。
  • 利用 reduce 方法来实现扁平化。遍历数组 arr ,如果当前元素是数组,则递归调用函数 flatDeep,否则将当前元素添加到结果数组 result 中。
// 利用栈结构来实现扁平化
function flatten(arr) {
  const result = [];
  const stack = [...arr];
  while (stack.length !== 0) {
    const next = stack.pop();
    if (Array.isArray(next)) {
      stack.push(...next);
    } else {
      result.push(next);
    }
  }
  return result.reverse();
}

// 利用递归来实现扁平化
function flatDeep(arr) {
  return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flatDeep(val)) : acc.concat(val), []);
}

查找数组中最大值、最小值

以下是 JavaScript 实现查找数组中最大值、最小值的两种方法:

  1. 使用 Math.max()Math.min() 方法
function getMaxOfArray(numArray) {
  return Math.max.apply(null, numArray);
}

function getMinOfArray(numArray) {
  return Math.min.apply(null, numArray);
}
  1. 排序之后,获取最大/最小值

    • 只比较找出最大值:假定数组中的第一个元素值最大 max,循环数组,将 max 与其他元素做比较,大的值赋给 max。循环结束之后,max 的值即为最大值。

实现数组乱序

function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

实现数组元素求和

  1. 利用递归,让数组元素不断相加求和:
function getArraySum(array) {
  if (array.length === 0) {
    return 0;
  } else {
    return array[0] + getArraySum(array.slice(1));
  }
}
  1. 利用 for 循环,让数组元素不断相加求和:
function getArraySum(array) {
  let sum = 0;
  for (let i = 0; i < array.length; i++) {
    sum += array[i];
  }
  return sum;
}
  1. 利用 forEach 遍历,让数组元素不断相加求和:
function getArraySum(array) {
  let sum = 0;
  array.forEach(function (value) {
    sum += value;
  });
  return sum;
}
  1. 使用 “eval(arr.join(”+“))” 语句求和:
function getArraySum(array) {
  return eval(array.join("+"));
}

实现日期格式化

JavaScript 中有很多方法可以格式化日期,以下是其中一些:

  1. 使用 toLocaleString() 方法格式化日期:
let date = new Date();
let formattedDate = date.toLocaleString();
  1. 使用 toISOString() 方法格式化日期:
let date = new Date();
let formattedDate = date.toISOString();
  1. 使用自定义函数格式化日期:
function formatDate(date) {
  let day = date.getDate();
  let month = date.getMonth() + 1;
  let year = date.getFullYear();
  return `${day}-${month}-${year}`;
}

let date = new Date();
let formattedDate = formatDate(date);

实现a,b的值交换,不能用临时变量

  1. 使用异或运算符
a = a ^ b;
b = a ^ b;
a = a ^ b;
  1. 使用加减法
a = a + b;
b = a - b;
a = a - b;
  1. 使用乘除法
a = a * b;
b = a / b;
a = a / b;

将数字每千分位用逗号隔开

您可以使用以下代码将数字每千分位用逗号隔开:

function formatNumber(num) {
    return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

这个函数的作用是将数字转换为字符串,并在每千分位插入逗号。例如,如果您想将数字123456789转换为字符串并在每千分位插入逗号,可以这样做:

let num = 123456789;
let formattedNum = formatNumber(num);
console.log(formattedNum);

输出结果为:

123,456,789

实现 add(1)(2)(3)

function add(x) {
  var sum = x;

  function innerAdd(y) {
    sum += y;
    return innerAdd;
  }

  innerAdd.toString = function() {
    return sum;
  };

  return innerAdd;
}

console.log(add(1)(2)(3)(4)(5)); // 15

在这个例子中,我们定义了一个add()函数,它接受一个参数x并返回一个新函数innerAdd,该新函数接受一个参数y并返回innerAdd本身。在innerAdd函数中,我们将y添加到sum变量中,并返回innerAdd本身。我们还重写了innerAdd函数的toString()方法,以便在调用add()函数时返回sum变量的值。我们可以使用add()函数创建一个新函数,该函数将1添加到其第一个参数中,然后将2添加到其第二个参数中,以此类推。最后,我们可以调用这个新函数来计算所有参数的总和。

场景问题

使用Promise封装AJAX请求

function ajax(url, method, data) {
    return new Promise(function(resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(xhr.responseText);
                } else {
                    reject(xhr.statusText);
                }
            }
        };
        xhr.onerror = function() {
            reject(xhr.statusText);
        };
        xhr.send(data);
    });
}

这个函数接受三个参数:请求的URL、请求的方法和请求的数据。它返回一个Promise对象,可以使用then()和catch()方法处理异步操作的结果。例如,如果您想使用GET方法从URL /data.json获取数据,可以这样做:

ajax('/data.json', 'GET', null)
    .then(function(response) {
        console.log(response);
    })
    .catch(function(error) {
        console.error(error);
    });

实现并发函数,函数参数为 urls (请求列表数组)、max(最大并发数量) 、fn(回调),请求都成功执行回调

function multiRequest(urls = [], max = 5, callback) {
    const len = urls.length;
    const result = new Array(len).fill(false);
    let count = 0;
    while (count < max) {
        next();
    }
    function next() {
        let current = count++;
        if (current >= len) {
            !result.includes(false) && callback(result);
            return;
        }
        const url = urls[current];
        fetch(url)
            .then(res => {
                result[current] = res;
                if (current < len) {
                    next();
                }
            })
            .catch(err => {
                result[current] = err;
                if (current < len) {
                    next();
                }
            });
    }
}

这个函数接受三个参数:请求的URL数组、最大并发数和回调函数。它使用fetch API执行异步请求,然后使用Promise.all()等待所有请求完成。当所有请求完成后,它会调用回调函数并传递结果数组。请注意,这个函数使用了递归来控制并发请求的数量,因此请确保您的URL数组不会太大,否则可能会导致堆栈溢出错误。

Promise实现图片的异步加载

您可以使用以下代码实现图片的异步加载:

function loadImageAsync(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = () => reject(new Error('Could not load image at ' + url));
        img.src = url;
    });
}

这个函数接受一个URL作为参数,返回一个Promise对象。当图片加载成功时,Promise对象将被解析并传递给Image对象。如果发生错误,则Promise对象将被拒绝并传递一个错误对象。您可以使用这个函数来异步加载图片,如下所示:

loadImageAsync('https://example.com/image.jpg')
    .then(img => {
        document.body.appendChild(img);
    })
    .catch(error => {
        console.error(error);
    });

实现发布订阅模式

发布订阅模式是一种消息模式,在软件架构中,发布者不会将消息直接发送给特定的接收者,而是通过消息通道广播出去,让订阅该消息主题的订阅者消费到。在JavaScript中,可以使用以下代码实现发布订阅模式:

class EventEmitter {
    constructor() {
        this.events = {};
    }

    on(eventName, listener) {
        if (!this.events[eventName]) {
            this.events[eventName] = [];
        }
        this.events[eventName].push(listener);
    }

    emit(eventName, ...args) {
        const listeners = this.events[eventName];
        if (listeners) {
            listeners.forEach(listener => listener(...args));
        }
    }

    removeListener(eventName, listenerToRemove) {
        const listeners = this.events[eventName];
        if (listeners) {
            const index = listeners.findIndex(listener => listener === listenerToRemove);
            if (index !== -1) {
                listeners.splice(index, 1);
            }
        }
    }
}

这个类提供了三个方法:onemitremoveListeneron方法用于添加事件监听器,emit方法用于触发事件,removeListener方法用于删除事件监听器。您可以使用这个类来实现发布订阅模式,如下所示:

const eventEmitter = new EventEmitter();

eventEmitter.on('event', data => {
    console.log(data);
});

eventEmitter.emit('event', 'Hello, world!');

实现双向数据绑定

当然,还有其他的方法可以实现双向数据绑定。例如,您可以使用ES5中的Object.defineProperty()方法来实现双向数据绑定。这个方法提供了对象的get和set回调,当对象发生变化时会调用这两个方法,以此来实现修改对应的DOM。以下是一个使用Object.defineProperty()方法实现双向数据绑定的示例:

var obj = {};
Object.defineProperty(obj, 'text', {
    get: function() {
        return this.value;
    },
    set: function(value) {
        this.value = value;
        document.getElementById('input').value = value;
        document.getElementById('output').innerHTML = value;
    }
});

document.getElementById('input').addEventListener('input', function() {
    obj.text = this.value;
});

这个示例中,我们使用了Object.defineProperty()方法来定义一个名为text的属性。当该属性被读取或赋值时,会分别调用get和set回调函数。在这个示例中,我们将输入框和输出框与该属性绑定在一起,以实现双向数据绑定。

<input id="input" type="text">
<div id="output"></div>

当然,ES6也提供了一些方法来实现双向数据绑定。例如,您可以使用Proxy对象来实现双向数据绑定 。以下是一个使用Proxy对象实现双向数据绑定的示例:

let obj = {text: ''};
let handler = {
    get(target, key) {
        return target[key];
    },
    set(target, key, value) {
        target[key] = value;
        document.getElementById('input').value = value;
        document.getElementById('output').innerHTML = value;
        return true;
    }
};
let proxy = new Proxy(obj, handler);

document.getElementById('input').addEventListener('input', function() {
    proxy.text = this.value;
});

这个示例中,我们使用了Proxy对象来代理一个名为obj的对象。当该对象的属性被读取或赋值时,会分别调用getset回调函数。在这个示例中,我们将输入框和输出框与该对象的text属性绑定在一起,以实现双向数据绑定。

实现简单路由

JavaScript也提供了一些方法来实现简单路由。例如,您可以使用window.location.hash来实现hash路由,或者使用HTML5的history.pushState()和history.replaceState()方法来实现history路由。

以下是一个使用hash路由的示例:

function router() {
    let hash = window.location.hash;
    if (hash === '#/home') {
        // 显示home页面
    } else if (hash === '#/about') {
        // 显示about页面
    } else {
        // 显示404页面
    }
}
window.addEventListener('hashchange', router);

这个示例中,我们使用了window.location.hash来获取当前的hash值,并根据不同的hash值显示不同的页面。当hash值发生变化时,会触发hashchange事件,从而重新调用router()函数。

以下是一个使用history路由的示例:

function router() {
    let path = window.location.pathname;
    if (path === '/home') {
        // 显示home页面
    } else if (path === '/about') {
        // 显示about页面
    } else {
        // 显示404页面
    }
}
window.addEventListener('popstate', router);

这个示例中,我们使用了window.location.pathname来获取当前的路径,并根据不同的路径显示不同的页面。当路径发生变化时,会触发popstate事件,从而重新调用router()函数。

判断对象是否循环引用

在JavaScript中,可以使用递归来判断对象是否存在循环引用。以下是一个示例函数,它使用一个数组来存储已经遍历过的对象,如果对象已经在数组中,则说明存在循环引用。

function hasCircularReferences(obj, parentArr = []) {
  let flag = false;
  if (typeof obj === 'object') {
    parentArr.push(obj);
    for (let key in obj) {
      if (typeof obj[key] === 'object') {
        if (parentArr.includes(obj[key])) {
          flag = true;
          break;
        } else {
          flag = hasCircularReferences(obj[key], parentArr);
          if (flag) break;
        }
      }
    }
  }
  return flag;
}

记忆缓存函数实现

实现一个function,是一个记忆函数,传一个函数进去,返回一个新的函数,返回的新的函数,调用新函数时,传的参数一样时将调用缓存的结果。

function memory(fn) { 
}
function add (val1,val2) {
    return val1 + val2
}
const memoryAdd = memory(add);
memoryAdd(1,2);// 第一次执行add
memoryAdd(1,2);// 第二次不执行add

以下是一个JavaScript实现的例子:

function memoize(func) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key]) {
      return cache[key];
    } else {
      const result = func.apply(this, args);
      cache[key] = result;
      return result;
    }
  };
}

这个例子中,memoize是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。新的函数会先将参数序列化成字符串,然后检查缓存中是否已经有了该参数对应的结果,如果有,则直接返回缓存中的结果;否则,调用原始函数计算结果,并将结果缓存起来。