深拷贝、 hash解决深拷贝,循环引用
// 深拷贝,hash解决深拷贝,循环引用
function deepCopy(obj, hash = new WeakMap()) {
let cloneObj
let Constructor = obj.constructor
switch(Constructor){
case RegExp:
cloneObj = new Constructor(obj)
break
case Date:
cloneObj = new Constructor(obj.getTime())
break
default:
if(hash.has(obj)) return hash.get(obj)
cloneObj = new Constructor()
hash.set(obj, cloneObj)
}
for (let key in obj) {
cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
}
return cloneObj
}
// 获取节点位置
const getPosition = el => {
if (!el) return false;
var rect = el.getBoundingClientRect();
//获取元素距离文档顶部的距离
var top =
rect.top +
(window.pageYOffset || document.documentElement.scrollTop) -
(document.documentElement.clientTop || 0);
var left =
rect.left +
(window.pageXOffset || document.documentElement.scrollLeft) -
(document.documentElement.clientLeft || 0);
return {
top,
left
};
};
// 版本比较
const compareVersion = function(version1, version2) {
let verision1Arr = version1.split(".");
let verision2Arr = version2.split(".");
let maxLen =
verision1Arr.length > verision2Arr.length
? verision1Arr.length
: verision2Arr.length;
for (let i = 0; i < maxLen; i++) {
let p1 = verision1Arr[i] >> 0 || 0;
let p2 = verision2Arr[i] >> 0 || 0;
if (p1 > p2) {
return 1;
} else if (p1 < p2) {
return -1;
}
}
return 0;
};
new 操作符做了这些事:
- 创建一个全新的对象,这个对象的 proto 要指向构造函数的原型对象
- 执行构造函数
- 返回值为 object 类型则作为 new 方法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) {
let instance = Object.create(fn.prototype);
let res = fn.apply(instance, args);
return typeof res === 'object' ? res: instance;
}
实现 call 方法
调用 call 方法具体做了什么:
- 将函数设为对象的属性
- 执行后删除这个函数
- 指定
this
到函数并传入给定参数执行函数 - 如果不传入参数,默认指向为
window
//实现一个call方法:
Function.prototype.myCall = function(context) {
//此处没有考虑context非object情况
context.fn = this;
let args = [];
for (let i = 1, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
context.fn(...args);
let result = context.fn(...args);
delete context.fn;
return result;
};
实现 apply 方法
思路: 利用 this
的上下文特性。
//实现apply只要把下一行中的...args换成args即可
Function.prototype.myCall = function(context = window, ...args) {
let func = this;
let fn = Symbol("fn");
context[fn] = func;
let res = context[fn](...args);//重点代码,利用this指向,相当于context.caller(...args)
delete context[fn];
return res;
}
实现 bind
bind
的实现对比其他两个函数略微地复杂了一点,因为 bind
需要返回一个函数,需要判断一些边界问题,以下是 bind
的实现
-
bind
返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new
的方式,我们先来说直接调用的方式 - 对于直接调用来说,这里选择了
apply
的方式实现,但是对于参数需要注意以下情况:因为bind
可以实现类似这样的代码f.bind(obj, 1)(2)
,所以我们需要将两边的参数拼接起来,于是就有了这样的实现args.concat(...arguments)
- 最后来说通过
new
的方式,在之前的章节中我们学习过如何判断this
,对于new
的情况来说,不会被任何方式改变this
,所以对于这种情况我们需要忽略传入的this
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
懒加载和预加载
// 懒加载
// <img src="default.png" data-src="https://xxxx/real.png">
function isVisible(el) {
const position = el.getBoundingClientRect()
const windowHeight = document.documentElement.clientHeight
// 顶部边缘可见
const topVisible = position.top > 0 && position.top < windowHeight;
// 底部边缘可见
const bottomVisible = position.bottom < windowHeight && position.bottom > 0;
return topVisible || bottomVisible;
}
function imageLazyLoad() {
const images = document.querySelectorAll('img')
for (let img of images) {
const realSrc = img.dataset.src
if (!realSrc) continue
if (isVisible(img)) {
img.src = realSrc
img.dataset.src = ''
}
}
}
// 测试
window.addEventListener('load', imageLazyLoad)
window.addEventListener('scroll', imageLazyLoad)
// or
window.addEventListener('scroll', throttle(imageLazyLoad, 1000))
//预加载
网友评论