1、手写 call
- Symbol 在 Es5 的对象属性名中都是字符串,当一对象的属性名出现重复时,后者往往会覆盖前者.
若使用 Symbol 就能够保证每个属性的名字都是独一无二的,相当于生成一个唯一的标识 ID,这样就从根本上防止属性名的冲突
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('must be a function');
}
if (!context) {
context = window;
}
const fn = Symbol();
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
const bar = { a: 1 };
function foo(b) {
console.log(this.a, b);
return this.a + b;
}
console.log(foo.myCall(bar, 2));
2、手写 apply
Function.prototype.myApply = function (context, args=[]) {
if (typeof this !== 'function') {
throw new TypeError('must be a function');
}
if (!context) {
context = window;
}
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
const bar = { a: 1 };
function foo(b) {
console.log(this.a, b);
return this.a + b;
}
console.log(foo.myApply(bar, [2]));
3、手写 bind
Function.prototype.myBind = function(context,...args) {
const fn = this;
if(typeof fn !== "function"){
throw new TypeError("it must be a function")
}
if(!context){
context = window;
}
return function (...otherArgs) {
return fn.apply(context,[...args,...otherArgs]);
}
}
const bar = {
a:1,
};
function foo(b,c,d) {
console.log(this.a,b,c,d)
return this.a+b+c+d;
}
const fn = foo.myBind(bar,2)
console.log(fn(3,4))
4、实现防抖
- 概念 触发事件后,在 n 秒内函数只执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
function debounce(func, delay){
let timer;
return function() {
console.log('debounce: return: ',arguments);
clearTimeout(timer)
timer = setTimeout(()=>{
func(arguments)
},delay)
}
}
const aa = debounce((...args)=>{
console.log('debounce: 执行了',...args)
},3000);
aa('aa','bb');
5、实现节流
function throttleOne (func, delay){
let preTime =0;
return function(){
const nowTime = Date.now()
if(nowTime-preTime>delay){
func(arguments)
preTime=nowTime;
}
}
}
function throttleTwo (func, delay){
let isStart = true;
return function(){
if(isStart){
isStart=false
setTimeout(() => {
func(arguments)
isStart = true
}, delay);
}
}
}
const bb = throttleTwo((...args)=>{
console.log('throttle: 执行了',...args)
},5000);
bb('aa','bb');
6、手写 promise.all
const isPromise = value => typeof value.then === 'function';
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
let arr = [];
let index = 0;
const processData = (key, data) => {
arr[key] = data;
if(++index === promises.length){
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
let result = promises[i];
if (isPromise(result)) {
result.then((data) => {
processData(i, data)
}, reject)
} else {
processData(i, result)
}
}
});
}
let p1 = new Promise((resolve, reject) => {
reject('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
Promise.all([p1,p2]).then((res)=>{
console.log('-result-',res)
})
7、手写 promise.race
const isPromise = value => typeof value.then === 'function';
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let result = promises[i];
if(isPromise(result)){
result.then(resolve,reject)
}else{
resolve(result);
}
}
});
}
Promise.all([p1,p2]).then((res)=>{
console.log('result: ',res)
})
8、手写 继承
//Object.create的理解
const arr = [{"1":1},2,3,5]
// Object.create 创建了一个新对象,这个新对象的 原型是arr
const arr3 = Object.create(arr)
// 实现一个组合集成
function Parent(){
this.name ="father"
this.age = 11
}
Parent.prototype.say= function (){
console.log('老爸讲话了')
}
// 构造函数继承,可以传参,多继承
function Child(color){
Parent.call(this)
this.color = color
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
/*
* 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
* Child.prototype = new Parent()
*/
/*
* Child.prototype = Parent.prototype
* 构造函数会覆盖
*/
const child1 = new Child('黄色')
const child2 = new Child('黄色')
const parent1 = new Parent()
console.log(child1.constructor)
console.log(child2.constructor)
console.log(parent1.constructor)
9、手写 new
- a.创建一个空的简单 JavaScript 对象(即{});
- b.链接该对象(即设置该对象的构造函数)到另一个对象 ;( 通俗理解就是新对象隐式原型proto链接到构造函数显式原型 prototype 上。)
- c.将步骤 1 新创建的对象作为 this 的上下文 ;( 实际是执行了构造函数 并将构造函数作用域指向新对象 )
- d.如果该函数没有返回对象,则返回 this。( 实际是返回一个空对象, new Object()就是返回一个空对象{} )
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype)
let res = fn.call(obj, ...args)
if (res && (typeof res === "object" || typeof res === "function")) {
return res
}
return obj
}
用法如下:
// // function Person(name, age) {
// // this.name = name
// // this.age = age
// // }
// // Person.prototype.say = function() {
// // console.log(this.age)
// // }
// // let p1 = myNew(Person, "lihua", 18)
// // console.log(p1.name)
// // console.log(p1)
// // p1.say()
10、手写-setTimeout 模拟实现 setInterval
function mySetInterval(fun, time) {
let timer = null;
function setIntervalMe() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fun();
setIntervalMe(fun, time);
}, time);
}
setIntervalMe();
return () => {
clearTimeout(timer);
};
}
const cancel = mySetInterval(() => {
console.log('aa', new Date());
}, 2000);
setTimeout(() => {
cancel();
}, 5000);
11、虚拟 dom 转换真实 dom 的简单实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.app {
height: 500px;
width: 500px;
background-color: bisque;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
const vnode = {
tag: 'DIV',
attrs: {
id: 'app',
class: 'app'
},
children: [
{
tag: 'DIV',
attrs: {
style: { height: '100px', width: '100px', backgroundColor: 'blue' }
},
children: [
{
tag: 'A',
attrs: {
style: { color: '#fff' }
},
children: [{ value: 'hello word' }]
}
]
},
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] },
{ tag: 'A', children: [] }
]
}
]
};
function _render(vnode) {
if (typeof vnode.tag !== 'string') {
return document.createTextNode(vnode.value);
}
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
for (let key in vnode.attrs) {
if (key === 'style') {
for (let styleName in vnode.attrs[key]) {
dom.style[styleName] = vnode.attrs[key][styleName];
}
} else {
dom.setAttribute(key, vnode.attrs[key]);
}
}
}
vnode.children &&
vnode.children.forEach(child => {
return dom.appendChild(_render(child));
});
return dom;
}
const nodes = _render(vnode);
document.getElementById('root').appendChild(nodes);
</script>
</body>
</html>
12、真实 dom 转换 虚拟 dom 简单实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root" class="tt" style="height: 100px; width: 100px; background-color: bisque">
<div title="tt1">hello1</div>
</div>
<script>
class VNode {
constructor(tag, attrs, value, type) {
this.tag = tag && tag.toLowerCase()
this.attrs = attrs
this.value = value
this.type = type
this.children = []
}
appendChild(vnode) {
this.children.push(vnode)
}
}
function getVNode(node) {
const nodeType = node.nodeType
let _vnode = null
if (nodeType === 1) {
// 是元素
const nodeName = node.nodeName
const attrs = Array.from(node.attributes)
let _attrObj = {}
attrs &&
attrs.forEach(item => {
_attrObj[item.nodeName] = item.nodeValue
})
_vnode = new VNode(nodeName, _attrObj, undefined, nodeType)
const childNodes = node.childNodes
childNodes.forEach(childNode => {
_vnode.appendChild(getVNode(childNode))
})
} else if (nodeType === 3) {
// 文本节点
_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)
}
return _vnode
}
const root = document.getElementById('root')
const vRoot = getVNode(root)
console.log(vRoot)
console.log(JSON.stringify(vRoot))
</script>
</body>
</html>