手写代码题


实现一个Promise

  • 过程

    • 1.new Promise((resolve,reject) = > { }) ,传入一个参数executor,传入就执行。executor有两个参数resolve和reject。

    • 2.Promise存在三个状态 pending(等待态)、fulfilled(成功态)、rejected(失败态)。

    • 3.new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变。

    • 4.若是executor函数报错 直接执行reject()

    • 5.then方法,有两个参数onFulfilled,onRejected。这两个是可选参数,当参数不是函数类型时,需要创建一个函数赋值给相应的参数

    • 6.then时state还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们。如果是fulfilled状态,就执行onFulfilled; rejected状态,就执行onRejected

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      class Promise{
      constructor(executor){
      this.state='pending';
      this.value=undefined;
      this.reason=undefined;
      this.resolvedCbs=[];
      this.rejectedCbs=[];

      let resolve=(value)=>{
      if(this.state==='pending'){
      this.state='fulfilled';
      this.value=value;
      this.resolvedCbs.forEach(cb=>cb());
      }
      }
      let reject=(reason)=>{
      if(this.state==='pending'){
      this.state='rejected';
      this.reason=reason;
      this.rejectedCbs.forEach(cb=>cb());
      }
      }

      try{
      executor(resolve,reject);
      }catch(err){
      reject(err);
      }
      }
      then(onFulfilled,onRejected){
      onFulfilled=typeof onFulfilled ==='function'?onFulfilled:v=>v;
      onRejected=typeof onRejected==='function'?onRejected:(err)=>{
      throw err;
      }

      if(this.state==='fulfilled'){
      onFulfilled(this.value);
      }

      if(this.state==='rejected'){
      onRejected(this.reason);
      }

      if(this.state==='pending'){
      this.resolvedCbs.push(()=>{onFulfilled(this.value)});
      this.rejectedCbs.push(()=>{onRejected(this.reason)});
      }
      }
      }

实现Promise.all

  • 必须为数组
  • 可以传入非promise,all自动将其转换为promise对象
  • 传入的值必须按顺序输出
  • 一旦又一个reject则状态立马变为reject,并将错误原因抛出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function myPromiseAll(promiseArr){
// 为了让传入的值不是promise也返回promise
return new Promise((resolve,reject) => {
if(!Array.isArray(promiseArr)){
throw('promiseArr必须为数组')
}
let resArr = []
let len = promiseArr.length;
let count = 0;
for(let i = 0; i < len; i++){
// Promise.resolve将数组中非promise转为promise
Promise.resolve(promiseArr[i])
.then(value => {
count++
resArr[i] = value
if(count == len) return resolve(resArr)
})
.catch(err => {
return reject(err)
})

}
})

}

实现Promise.race

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myPromiseRace(promiseArr){
return new Promise((resolve,reject) => {
if(!Array.isArray(promiseArr)) throw('参数必须为数组')
let len = promiseArr.length
for(let i = 0; i < len; i++){
Promise.resolve(promiseArr[i]).then(val => {
resolve(val)
}).catch(err => {
reject(err)
})
}

})
}

实现Ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function ajax(url,fnSucc,fnFail){
//创建oAjax对象(拥有一个手机)
if(window.XMLHttpRequest){
var oAjax=new XMLHttpRequest();//非IE6
}else{
var oAjax=new ActiveXObject('MicroSoft.XMLHTTP');//IE6
}

//连接到服务器(拨号)
oAjax.open("GET",url,true);
//发送请求(说)
oAjax.send();
//接收返回值(听)
oAjax.onreadystatechange=function (){
if(oAjax.readyState==4){
if(oAjax.status==200){
fnSucc(oAjax.responseText);
}else{
if(fnFail){
fnFail(oAjax.status);
}
}
}
}
}

实现 call,apply

  • 如何实现这几个函数

    • 如果不传入第一个参数,那么上下文默认为window
    • 改变this指向,让新的对象可以执行该函数,并接受参数 (this的使用场景:对象属性使用)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    //call
    Function.prototype.myCall=function (obj){
    if(typeof this !== 'function'){
    throw new Error('error');
    }
    obj=obj || window;
    const args=[...arguments].slice(1);//去掉第一个参数
    obj.fn=this;//将执行函数设为对象属性
    const res=obj.fn(...args);
    delete obj.fn;
    return res;
    }

    //Test
    const foo={
    value:1,
    }
    function bar(name){
    console.log(this.value);//在全局作用域下执行,this指向 window
    console.log(name);
    }
    bar.myCall(foo);

    //apply
    Function.prototype.myApply=function (obj){
    if(typeof this !== 'function'){
    throw new Error('Error');
    }
    obj=obj || window;
    obj.fn=this;
    let res;
    if(arguments[1]){
    res=obj.fn(...arguments[1]);
    }else{
    res=obj.fn();
    }
    delete obj.fn;
    return res;
    }

    //Test
    const foo={
    value:1,
    }
    function bar(name){
    console.log(this.value);//在全局作用域下执行,this指向 window
    console.log(name);
    }
    bar.myApply(foo,['winnie']);

实现 bind

  • 注意

    • 1.改变this指向,可用call或apply

    • 2.返回的是一个函数,也可以传参,两次传参要合并

    • 3.对绑定函数的使用new 操作符时,传入的this忽略,但其他参数仍可用(this规则的优先级,new优先级最高)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      Function.prototype.myBind=function(obj){
      if(typeof this !== 'function'){
      throw new Error('error');
      }
      const exeFn=this;
      const args1=[...arguments].slice(1);
      return function F(...arguments){
      if(this instanceof F){//使用了new 操作符
      return new exeFn(...args1,...arguments);
      }
      return exeFn.apply(obj,args1.concat(...arguments));
      }
      }

      //Test
      const foo={
      value:1,
      }

      function Bar(name,age){
      console.log(this.value);
      console.log(name);
      console.log(age);
      }
      const myBar=Bar.myBind(foo,'winnie');//对绑定函数的使用new 操作符时,传入的this忽略,但其他参数仍可用
      const bar1=new myBar(18);
      const bar2=Bar.myBind(foo,'lm',18)();

实现一个继承

  • 寄生组合继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function Parent(val){
    this.value=val;
    }
    Parent.prototype.getValue=function (){
    console.log(this.value);
    }
    function Child(val){
    //继承父类属性
    Parent.call(this,val);
    }
    Child.prototype=Object.create(Parent.prototype,{
    constructor:{
    value:Child,
    enumerable:false,
    writable:true,
    configurable:true
    }
    });//Object.create()方法创建一个新对象,使用现有对象来提供新建对象的__proto__

实现一个深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function deepClone(obj){
function isObject(o){
return (typeof o ==='object'|| typeof o ==='function')&&o!==null;
}
if(!isObject(obj)){
throw new Error('非对象')
}

let isArray=Array.isArray(obj);
let newObj=isArray?[...obj]:{...obj};
//遍历属性,若属性值是引用类型,则需要深拷贝
Reflect.ownKeys(newObj).forEach(key=>{
newObj[key]=isObject(obj[key])?deepClone(obj[key]):obj[key];
})
return newObj;
}
let obj={
a:[1,2,3],
b:{
c:4,
d:5
}
}
let newObj=deepClone(obj);
obj.b.c=2;
console.log(newObj.b.c);//4

实现一个 instanceOf

1
2
3
4
5
6
7
8
9
10
11
12
function myInstanceOf(left,right){
let proto=left.__proto__;
let prototype=right.prototype;
while(true){
if(proto===null) return false;
if(proto===prototype) return true;
proto=proto.__proto__;
}
}

const arr=[];
console.log(myInstanceOf(arr,Object));

实现一个new操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Person(name){
this.name=name;
}
Person.prototype.getName=function(){
return this.name;
}

function New(){
//创建一个新对象
const obj={};
const constructor=[...arguments].shift();//构造函数
//链接到原型
obj.__proto__=constructor.prototype;
//绑定this
constructor.apply(obj,[...arguments].slice(1));
//返回新对象
return obj;
}

let p=new Person('winnie');
let p1=New(Person,'winnie');
console.log(p.getName()===p1.getName());

实现防抖和节流

  • 防抖 原理:延迟一段时间再执行,如果在延迟的时间内继续触发,会重新计算。
  • 节流 原理:隔一段时间,执行一次。就像水龙头滴一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//防抖
function debounce(fn,delay){
let timer=null,context,args;
return function(){
context=this;
args=[...arguments];
if(timer) clearTimeout(timer);
timer=setTimeout(function(){
fn.apply(context,args);
},delay);
}
}
//节流
function throttle(fn,delay){
let timer=null,context,args;
return function(){
context=this;
args=[...arguments];
if(!timer){
timer=setTimeout(function(){
timer=null;
fn.apply(context,args);
},delay);
}
}
}

实现一个函数柯里化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const curry=(fn,...arguments)=>{
let args=[...arguments];
return function innerCurry(){
if(arguments.length===0){
return fn(...args);
}else{
args.push(...arguments);
return innerCurry;
}
}
}
function add(){
return [...arguments].reduce(function(a,b){return a+b},0);
}
console.log(curry(add,1,2,3)(1)(2)(3,4,5,5)(5,6,6,7,8,8)(1)(1)(1)());//69

斐波那契数

  • 递归版
1
2
3
4
5
6
7
8
9
10
const recursionFib=(n)=>{
if(n===0){
return 0;
}
else if(n===1){
return 1;
}
return recursionFib(n-1)+recursionFib(n-2);
}
console.log(recursionFib(8));
  • 迭代版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const iterationFib=(n)=>{
let [fib1,fib2,fib3]=[0,1,0];
if(n===0){
return fib1;
}
else if(n===1){
return fib2;
}
while(n>1){
fib3=fib1+fib2;
fib1=fib2;
fib2=fib3;
n--;
}
return fib3;
}
console.log(iterationFib(9));

判断变量类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//判断JS变量类型,值类型、引用类型、null单独判断
function judgeType(target){
let type=typeof target;
if(target==='null'){
return 'null';
}
else if(type==='object'){
return type=Object.prototype.toString.call(target).match(/\[object (.*)\]/)[1];
}
else
return type;
}

const type=(target)=>{
let type=typeof target;//typeof可以准确判断除了null以外的值类型
const template={
"[object Array]":"array",
"[object Object]":"object",
"[object Number]":"Number-object",
"[object Boolean]":"Boolean-object",
"[object String]":"String-object"
}
if(target===null){
return "null";
}
else if(type == 'object'){
let res=Object.prototype.toString.call(target);
return template[res];
}
else{
return type;
}
}
console.log(type(null));
console.log(type(true));
console.log(type(new String('winnie')));

最长公共子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const LCS=(str1,str2)=>{
let [arr,maxLen,subStr,index]=[[],0,"",0];

for(let i=0;i<=str1.length;i++){
arr[i]=[];
for(let j=0;j<=str2.length;j++){
arr[i][j]=0;
}
}

for(let i=1;i<=str1.length;i++){
for(let j=1;j<=str2.length;j++){
if(str1[i-1]===str2[j-1]){
arr[i][j]=arr[i-1][j-1]+1;
}else{
arr[i][j]=0;
}
if(arr[i][j]>maxLen){
maxLen=arr[i][j];
index=i;
}
}
}
console.log(index,maxLen);
if(maxLen===0){
return "";
}else{
for(var k = index - maxLen; k < index; k++){
subStr += str1[k];
}
return subStr;
}
}

const LCS1=(str1,str2)=>{
let [subStr,L1,L2]=["",str1.length,str2.length];
if (L1>L2){//取两个字符串中最短的
let temp=str1;
str1=str2;
str2=temp;
}
let [newL1,newL2]=[str1.length,str2.length];
for (let j=newL1; j > 0; j--) {//在最短的子串中找最长公共子串
for (let i= 0; i <= newL1-j ; i++){
subStr= str1.substr(i,j);
if (str2.indexOf(subStr) >= 0) {//在较长子串中查询
return subStr;
}
}
}
return "";
}
var str1="abcdefg";
var str2="xyzabcd";
console.log(LCS(str1, str2)); // abcd
console.log(LCS1("aaaX3333--", "baa333ccX3333333x"));

最长回文子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const LPS=(str)=>{
let reverseStr=str.split("").reverse().join(""),
strLen=str.length,
palindromeStr="";
for(let i=strLen;i>0;i--){
for(let j=0;j<=strLen-i;j++){
palindromeStr=str.substr(j,i);
if(reverseStr.indexOf(palindromeStr)>=0){
return palindromeStr;
}
}
}
return "";
}
console.log(LPS("cbbd"));

杨辉三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const triangle=(n)=>{
let arr=[];
for(let i=0;i<n;i++){//i控制行数
arr[i]=new Array(i+1);//对每行创建一个长度为i+1的数组,用于存储每行的数据
arr[i][0]=1;
arr[i][i]=1;
for(let j=1;j<i;j++){
arr[i][j]=arr[i-1][j-1]+arr[i-1][j];
}
}
return arr;
}

const print=(arr)=>{
let n=arr.length,
nbsp='';
for(let i=0;i<n;i++){
for(let j=0;j<n-i-1;j++){
nbsp+='';
}
console.log(nbsp+arr[i]);
}
}
print(triangle(8));

括号匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const matchBrackets=(s)=>{
let map={
'(':-1,
')':1,
'[':-2,
']':2,
'{':-3,
'}':3
}
let stack=[];
for(let i=0;i<s.length;i++){
if(map[s[i]]<0){
stack.push(s[i]);
}else{
let last=stack.pop();
if(map[last]+map[s[i]]!==0) return false;
}
}
if(stack.length>0) return false;
return true;
}
console.log(matchBrackets("[(])"));

实现函数的累加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const plus_one=(...arguments)=>{//对arguments使用扩展运算符不能少
let sum=[...arguments].reduce((sum,cur)=>{ return sum+cur},0);
return function addRest(...arguments){
if(arguments.length===0){
return sum;
}else{
sum=[...arguments].reduce((sum,cur)=>{return sum+cur},sum);
return addRest;
}
}
}

const plus_two=(...arguments)=>{//对arguments使用扩展运算符不能少
let cache=[...arguments],sum;
return function addRest(...arguments){
cache=cache.concat(...arguments);
sum=cache.reduce((sum,cur)=>{ return sum+cur},0);
if(arguments.length===0){
return sum;
}else{
return addRest;
}
}
}

const plus_three=(...arguments)=>{
let args=[...arguments];//不断保存参数
return function plus(){
if(arguments.length===0){
return args.reduce((sum,cur)=>{return sum+cur},0);
}else{
args=args.concat(...arguments);
return plus;
}
}
}
console.log(plus_one(1,2)(3,4)(5)());
console.log(plus_two(1,2)(3,4)(5)());
console.log(plus_three(1,2)(3,4)(5)());

闭包的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function CoolModule(){
var something="cool";
var another = [1,2,3];
function doSomething(){
console.log(something);
}
function doAnother(){
console.log(another.join("!"));
}
return {
doSomething:doSomething,
doAnother:doAnother
};
}

function isFirstLoad(){//封装变量,收敛权限
let _list=[];//
return function(variable){//定义_list的操作,外界只能使用这个函数,改变传参,但是无法操作_list
if(_list.indexOf(variable)>-1){
return false;
}else{
_list.push(variable);
return true;
}
}
}

let firstLoad=isFirstLoad();
console.log(firstLoad(10));
console.log(firstLoad(10));

实现一个按顺序加载的 promise

1
2
3
4
5
6
7
8
9
10
11
12
/* 使用 async await */
async function queue(arr) {
let res = null
for (let promise of arr) {
res = await promise(res)
}
return await res
}
queue([a, b, c])
.then(data => {
console.log(data)
})

实现一个 EventEmitter 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class EventEmitter {
constructor () {
// 存储事件
this.events = this.events || new Map()
}
// 监听事件
addListener (type, fn) {
if (!this.events.get(type)) {
this.events.set(type, fn)
}
}
// 触发事件
emit (type) {
let handle = this.events.get(type)
handle.apply(this, [...arguments].slice(1))
}
}

实现一个jsonp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function jsonp({url,params,callback}){
return new Promise((resolve,reject)=>{
//创建script标签
let script=document.createElement('script');
//将回调函数挂在window上
window[callback]=function(data){
resolve(data);
document.body.removeChild(script);
}
//回调函数加在请求地址上
params={...params,callback};
let arr=[];
for(let key in params){
arr.push(`${key}=${params[key]}`);
}
script.src=`${url}?${arr.join('&')}`;
document.body.appendChild(script);
})
}