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

一、html和css部分

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

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

2、如何理解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 就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。

3、如何清除浮动?

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

4、用纯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;
}

5、css实现三栏布局

1、flex方式

<!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>

2、绝对定位方式

<!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>

3、浮动方式

<!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>

6、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>

7、让一个div垂直居中

         1、宽度和高度是已知的

<!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>

         2、宽度和高度是未知的

<!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>

二、JS

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

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

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

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

3、防抖和节流

综合应用场景

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

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

  • 非立即执行版的意思是触发事件后函数不会立即执行,而是在 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 秒中只执行一次函数。 节流会稀释函数的执行频率。

对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。

  • 时间戳版的函数触发是在时间段内开始的时候
  • 定时器版的函数触发是在时间段内结束的时候。
/**
 * @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)
            }
        }
    }
}

4、数组的常用方法

改变原数组的方法

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

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


2、sort() 数组排序

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


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

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


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

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


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

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


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

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


7、copyWithin() 指定位置的成员复制到其他位置

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


8、fill() 填充数组

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

不改变原数组的方法

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

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

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

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

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

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

5、闭包

        闭包概念

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


        闭包用途

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


        闭包缺点

1、由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2、闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。


        闭包应用场景

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

6、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() }

7、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);
        }
    }
}

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

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

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

浅拷贝的实现方式

1、自定义函数

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

2、ES6的Object.assign()方法

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

深拷贝的实现方式

1、JSON.stringify 和 JSON.parse

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

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

2、Lodash

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

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

3、自己封装

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;
}

9、跨域(重点)

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

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

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

10、如何阻止冒泡?

冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(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; 
}

11、如何阻止默认事件?

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; 
}

13、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来说,这里就是暂缓性死区

14、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(); // 我是汪某!

15、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) {}

三、手撸代码

1、实现一个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;
}

2、实现一个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;
}

3、实现一个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]

4、手写一个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)
})

5、手写防抖(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();
        }
    }
}

6、实现一个继承

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', '汪某');


7、手写一个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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值