主要也是防抖节流call,bind,apply,promise
1.防抖
n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
简单实现
function debounce(func, wait) {
let timeout;
return function () {
let context = this; // 保存this指向
let args = arguments; // 拿到event对象
clearTimeout(timeout)
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:
function debounce(func, wait, immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null;
}, wait)
if (callNow) {
func.apply(context, args)
}
}
else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
}
}
2. 节流
n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
简单实现
function throttled(fn, delay) {
let timer = null
let starttime = Date.now()
return function () {
let curTime = Date.now() // 当前时间
let remaining = delay - (curTime - starttime) // 从上一次到现在,还剩下多少多余时间
let context = this
let args = arguments
clearTimeout(timer)
if (remaining <= 0) {
fn.apply(context, args)
starttime = Date.now()
} else {
timer = setTimeout(fn, remaining);
}
}
}
call,apply,bind的区别
call和apply都是立即执行的,call接收的参数是多个数组,apply接收的参数是一个数组
bind是返回一个新的函数
3. call
Function.prototype.newCall = function (context,...params) {
if (typeof context === 'object') {
context = context || window
} else {
context = Object.create(null);
}
let fn = Symbol();
context[fn] = this
var result = context[fn](...params);
delete context.fn;
return result;
}
var person = {
name: "jayChou"
};
function say(age, sex) {
console.log(`name: ${this.name},age: ${age}, sex: ${sex}`);
return age + sex;
}
var check = say.newCall(person, 18, '男');
console.log(check); // 18男
4. apply
Function.prototype.newApply = function(context, parameter) {
if (typeof context === 'object') {
context = context || window
} else {
context = Object.create(null)
}
let fn = Symbol()
context[fn] = this;
var result = context[fn](...parameter);
delete context[fn];
return result;
}
5.bind
// 手写bind
Function.prototype.bind=function(context,...bindments){
context=context||window;
const func=this;
return function F(...callments){
let args=bindments.concat(callments);
if(this instanceof F){
return new func(...args);
}
return func.call(context,...args)
}
}
function one(a,b){
this.a=a;
this.b=b;
console.log(this)
}
var obj={name:'yiyi'}
var child=one.bind(obj,2);
child(3);
var two=new child(4);
console.log(two)
6.实现promise
function Promise(exector) {
let self = this;
//status表示一种状态
let status = "pending";
let value = undefined;
let reason = undefined;
//成功执行
function resolve(value) {
if (status == 'pending') {
self.value = value;
self.status = "resolve";
}
}
//执行失败
function reject(reason) {
if (status == 'pending') {
self.reason = reason;
self.status = "reject"
}
}
//对异常操作
try {
exector(resolve, reject)
} catch (e) {
reject(e)
}
//设置promise的then方法
Promise.prototype.then = function(reject, resolve) {
let self = this;
if (this.status == 'resolve') {
reject(self.value)
}
if (this.status == 'reject') {
resolve(self.reason)
}
}
}
//new 一个promise 进行测试
let promise = new Promise((reject, resolve) => {
resolve("return resolve");
});
promise.then(data => {
console.log(`success${data}`);
}, err => {
console.log(`err${err}`);
})
7.实现深浅拷贝:
- 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
- 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
浅拷贝(会改变原来的值)
function shallowClone(obj) {
const newObj = {};
for(let prop in obj) {
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
可以实现浅拷贝的方法:
- Object.assign
- Array.prototype.slice(), Array.prototype.concat()
- es6的扩展运算符
- Object.assign(target,source)
var obj = {
age: 18,
nature: ['smart', 'good'],
names: {
name1: 'fx',
name2: 'xka'
},
love: function () {
console.log('fx is a great girl')
}
}
var newObj = Object.assign({}, fxObj);
- Array.prototype.slice()
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
- Array.prototype.concat()
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
- es6的扩展运算符
const fxArr = ["One", "Two", "Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
深拷贝
深拷贝方式有:
- _.cloneDeep()
- jQuery.extend()
- JSON.stringify()
- 手写循环递归
- .cloneDeep()
const _ = require('lodash');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
- jQuery.extend()
const $ = require('jquery');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
- JSON.stringify()
const obj2=JSON.parse(JSON.stringify(obj1));
但是这种方式存在弊端,会忽略undefined、symbol和函数
const obj = {
name: 'A',
name1: undefined,
name3: function() {},
name4: Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
- 循环递归
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
参考地址:
https://vue3js.cn/interview/JavaScript/copy.html#%E5%9B%9B%E3%80%81%E5%8C%BA%E5%88%AB
这个大佬总结的很好,我复习一波