面试中的手撕代码(一)

1,956 阅读11分钟

总结了一些面面试中常见的手写代码题,如有更好见解欢迎评论指正~

手动实现map

Array.prototype._map = function (callbackFn, thisArg) {
    // 判断this值是否合法,否则抛出异常
    if (this === null || this === undefined) {
        throw new TypeError("Cannot read property 'map' of null");
    }
    // 判断回调函数是否合法
    if (typeof callbackFn !=='function') {
        throw new TypeError(callbackFn + ' is not a function')
    }
    // 将this值(调用_map的数组)具象化成变量O
    let O = Object(this);
    let receiver = thisArg;
    // 使用无符号右移运算符对length值右移0位,相当于向下取整,丢弃小数部分
    let len = O.length >>> 0;
    // 创建一个长度相同的结果数组
    let A = new Array(len);
    for (let k = 0; k < len; k++) {
        // 以0-length的顺序处理元素,跳过空值
        if (k in O) {
            let kValue = O[k];
            // 依次传入this, 当前项,当前索引,整个数组
            let mappedValue = callbackFn.call(receiver, kValue, k, O);
            A[k] = mappedValue;
        }
    }
    // 返回结果
    return A;
}

const list = [1,2,3]

const mapRes = list._map(e=>e)

获取元素的Z-index

function getZIndex(){
    return [...document.all].reduce((r, e) => Math.max(r, +window.getComputedStyle(e).zIndex || 0), 0)
}

翻转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>反转DOM结构</title>
</head>

<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>
    <button onclick="reverseChildrenNodes()">反转DOM</button>

    <script>

        function reverseChildrenNodes() {
            let ele = document.querySelector('ul')

            let len = ele.childNodes.length

            while (len-- > 0) {
                // 原节点会被自动移除
                ele.appendChild(ele.childNodes[len])
            }

        }
    </script>
</body>

</html>

或者

<!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>反转DOM结构</title>
</head>

<body>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>
    <button onclick="reverseChildrenNodes()">反转DOM</button>

    <script>

        function reverseChildrenNodes() {
            let ele = document.querySelector('ul')

            let children = Array.from(ele.childNodes)

            for (const child of children) {
                ele.removeChild(child)
            }

            children.reverse()

            for (const child of children) {
                ele.appendChild(child)
            }
        }
    </script>
</body>

</html>

每间隔1s打印

function sleep(wait, val) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(val)
        }, wait)
    })
}

async function test() {
    for (let i = 0; i < 5;) {
        i++
        console.log(await sleep(1000,i))
    }
}

test()

手写代码实现红绿灯效果

红灯3秒,绿灯1秒,黄灯2秒,循环重复

function sleep(time, value) {
  return new Promise(res => {
    setTimeout(() => {
      res(value);
    }, time)
  })
};

const lightDict = [
  { value: 'red', time: 2000 },
  { value: 'green', time: 3000 },
  { value: 'yellow', time: 1000 },
];

async function test() {
  while (true) {
    for (let item of lightDict) {
      console.log(await sleep(item.time, item.value), new Date().getTime())
    }
  }
};

test();

或者

async function light() {
  console.log('红灯', Date.now());
  
  setTimeout(() => {
    console.log('绿灯', Date.now());
    setTimeout(() => {
      console.log('黄灯', Date.now());
      setTimeout(() => {
        light()
      }, 2000)
    }, 1000)
  }, 3000)
  
};

数组"取"重排序

  • 取得两个数组⾥相同的部分, 并去重
  • 然后按照从⼩到⼤顺序排序, 最后结果返回 (注意, 是返回结果, 不是把结果打印出来)

const arrayA = [4, 2, 1, 2, 5];
const arrayB = [2, 3, 1, 6];
function process(arrayA, arrayB) {
    let res = []
    for (let i = 0; i < arrayA.length; i++) {
        if (arrayB.includes(arrayA[i])&&!res.includes(arrayA[i])) {
            res.push(arrayA[i])
        }
    }

    return res.sort((a, b) => a - b)
}
/*
应该返回 [1, 2]
*/
console.log(process(arrayA, arrayB))

数组转树状结构

const data = [
    { id: '01', name: '张大大', pid: '00', job: '项目经理' },
    { id: '02', name: '小亮', pid: '01', job: '产品leader' },
    { id: '03', name: '小美', pid: '01', job: 'UIleader' },
    { id: '04', name: '老马', pid: '01', job: '技术leader' },
    { id: '05', name: '老王', pid: '01', job: '测试leader' },
    { id: '06', name: '老李', pid: '01', job: '运维leader' },
    { id: '07', name: '小丽', pid: '02', job: '产品经理' },
    { id: '08', name: '大光', pid: '02', job: '产品经理' },
    { id: '09', name: '小高', pid: '03', job: 'UI设计师' },
    { id: '10', name: '小刘', pid: '04', job: '前端工程师' },
    { id: '11', name: '小华', pid: '04', job: '后端工程师' },
    { id: '12', name: '小李', pid: '04', job: '后端工程师' },
    { id: '13', name: '小赵', pid: '05', job: '测试工程师' },
    { id: '14', name: '小强', pid: '05', job: '测试工程师' },
    { id: '15', name: '小涛', pid: '06', job: '运维工程师' }
]

// 递归:代码简洁,效率低
function transToTree(list, root) {
    return list
        .filter(item => item.pid === root)
        .map(item => ({ ...item, children: transToTree(list, item.id) }))
}
// console.log(JSON.stringify(transToTree(data, '00')))

// 循环:利用引用数据类型的特性
function transToTree1(list, root) {
    let res = [];
    let map = {};

    for (let item of list) {
        map[item.id] = { ...item }
    }

    for (let item of list) {
        const { pid, id } = item
        if (pid === root) {
            res.push(map[id])
        } else {
            map[pid].children ? map[pid].children.push(map[id]) : map[pid].children = [map[id]]
        }
    }
    return res
}

console.log(JSON.stringify(transToTree1(data, '00')))

函数柯里化

function curry(fn, ...rest) {
    const length = fn.length

    return function () {
        const args = [...rest, ...arguments]
        console.log([...rest], [...arguments])
        if (args.length < length) {
            return curry.call(this, fn, ...args)
        } else {
            return fn.apply(this, args)
        }
    }
}

const fn = (a, b, c) => {
    return a + b + c
}
const f = curry(fn)
console.log(f(1)(2)(3))
console.log(f(1)(2, 3))
console.log(f(1, 2, 3))

实现emitterEvent

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

    on(name, cb) {
        this.events[name] = this.events[name] ?? []
        this.events[name].push(cb)
    }

    emit(name, ...rest) {
        if(!this.events[name]){
            console.log('不存在emit事件名称')
            return
        }
        this.events[name].forEach(fn => fn(...rest))
    }

    off(name, cb) {
        if(!this.events[name]){
            console.log('不存在off事件名称')
            return
        }
        this.events[name] = this.events[name].filter(e => e !== cb)
    }

    once(name, cb) {
        const fn = (...args) => {
            cb(...args)
            this.off(name, fn)
        }
        this.on(name, fn)
    }
}
const eventBus = new EventEmitter()
// eventBus.off('不存在','data')
// eventBus.once("once", (params) => { console.log(1, params) })
// eventBus.on("once", (params) => { console.log(2, params) })
// eventBus.on("not once", (params) => { console.log(3, params) })
// eventBus.on("not once", (params) => { console.log(4, params) })
// eventBus.emit("once", "once emit")
// eventBus.emit("once", "once emit")
// eventBus.emit("once", "once emit")
// eventBus.emit("not once", "not once emit")

function testOff(params) {
    console.log(5, params)
}
function testOff1(params) {
    console.log(6, params)
}

eventBus.on("testOff", testOff)
eventBus.on("testOff", testOff1)
eventBus.emit('testOff', 'off1')
eventBus.off('testOff', testOff)
eventBus.emit('testOff', 'off2')

异步加法

// 异步加法
function asyncAdd(a, b, cb) {
    setTimeout(() => {
        cb(null, a + b)
    }, Math.random() * 1000)
}

async function total() {
    const res1 = await sum(1, 2, 3, 4, 5, 6, 4)
    const res2 = await sum(1, 2, 3, 4, 5, 6, 4)
    console.log([res1, res2])
    return [res1, res2]
}

// 实现下 sum 函数。注意不能使用加法,在 sum 中借助 asyncAdd 完成加法。尽可能的优化这个方法的时间。
function sum(){}
// 异步加法
function asyncAdd(a, b, cb) {
    setTimeout(() => {
        cb(null, a + b)
    }, Math.random() * 1000)
}

async function total() {
    const res1 = await sum(1, 2, 3, 4, 5, 6, 4)
    const res2 = await sum(1, 2, 3, 4, 5, 6, 4)
    console.log([res1, res2])
    return [res1, res2]
}

let caches = {}//缓存不能定义在sum内部,不然每次计算都会声明,永远不会有缓存
total()

// 实现下 sum 函数。注意不能使用加法,在 sum 中借助 asyncAdd 完成加法。尽可能的优化这个方法的时间。
async function sum(...rests) {
    let res = 0;
    let key = rests.join('+')
    if (caches.hasOwnProperty(key)) {
        console.log('有缓存直接读取缓存')
        return caches[key]
    }
    console.log('没有缓存')
    for (let item of rests) {
        res = await calculate(res, item)
    }

    caches[key] = res

    return res
}

function calculate(num1, num2) {
    return new Promise((res, rej) => {
        asyncAdd(num1, num2, (err, result) => {
            if (err) {
                rej(err)
                return
            }
            res(result)
        })
    })
}

非立即执行防抖函数

function debounce(fn, wait) {
  var timeout;

  return function () {
    let context = this;
    let args = arguments;

    if (timeout) clearTimeout(timeout);

    timeout = setTimeout(() => {
      fn.apply(context, args);
    }, wait);
  };
}

立即执行防抖函数

<!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>
    <script>
        function debounce(fn, wait) {
            let timeout = null
            let flag = 0
            
            return function () {
                let context = this
                let args = [...arguments]

                if (timeout) {
                    clearTimeout(timeout)
                }
                if (flag) {
                    timeout = setTimeout(() => {
                        fn.apply(context, args)
                        console.log('非立即执行')
                    }, wait)
                } else {
                    fn.apply(context, args)
                    console.log('立即执行')
                    flag++
                }
            }
        }
        function con() {
            console.log('log--->', Date.now())
        }
        document.addEventListener('scroll', debounce(con, 1000))
    </script>
</head>

<body>
    <div style="background-color: red;height:30000px">

    </div>

</body>

</html>

立即执行节流函数

function throttle(fn, wait) {
  let previous = 0;

  return function () {
    let now = Date.now();
    let context = this;
    let args = arguments;

    if (now - previous > wait) {
      fn.apply(context, args);
      previous = now;
    }
  }
}

非立即执行节流函数

<!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>
    <script>
        function throttle(fn, wait) {
            let previous = 0;

            return function () {
                let now = Date.now();
                let context = this;
                let args = arguments;
                if (previous === 0) {
                    previous = now
                } else if (now - previous > wait) {
                    fn.apply(context, args);
                    previous = now;
                }
            }
        }
        function con() {
            console.log('log--->', Date.now())
        }
        document.addEventListener('scroll', throttle(con, 2000))
    </script>
</head>

<body>
    <div style="background-color: red;height:30000px">

    </div>

</body>

</html>

浅拷贝

function clone(o) {
    const obj = {};
    for (i in o) {
        if(o.hasOwnProperty(i)){  // hasOwnProperty忽略继承属性length toString等
            obj[i] = o[i]
        }        
    }
    return obj
};

深拷贝

function deepCopy(obje) {
    if(typeof obje === 'object'){
        let afterClone = Array.isArray(obje) ? [] : {};
        for(let item in obje){
            if(obje.hasOwnProperty(item)){
                afterClone[item] = deepCopy(obje[item]);
            }
        }
        return afterClone;
    }else{
        return obje;
    }
}

var theObj = {
    name:'jerry',
    age:15,
    a:undefined,
    b:{
        c:456
    }
};

深拷贝的循环引用问题

但是上述方法循环引用会报错,for example:

var A = {
    name:'jerry',
    age:15,
    a:undefined,
    b:{
        c:456
    }
};
A.A=A

利用Map的key可以是引用数据类型,将要拷贝的对象当做key存起来,value是深拷贝后的对象:

function deepCopy(obje,map=new Map()) {
    if(typeof obje === 'object'){
        let afterClone = Array.isArray(obje) ? [] : {};
        
        if(map.get(obje)){
		      return map.get(obje);
		  }
		  map.set(obje,afterClone);
		  
		  
        for(let item in obje){
            if(obje.hasOwnProperty(item)){
                afterClone[item] = deepCopy(obje[item],map);
            }
        }
        // return afterClone;
        return map.get(obje);
    }else{
        return obje;
    }
}

如上代码解决了循环引用的问题。

在node环境下会打印如下内容:

afterClone <ref *1> {
  name: 'jerry',
  age: 15,
  a: undefined,
  b: { c: 456 },
  A: [Circular *1]
}

node通过cirular来标识是循环引用,而浏览器会正常输出处理完的循环引用对象。

解决循环引用的原理是:在每次对复杂数据类型进行深拷贝前保存其值,如果下次又出现了该值,就不再进行拷贝,直接截止。

Date和RegExp以及null

function deepClone(target, map = new Map()) {
    if (typeof target !== 'object' || !target) {
        return target
    } else {
        let res = Array.isArray(target) ? [] : {};

        if (map.get(target)) {
            return map.get(target)
        }
        map.set(target, res)
        for (let k in target) {
            if (target.hasOwnProperty(k)) {
                const element = target[k]
                if (element instanceof Date) {
                    // 日期类型
                    let time = new Date(element.getTime()); // 与被拷贝时间达到一致
                    res[k] = time;
                } else if (element instanceof RegExp) {
                    // 正则类型
                    
                    res[k] = new RegExp(element);
                } else {
                    res[k] = deepClone(target[k], map)
                }
            }
        }
        return map.get(target)
    }
}

let obj = {
    strAttr: '字符串',
    numAttr: 6,
    booleanAttr: false,
    nullAttr: null,
    undefinedAttr: undefined,
    symbolAttr: Symbol(1),

    objAttr: {
        a: 1,
        b: 2,
    },
    arrAttr: [3, 4, 5],
    dateAttr: new Date(),
    funAttr: function () { console.log('1') },
    regAttr: new RegExp()
}
obj.objAttr.c = obj.objAttr

let cloneObj = deepClone(obj)
cloneObj.objAttr.a = 6

console.log(obj, '克隆结果', cloneObj)

Object.create()

    Object.mycreate = function (proto, properties) {
      function F() { };
      F.prototype = proto;

      let res = new F()
      if (properties) {
        Object.defineProperties(res, properties);
      }
      return res;
    }
    var hh = Object.mycreate({ a: 1 }, {mm:{value:'isme'}});
    console.dir(hh);

创建对象不同方式及存在的差异

日常开发中,经常会使用如下方式创建对象:

  • 字面量
  • new构造函数
  • Object.create

那么上述三种方式存在什么区别呢?

  • 字面量创建和new关键字创建并没有区别,创建的新对象的__proto__都指向Object.prototype,只是字面量创建更高效一些。
  • Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。———— MDN
    • 也就是说Object.create接受两个参数,第一个参数是新创建对象的原型(新创建的对象__proto__指向第一个参数),第二个对象指的是像新创建的对象中添加的属性。

    • 若是第一个参数数是null,那新对象就彻彻底底是个空对象,没有继承Object.prototype上的任何属性和方法,如hasOwnProperty()、toString()等。

排序

选择

选择排序的大体思路是每次选出最小的一个数字,放在最前边。前i个元素是已经选出来的最小的。

function selectSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    let min_index = i;
    for (let j = i + 1; j < arr.length; j++) {
      min_index = arr[j] < arr[min_index] ? j : min_index;
    }
    [arr[i], arr[min_index]] = [arr[min_index], arr[i]];
  }
  return arr;
}

冒泡

冒泡排序的具体思路是前后两个数相比较,把较大的一个放到后边。后边i个元素是已经冒泡到后边的较大的元素。

function bubbleSort(arr) {
  for (let i = arr.length - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr
}

快排

选择一个基准,将数组基于基准分为两个部分,递归进行排序。

function quickSort(arr) {
  // 递归结束条件
  if (arr.length <= 1) {
    return arr
  }

  let pivotIndex = Math.floor(arr.length / 2);
  let pivot = arr.splice(pivotIndex, 1)[0];

  let left = [];
  let right = [];

  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return quickSort(left).concat([pivot], quickSort(right));
}

new函数

function _new(fn) {
  // 1、创建一个对象
  let res = {};
  
  // 2、连接到原型
  if (fn.prototype !== null) {
    res.__proto__ = fn.prototype;
  }

  //3、绑定this
  let ret = fn.apply(res, Array.prototype.slice.call(arguments, 1));

  // 4、返回对象(构造函数返回的是基本类型或没有返回值---返回res;返回的是引用类型---返回构造函数返回的值)
  if ((typeof ret === "object" || typeof ret === "function") && ret != null) {
    return ret;
  }

  return res;
}

// 测试
function Person(name, age) {
  this.name = name;
  this.age = age;
}

let obj = _new(Person, "jerry", 22);
console.log(obj); //Person { name: 'jerry', age: 22 }

function Another(name, age) {
  return {
    name: name,
    age: age,
    class: 3,
  };
}

let obj2 = _new(Another, "tom", 23);
console.log(obj2) //{ name: 'tom', age: 23, class: 3 }

new的过程都干了什么

  1. 创建空对象
  2. 链接原型
  3. 改变this指向,并向对象中添加属性
  4. 返回对象(若构造函数中返回的是引用数据类型,则返回构造函数返回的内容,否则返回新创建的对象)

instanceof

function instanceOf(left, right) {
  let prototype = right.prototype;
  left = left.__proto__;
  while (true) {
    if (left === null) {
      return false;
    }
    if (prototype === left) {
      return true;
    }
    left = left.__proto__;
  }
}
console.log(instanceOf([], Object));

call、apply、bind

以下实现的主要思路是对象中的方法,this指向对象。

call

    Function.prototype._call = function (obj) {
      obj = obj ? obj : window; // 不传入入参,this指向window
      obj.fn = this; //this是调用call的方法
      let args = [...arguments].slice(1);
      let result = obj.fn(...args);
      delete obj.fn;
      return result;
    };

    //测试
    function console_() {
      console.log(this.name);
    }

    let name = 'windowwwww' 
    let obj1 = {
      name: "obj",
    };
    console_._call(obj1) //obj
    console_.call() //windowwwww

apply

    Function.prototype._apply = function (obj, arr) {
      obj = obj ? obj : window;
      obj.fn = this;
      let result;

      if (!arr) {
        result = obj.fn();
      } else {
        result = obj.fn(...arr);
      }

      delete obj.fn;
      return result;
    };

    function console_() {
      console.log(this.name);
    }

    let name = 'windowwwww' 
    let obj1 = {
      name: "obj",
    };
    console_._apply(obj1) //obj
    console_._apply() //windowwwww

bind

Function.prototype._bind = function (obj) {
  var args = [...arguments].slice(1);
  var fn = this; //保存this
  
  return function F(){
    if(this instanceof F){
        return new fn(...args,...argumrnts)
    }
    return fn.apply(obj,[...args,...arguments])
  }
};

sleep

setTimeout

function sleep(fn, wait) {
    setTimeout(fn, wait)
}

let testFun = () => { console.log('sleep',Date.now()) }
sleep(testFun,1000)
console.log('time',Date.now())

/*
time 1661666516858
sleep 1661666517860
*/

promise

function sleep(wait) {
    return new Promise(res => {
        setTimeout(res, wait)
    })
}

sleep(1000).then(() => {
    console.log('sleep', Date.now())
})
console.log('time', Date.now())

/*
time 1661666684579
sleep 1661666685582
*/

反转字符串

function reverseStr(str){
    if (str === '') return '';
    return reverseStr(str.substr(1)) + str.charAt(0);
}

console.log(reverseStr('123'))
function reverseStr(str){
    return str.split('').reverse().join('')
}

console.log(reverseStr('123'))
let reverseString = function (s) {
    let res = s.split('')
    let left = 0, right = res.length - 1;
    while (left < right) {
        let t = res[right]
        res[right] = res[left]
        res[left] = t

        left++
        right--
    }
    return res.join('')
};

console.log(reverseString('1234567'))

二叉树

数组节点生成二叉树

array = [1, 2, 3, 4, 5];

function creatTree(arr) {
  //创建节点数组
  function Node(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
  var nodeArr = [];
  arr.forEach((e) => {
    nodeArr.push(new Node(e));
  });
  console.log(nodeArr);
<!--  /* 节点数组创建完成-->
<!--  [-->
<!--  Node { key: 1, left: null, right: null },-->
<!--  Node { key: 2, left: null, right: null },-->
<!--  Node { key: 3, left: null, right: null },-->
<!--  Node { key: 4, left: null, right: null },-->
<!--  Node { key: 5, left: null, right: null }-->
<!--]-->
<!--*/-->

// 生成二叉树
<!--找到最后一个父节点-->

  var lastParent = parseInt(nodeArr.length / 2 - 1);

  for (let i = 0; i < lastParent; i++) {
    nodeArr[i].left = nodeArr[i * 2 + 1];
    nodeArr[i].right = nodeArr[i * 2 + 2];
  }

  nodeArr[lastParent].left = nodeArr[2 * lastParent + 1];

<!--看最后一个父节点又没有有孩子-->
  if (nodeArr.length % 2 === 1) {
    nodeArr[lastParent].right = nodeArr[2 * lastParent + 2];
  }

  return nodeArr;
}

console.log('结果--->',creatTree(array));
<!--/*-->
<!--[-->
<!--  Node {-->
<!--    key: 1,-->
<!--    left: Node { key: 2, left: [Node], right: [Node] },-->
<!--    right: Node { key: 3, left: null, right: null }-->
<!--  },-->
<!--  Node {-->
<!--    key: 2,-->
<!--    left: Node { key: 4, left: null, right: null },-->
<!--    right: Node { key: 5, left: null, right: null }-->
<!--  },-->
<!--  Node { key: 3, left: null, right: null },-->
<!--  Node { key: 4, left: null, right: null },-->
<!--  Node { key: 5, left: null, right: null }-->
<!--]-->
<!--*/-->

二叉树的层次遍历(广度遍历)

二叉树的广度遍历借助队列完成

function levelOrder(root) {

  var que = [];
  que.push(root);
  
  while (que.length != 0) {
  
    var tmp = que.shift();
    console.log(tmp.data);
    
    if (tmp.left != null) {
      que.push(tmp.left);
    }
    if (tmp.right != null) {
      que.push(tmp.right);
    }
  }
}

var stack = function () {
  this.data = [];

  this.push = push;
  this.pop = pop;

  this.clear = clear;
  this.length = length;
};

var push = function (e) {
  this.data.push(e);
};

var pop = function () {
  this.data.pop();
};

var clear = function () {
  this.length = 0;
};

var length = function () {
  return this.data.length;
};

队列

var que = function () {
  this.data = [];

  this.enQuw = enQuw;
  this.deQue = deQue;

  this.clear = clear;
  this.length = length;
};

var enQuw = function (e) {
  this.data.push(e);
};

var deQue = function () {
  this.data.shift();
};

var clear = function () {
  this.length = 0;
};

var length = function () {
  return this.data.length;
};

栈实现队列

var stackA = [];
var stackB = [];

function push(node){
  stackA.push(node);
}

function pop(){
  if(stackB.length){
    return stackB.pop();
  }else{
    if(stackA.length){
      var len = stackA.length;
      for(i=0;i<len;i++){
        stackB.push(stackA.pop());
      }
      return stackB.pop()
    }else{
      return null;
    }
  }
}

push(1);

push(2);
push(3);
console.log(stackA);  //[ 1, 2, 3 ]
console.log(pop()) //1