前端面试基础题(HTML与JS)

1、html和css部分

一、如何理解CSS的盒子模型?

标准盒子模型:宽度=内容的宽度(content)+ border + padding
 
低版本IE盒子模型:宽度=内容宽度(content+border+padding)

二、如何理解BFC?(什么是BFC?)

BFC(Block Formatting Context)格式化上下文,是 Web 页面中盒模型布局的 CSS 渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。
* 造成 BFC 的条件
   * 浮动元素,float 除 none 之外的值
   * 定位元素,position(absolute,fixed)
   * display 为如下其中之一的值 inline-block,table-cell,table-caption
   * overflow 除了 visible 之外的值(hidden,auto,scroll)
* BFC 的特性
   * 内部的 Box 会在垂直方向上一个接一个的放置。
   * 垂直方向上的距离由 margin 决定
   * bfc 的区域不会与 float 的元素区域重叠。
   * 计算 bfc 的高度时,浮动元素也参与计算
   * bfc 就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。

三、如何清除浮动?

不清楚浮动会发生高度塌陷:浮动元素父元素高度自适应(父元素不写高度时,子元素写了浮动后,父元素会发生高度塌陷)
一、clear清除浮动(添加空div法)在浮动元素下方添加空div,并给该元素写css样式:   {clear:both;height:0;overflow:hidden;}
二、 给浮动元素父级设置高度
三、 父级同时浮动(须要给父级同级元素添加浮动)
四、 父级设置成inline-block,其margin: 0 auto居中方式失效
五、 给父级添加overflow:hidden 清除浮动方法
六、 万能清除法 after伪类 清浮动(如今主流方法,推荐使用)
 
.float_div:after{
    content:".";
    clear:both;
    display:block;
    height:0;
    overflow:hidden;
    visibility:hidden;
}
.float_div{
    zoom:1
}

四、用纯CSS建立一个三角形的原理是什么?(transparent 透明度)

span {
    width: 0;
    height: 0;
    border-top: 40px solid transparent;
    border-left: 40px solid transparent;
    border-right: 40px solid transparent;
    border-bottom: 40px solid #ff0000;
}

五、css实现三栏布局

一、flex方式javascript

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            display: flex;
            justify-content: center;
            height: 200px;
        }
        .left {
            width: 200px;
            background-color: red;
            height: 100%;
        }
        .content {
            background-color: yellow;
            flex: 1;
        }
        .right {
            width: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left"></div>
        <div class="content"></div>
        <div class="right"></div>
    </div>
</body>
</html>

二、绝对定位方式css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            position: relative;
            height: 200px;
        }
        .left {
            width: 200px;
            background-color: red;
            left: 0;
            height: 100%;
            position: absolute;
        }
        .content {
            background-color: yellow;
            left: 200px;
            right: 200px;
            height: 100%;
            position: absolute;
        }
        .right {
            width: 200px;
            background-color: green;
            right: 0;
            height: 100%;
            position: absolute;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left"></div>
        <div class="content"></div>
        <div class="right"></div>
    </div>
</body>
</html>

三、浮动方式html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            height: 200px;
        }
        .left {
            width: 200px;
            background-color: red;
            float: left;
            height: 100%;
        }
        .content {
            background-color: yellow;
            height: 100%;
        }
        .right {
            width: 200px;
            background-color: green;
            float: right;
            height: 100%;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left"></div>
        <div class="right"></div>
        <div class="content"></div>
    </div>
</body>
</html>

六、ss3实现0.5px的细线?

/* css */
.line {
    position: relative;
}
.line:after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 1px;
    background-color: #000000;
    -webkit-transform: scaleY(.5);
    transform: scaleY(.5);
}
 
/* html */
<div class="line"></div>

七、让一个div垂直居中

         一、宽度和高度是已知的java

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            width: 400px;
            height: 200px;
            position: relative;
            background: red;
        }
        .content {
            width: 200px;
            height: 100px;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-left: -100px;
            margin-top: -50px;
            background: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="content"></div>
    </div>
</body>
</html>

         二、宽度和高度是未知的nginx

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            width: 400px;
            height: 200px;
            position: relative;
            background: red;
        }
        .content {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="content"></div>
    </div>
</body>
</html>

2、JS

一、new一个对象的过程当中发生了什么嘛

1. 建立空对象;
var obj = {};
2. 设置新对象的constructor属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的prototype对象;
obj.__proto__ = ClassA.prototype;
3. 使用新对象调用函数,函数中的this被指向新实例对象:
ClassA.call(obj);//{}.构造函数();          
4. 将初始化完毕的新对象地址,保存到等号左边的变量中

二、宏任务跟微任务(面试过,当时一脸懵逼)

  1. macro-task(宏任务):包括总体代码script,setTimeout,setInterval
  2. micro-task(微任务):Promise,process.nextTick

三、防抖和节流

综合应用场景es6

  • 防抖(debounce):就是指触发事件后在 n 秒内函数只能执行一次,若是在 n 秒内又触发了事件,则会从新计算函数执行时间。
    • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
    • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
  • 节流(throttle):就是指连续触发事件可是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
    • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
    • 监听滚动事件,好比是否滑到底部自动加载更多,用throttle来判断 所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,若是在 n 秒内又触发了事件,则会从新计算函数执行时间。

防抖函数分为非当即执行版和当即执行版。web

  • 非当即执行版的意思是触发事件后函数不会当即执行,而是在 n 秒后执行,若是在 n 秒内又触发了事件,则会从新计算函数执行时间。
  • 当即执行版的意思是触发事件后函数会当即执行,而后 n 秒内不触发事件才能继续执行函数的效果。
/**
 * @desc 函数防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表当即执行,false 表非当即执行
 */
function debounce(func,wait,immediate) {
    let timeout;
 
    return function () {
        let context = this;
        let args = arguments;
 
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

所谓节流,就是指连续触发事件可是在 n 秒中只执行一次函数。 节流会稀释函数的执行频率。面试

对于节流,通常有两种方式能够实现,分别是时间戳版和定时器版。json

  • 时间戳版的函数触发是在时间段内开始的时候
  • 定时器版的函数触发是在时间段内结束的时候。
/**
 * @desc 函数节流
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param type 1 表时间戳版,2 表定时器版
 */
function throttle(func, wait ,type) {
    if(type===1){
        let previous = 0;
    }else if(type===2){
        let timeout;
    }
    return function() {
        let context = this;
        let args = arguments;
        if(type===1){
            let now = Date.now();
 
            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }else if(type===2){
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }
    }
}

四、数组的经常使用方法

改变原数组的方法跨域

一、splice() 添加/删除数组元素

语法:arrayObject.splice(index,howmany,item1,.....,itemX)
参数:
   1.index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
   2.howmany:可选。要删除的项目数量。若是设置为 0,则不会删除项目。
   3.item1, ..., itemX: 可选。向数组添加的新项目。
 
返回值: 若是有元素被删除,返回包含被删除项目的新数组。


二、sort() 数组排序

语法:arrayObject.sort(sortby)
参数:
   1.sortby 可选。规定排序顺序。必须是函数。。
 
返回值: 返回包排序后的新数组。


三、pop() 删除一个数组中的最后的一个元素

语法:arrayObject.pop()
参数:无
 
返回值: 返回被删除的元素。


四、push() 向数组的末尾添加元素

语法:arrayObject.push(newelement1,newelement2,....,newelementX)
参数:
   1.newelement1    必需。要添加到数组的第一个元素。
   2.newelement2    可选。要添加到数组的第二个元素。
   3.newelementX    可选。可添加若干个元素。
 
返回值: 返回被删除的元素。


五、unshift() 向数组的开头添加一个或更多元素

语法:arrayObject.unshift(newelement1,newelement2,....,newelementX)
参数:
   1.newelement1    必需。要添加到数组的第一个元素。
   2.newelement2    可选。要添加到数组的第二个元素。
   3.newelementX    可选。可添加若干个元素。
 
返回值: arrayObject 的新长度。。


六、reverse() 颠倒数组中元素的顺序

语法:arrayObject.reverse()
参数:无
 
返回值: 颠倒后的新数组。


七、copyWithin() 指定位置的成员复制到其余位置

语法: array.copyWithin(target, start = 0, end = this.length)
参数:
   1.target(必需):从该位置开始替换数据。若是为负值,表示倒数。
   2.start(可选):从该位置开始读取数据,默认为 0。若是为负值,表示倒数。
   3.end(可选):到该位置前中止读取数据,默认等于数组长度。若是为负值,表示倒数。
 
返回值: 返回当前数组。


八、fill() 填充数组

语法: array.fill(value, start, end)
参数:
   1.value  必需。填充的值。
   2.start  可选。开始填充位置。
   3.end    可选。中止填充位置 (默认为 array.length)
 
返回值: 返回当前数组。

不改变原数组的方法

一、slice() 浅拷贝数组的元素
语法: array.slice(begin, end);
参数:
   1.begin(可选): 索引数值,接受负值,从该索引处开始提取原数组中的元素,默认值为0。
   2.end(可选):索引数值(不包括),接受负值,在该索引处前结束提取原数组元素,默认值为数组末尾(包括最后一个元素)。
 
返回值: 返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象,且原数组不会被修改。

二、join() 数组转字符串
语法:array.join(str)
参数:
   1.str(可选): 指定要使用的分隔符,默认使用逗号做为分隔符。
 
返回值: 返回生成的字符串。

三、concat() 合并两个或多个数组
语法: var newArr =oldArray.concat(arrayX,arrayX,......,arrayX)
参数:
   1.arrayX(必须):该参数能够是具体的值,也能够是数组对象。能够是任意多个。
 
返回值: 返回返回合并后的新数组。

四、indexOf() 查找数组是否存在某个元素
语法:array.indexOf(searchElement,fromIndex)
参数:
   1.searchElement(必须):被查找的元素
   2.fromIndex(可选):开始查找的位置(不能大于等于数组的长度,返回-1),接受负值,默认值为0。
 
返回值: 返回下标

五、lastIndexOf() 查找指定元素在数组中的最后一个位置
语法:arr.lastIndexOf(searchElement,fromIndex)
参数:
   1.searchElement(必须): 被查找的元素
   2.fromIndex(可选): 逆向查找开始位置,默认值数组的长度-1,即查找整个数组。
 
返回值: 方法返回指定元素,在数组中的最后一个的索引,若是不存在则返回 -1。(从数组后面往前查找)

六、includes() 查找数组是否包含某个元素
语法: array.includes(searchElement,fromIndex=0)
参数:
   1.searchElement(必须):被查找的元素
   2.fromIndex(可选):默认值为0,参数表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false。负值绝对值超过长数组度,重置从0开始搜索。
 
返回值: 返回布尔

五、闭包

闭包概念

可以读取其余函数内部变量的函数。
或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。


        闭包用途

一、读取函数内部的变量
二、让这些变量的值始终保持在内存中。不会再f1调用后被自动清除。
三、方便调用上下文的局部变量。利于代码封装。
缘由:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,所以f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。


        闭包缺点

一、因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露。解决方法是,在退出函数以前,将不使用的局部变量所有删除。
二、闭包会在父函数外部,改变父函数内部变量的值。因此,若是你把父函数看成对象(object)使用,把闭包看成它的公用方法(Public Method),把内部变量看成它的私有属性(private value),这时必定要当心,不要随便改变父函数内部变量的值。


        闭包应用场景

一、闭包应用场景之setTimeout
//setTimeout传递的第一个函数不能带参数
setTimeout((param) => {
    alert(param)
}, 1000);
 
//经过闭包能够实现传参效果
function func(param) {
    return function() {
        alert(param)
    }
}
var f1 = func('汪某');
setTimeout(f1, 1000)//汪某

六、js原型和原型链

每一个对象都会在其内部初始化一个属性,就是prototype(原型),当咱们访问一个对象的属性时,若是这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有本身的prototype,因而就这样一直找下去,也就是咱们平时所说的原型链的概念。

关系:instance.constructor.prototype = instance.proto

特色:JavaScript对象是经过引用来传递的,咱们建立的每一个新对象实体中并无一份属于本身的原型副本,当咱们修改原型时,与之相关的对象也会继承这一改变。 当咱们须要一个属性时,JavaScript引擎会先看当前对象中是否有这个属性,若是没有的话,就会查找它的prototype对象是否有这个属性,如此递推下去,一致检索到Object内建对象。

function Func(){}
Func.prototype.name = "汪某";
Func.prototype.getInfo = function() {
   return this.name;
}
var person = new Func();
console.log(person.getInfo());//"汪某"
console.log(Func.prototype);//Func { name = "汪某", getInfo = function() }

七、Promise

一句话归纳Promise:Promise对象用于异步操做,它表示一个还没有完成且预计在将来完成的异步操做。

promise是用来解决两个问题的:

  • 回调地狱,代码难以维护,经常第一个的函数的输出是第二个函数的输入这种现象
  • promise能够支持多个并发的请求,获取并发请求中的数据

这个promise能够解决异步的问题,自己不能说promise是异步的

/*Promise 的简单实现*/
 
class MyPromise {
    constructor(fn) {
        this.resolvedCallbacks = [];
        this.rejectedCallbacks = [];
        this.state = "PADDING";
        this.value = "";
        fn(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(value) {
        if (this.state === "PADDING") {
            this.state = "RESOLVED";
            this.value = value;
            this.resolvedCallbacks.forEach(cb => cb());
        }
    }
    reject(value) {
        if (this.state === "PADDING") {
            this.state = "REJECTED";
            this.value = value;
            this.rejectedCallbacks.forEach(cb => cb());
        }
    }
    then(resolve = function() {}, reject = function() {}) {
        if (this.state === "PADDING") {
            this.resolvedCallbacks.push(resolve);
            this.rejectedCallbacks.push(reject);
        }
        if (this.state === "RESOLVED") {
            resolve(this.value);
        }
        if (this.state === "REJECTED") {
            reject(this.value);
        }
    }
}

八、深拷贝、浅拷贝(重点)

浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象自己,新旧对象仍是共享同一块内存;但深拷贝会另外创造一个如出一辙的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;

区别:浅拷贝只复制对象的第一层属性、深拷贝能够对对象的属性进行递归复制;

浅拷贝的实现方式

一、自定义函数

function simpleCopy (initalObj) {
   var obj = {};
   for ( var i in initalObj) {
    obj[i] = initalObj[i];
   }
   return obj;
}

二、ES6的Object.assign()方法

let newObj = Object.assign({}, obj);

深拷贝的实现方式

一、JSON.stringify 和 JSON.parse

用 JSON.stringify 把对象转换成字符串,再用 JSON.parse 把字符串转换成新的对象。

let newObj = JSON.parse(JSON.stringify(obj));

二、Lodash

用 lodash 函数库提供的 _.cloneDeep 方法实现深拷贝。

var _ = require('lodash');
var newObj = _.cloneDeep(obj);

三、本身封装

function deepClone(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj === "object") {
        // for...in 会把继承的属性一块儿遍历
        for (let key in obj) {
            // 判断是否是自有属性,而不是继承属性
            if (obj.hasOwnProperty(key)) {
                //判断ojb子元素是否为对象或数组,若是是,递归复制
                if (obj[key] && typeof obj[key] === "object") {
                    objClone[key] = this.deepClone(obj[key]);
                } else {
                    //若是不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}

九、跨域(重点)

跨域须要针对浏览器的同源策略来理解,同源策略指的是请求必须是同一个端口,同一个协议,同一个域名,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。

受浏览器同源策略的影响,不是同源的脚本不能操做其余源下面的对象。想要操做另外一个源下的对象是就须要跨域。

  • jsonp
  • iframe
  • 跨域资源共享(CORS)
  • nginx 代理跨域

十、如何阻止冒泡?

冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true。

//阻止冒泡行为 
function stopBubble(e) { 
//若是提供了事件对象,则这是一个非IE浏览器 
if ( e && e.stopPropagation ) 
    //所以它支持W3C的stopPropagation()方法 
    e.stopPropagation(); 
else 
    //不然,咱们须要使用IE的方式来取消事件冒泡 
    window.event.cancelBubble = true; 
}

十一、如何阻止默认事件?

w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false

//阻止浏览器的默认行为 
function stopDefault( e ) { 
    //阻止默认浏览器动做(W3C) 
    if ( e && e.preventDefault ) 
        e.preventDefault(); 
    //IE中阻止函数器默认动做的方式 
    else 
        window.event.returnValue = false; 
    return false; 
}

1三、var,let,const(ES6)

//变量提高
console.log(a);  // undefined
console.log(b);  // 报错
console.log(c);  // 报错
var a = 1;
let b = 2;
const c = 3;
 
// 全局声明
console.log(window.a) //  1 
 
// 重复声明
let b  = 200;//报错

其实这里很容易理解,var是能够变量提高的。而let和const是必须声明后才能调用的。 对于let和const来讲,这里就是暂缓性死区

1四、Class(ES6)

es6新增的Class其实也是语法糖,js底层其实没有class的概念的,其实也是原型继承的封装。

class People {
    constructor(props) {
        this.props = props;
        this.name = '汪某';
    }
    callMyName() {
        console.log(this.name);
    }
}
class Name extends People { // extends 其实就是继承了哪一个类
    constructor(props) {
        //  super至关于 把类的原型拿过来 
        //  People.call(this, props)
        super(props)
    }
    callMyApple() {
        console.log('我是汪某!')
    }
}
 
let a = new Name('啊啊啊')
a.callMyName(); //汪某
a.callMyApple(); // 我是汪某!

1五、Set(ES6)

Set数据结构相似数组,但全部成员的值惟一。

let a = new Set();
[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x));
for(let k of a){
    console.log(k)
};
// 1 2 3 4 5

基本使用

let a = new Set([1,2,3,3,4]);
[...a]; // [1,2,3,4]
a.size; // 4
 
// 数组去重
[...new Set([1,2,3,4,4,4])];// [1,2,3,4]

方法

  • add(value):添加某个值,返回 Set 结构自己。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除全部成员,没有返回值。
let a = new Set();
a.add(1).add(2); // a => Set(2) {1, 2}
a.has(2);        // true
a.has(3);        // false
a.delete(2);     // true  a => Set(1) {1}
a.clear();       // a => Set(0) {}

3、手撸代码

一、实现一个call或 apply

/******** Call  ********/
Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;
 
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
 
    var result = eval('context.fn(' + args +')');
 
    delete context.fn
    return result;
}

        /******** Apply  ********/
Function.prototype.apply2 = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;
 
    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }
 
    delete context.fn
    return result;
}

二、实现一个Function.bind

Function.prototype.bind2 = function (context) {
    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
}

三、实现一个new操做符

let a = new Map([
    ['name', 'leo'],
    ['age', 18]
])
for (let i of a.keys()) {
    console.log(i)
};
//name 
//age
 
for (let i of a.values()) {
    console.log(i)
};
//leo 
//18
 
for (let i of a.entries()) {
    console.log(i)
};
//["name", "leo"]
 
a.forEach((v, k, m) => {
    console.log(`key:${k},value:${v},map:${m}`)
})
//["age", 18]

四、手写一个Promise(中高级必考)

function myPromise(constructor) {
    let self = this;
    self.status = "pending"
        //定义状态改变前的初始状态
    self.value = undefined;
    //定义状态为resolved的时候的状态
    self.reason = undefined;
    //定义状态为rejected的时候的状态
    function resolve(value) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.value = value;
            self.status = "resolved";
        }
    }
    function reject(reason) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.reason = reason;
            self.status = "rejected";
        }
    }
    //捕获构造异常
    try {
        constructor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}
 
//同时,须要在 myPromise的原型上定义链式调用的 then方法:
myPromise.prototype.then = function(onFullfilled, onRejected) {
    let self = this;
    switch (self.status) {
        case "resolved":
            onFullfilled(self.value);
            break;
        case "rejected":
            onRejected(self.reason);
            break;
        default:
    }
}
 
//测试一下:
var p = new myPromise(function(resolve, reject) {
    resolve(1)
});
p.then(function(x) {
    console.log(x)
})

五、手写防抖(Debouncing)和节流(Throttling)

// 防抖函数
function debounce(fn, wait = 50, immediate) {
    let timer;
    return function() {
        if (immediate) {
            fn.apply(this, arguments)
        }
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, wait)
    }
}
// 节流函数
function throttle(fn, wait) {
    let prev = new Date();
    return function() {
        const args = arguments;
        const now = new Date();
        if (now - prev > wait) {
            fn.apply(this, args);
            prev = new Date();
        }
    }
}

六、实现一个继承

function Parent(name) {
    this.name = name;
}
 
Parent.prototype.sayName = function() {
    console.log('parent name:', this.name);
}
 
function Child(name, parentName) {
    Parent.call(this, parentName);
    this.name = name;
}
 
function create(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
    console.log('child name:', this.name);
}
 
Child.prototype.constructor = Child;
var parent = new Parent('汪某');
parent.sayName();// parent name: 汪某
var child = new Child('son', '汪某');

七、手写一个JS深拷贝(重点)

function deepCopy(obj) {
    //判断是不是简单数据类型,
    if (typeof obj == "object") {
        //复杂数据类型
        var result = obj.constructor == Array ? [] : {};
        for (let i in obj) {
            result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
        }
    } else {
        //简单数据类型 直接 == 赋值
        var result = obj;
    }
    return result;
}