JS面试考题记录

3,255 阅读6分钟

箭头函数和普通函数的区别!

  1. 箭头函数没有prototype(原型),所以箭头函数本身没有this
let a = ()=>{}
console.log(a.prototype) // undefined
  1. 箭头函数的this指向在定义的时候继承自外层第一个普通函数的this。
let a, 
    barObj = {msg: 'bar'},
    fooObj = { msg: 'foo' };

function foo(){
    a();
}
function bar(){
    a = ()=>{console.log(this)}
};

bar.call(barObj);
foo.call(fooObj);
// {msg: 'bar'}
  1. 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window
let foo = ()=>{console.log(this)};
foo(); // window

'use strict'
let foo = ()=>{console.log(this)};
foo(); // window
  1. 箭头函数本身的this指向不能改变,但可以修改它要继承的对象this。
let foo = ()=>{console.log(this)};
let fooObj = {msg: 'fooObj'};
foo.call(fooObj); // window


let a, 
    barObj = {msg: 'bar'},
    fooObj = { msg: 'foo' };

function foo(){
    a();
}
function bar(){
    a = ()=>{console.log(this)}
};

bar.call(barObj);
foo.call(fooObj);
// {msg: 'bar'}

  1. 箭头函数的this指向全局,使用aguments会报未生命的错误。
let foo = ()=>
foo(); //Uncaught ReferenceError: arguments is not defined
  1. 箭头函数的this指向普通函数时,它的arguments指向普通函数的arguments
function foo(){
    console.log(arguments);
    let bar = ()=>{
        console.log(arguments);
    }
    bar();
}
foo(1,2,3); // [1,2,3] [1,2,3]

  1. 使用new调用函数会报错,因为箭头函数没有constructor
let a = ()=>{}
let b = new a();  // a is not a constructor

  1. 箭头函数不支持new.target
let a = ()=>{
    console.log(new.target)
}
a(); // new.target expression is not allowed here
  1. 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名
function fun1(a, a){
    console.log(a, arguments)
}
let fun2 = (a, a) => {
    console.log(a); 
} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
fun1(1,2); // 2 [1,2]
  1. 箭头函数相对于普通函数语法更简洁优雅。

instanceof 实现原理

function _instanceof(leftValue, rightValue){
    let leftValue = leftValue.__proto__;
    let rightValue = rightValue.prototype;
    
    while(true){
        if(leftValue == null){
            return false;
        }
        if(leftValue === rightValue){
            return true;
        }
        leftValue = leftValue.__proto__;
    }
}
function Foo() {
}

Object instanceof Object // true
Function instanceof Function // true
Function instanceof Object // true
Foo instanceof Foo // false
Foo instanceof Object // true
Foo instanceof Function // true

promise.all函数实现

promise.all = funciton(promis){
    let arr =[];
    let i = 0;
    
    let proccess = function(reuslt, index, resolve){
        arr[index] = result;
        i++;
        if(i == promis.length){
            return resolve(arr);
        }
    }
    
    return new Promise( (resolve,rejcet)=>{
        for(let i = 0; i < promis.length; i++){
            promis[i].then( (result)=>{proccess(result, index, resolve)} , (err)=>{reject(err)} );
        }
    } )
}

bind方法实现

Function.prototype._bind = function(context, ...args){
    if(typeof context !== 'function'){
        throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
    }
    let self = this;
    let bound = function(){
        return this.apply(context, args)
    }
    return bound
}

如果_bind 返回的函数被作为构造函数来使用,此时 _bind 指定的this值会失效,但传入的参数依然生效。改版一:

Function.prototype._bind = function(context, ...args){
    if(typeof context !== 'function'){
        throw new Error('Function.prototype.bind - what is trying to be bound is not callable')
    }
    let self = this;
    let bound = function(...arr){
        // 判断是否是作为构造函数
        //是的话,返回this 指向new创建的实例
        //否的话,指向context
        return self.apply(context instanceof bound ? this : context, [].concat(args, arr));
    }
    
    //返回函数的prototype 为 绑定函数的prototype,实例讲究可以继承绑定函数的原型中的值
    bound.prototype = this.prototype;
    return bound;
}

因为 bound.prototype = this.prototype,如果实例函数修修改prototype,也 修改了绑定函数的prototype。

function bar() {}

var bindFoo = bar.bind2(null);

// 修改 bindFoo 的值
bindFoo.prototype.value = 1;

// 导致 bar.prototype 的值也被修改了
console.log(bar.prototype.value)    // 1

改版二:

Function.prototype._bind = function(context, ...args){
    if(typeof context !== 'function'){
        throw new Error('Function.prototype.bind - what is trying to be bound is not callable');
    }
    let self = this;
    let fun = function(){};
    let bound = function(...arr){
        return self.apply(context instanceof fun ? this : context, [].concat(args,  arr) )
    }
        
    fun.prototype = self.prototype;
    bound.prototype = fun.prototype;
    return bound;
}

防抖函数

function debounce(fn, delay, immediate){
    let timer = null;
    
    return (...args)=>{
        let context = this;     
        if(timer){ clearTimeout(timer) }
        if(immediate){
            if(timer){return}
            
            fn.apply(context, args);
     
            timer = setTimeout(()=>{
                timer = null;   
            },delay)
        }else {
            timer = setTimeout( ()=>{
                fn.apply( context, args );
            } , delay)
        }
        
    }
}

截流函数

function throttle(fn, delay, mustRunDelay ){
    let c = null;
    let start = null;
    
    return (...args)=>{
        let context = this;
        let current = new Date();
        if(timer){ clearTimeout(timer) }
        
        if(!start){
            start = current;
        }
        
        if(mustRunDelay && current - now >= mustRunDelay){
            fn.apply(context, args);
            start = current;
        }else{
            tiemr = setTimeout(()=>{
                fn.apply(context, args);
                start = current;    
            }, delay)
        }
        
    }
}

深克隆

function deepClone(obj, hash = new Map()){
    if( obj instanceof RegExp ){return new RegExp(obj)}
    if( obj instanceof Date ){ return new Data(obj) }
    if( obj == null || typeof obj !== 'object' ){
        return obj
    }
    if( hash.has(obj) ){
        return hash.get(obj);
    }
    
    let t = new obj.constructor();
    hash.set( obj, t );
    
    for(let key in obj){
        if( obj.hasOwnProperty( key ) ){
            t[key] = deepClone(obj[key], hash );
        }
    }
    return t;
}

js equal函数

let USUAL_TYPE = ['[object Number]', '[object String]', '[object Boolean]'];
let toString = Object.prototype.toString;

function equal(v1, v2, isStrongType){
    let type1 =  toString.call(v1);
    let type2 = toString.call(v2);

    if(isStrongType || ( USUAL_TYPE.indexOf(type1) == -1 || USUAL_TYPE.indexOf(type2)  )){
        if(type1 !== type2){
            return false;
        }
    }
    
    if(Array.isArray(v1)){
        if(v1.length !== v2.length ){return false}
        for(let i = 0; i<v1.length; i++){
            if(!equal(v1[i], v2[i], isStrongType)){return false}
        }
    }else if( toString.call(v1) === '[object Object]' ){
        if(Object.keys(v1).length !== Object.keys(v2).length){return false}
        for(let key in v1){
            if(!(v1.hasOwnProperty(key) && v2.hasOwnProperty(key) ) || !equal(v1[key] , v2[key], isStrongType)) {return false}
        }
    }else{
        return isStrongType ? v1 === v2 : v1 == v2;
    }
    
    return true
}

CSS实现一个三角形

<div></div>

div{
   border-top: 10px solide transparent;
   border-left: 10px solide transparent;
   border-right: 10px solide transparent;
   border-bottom: 10px solide #000;
}

promis实现

class _Promise{
    constructor(executor){
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined; 
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        
        let resolve = (value)=>{
            if(!this.state === 'pending' )return
            this.state = 'fulfilled';
            this.value = value;
            this.onFulfilledCallbacks.forEach(fn=>fn());
        }
        
        let reject = (reason)=>{
            if(!this.state === 'pending' )return
            this.state = 'rejected';
            this.reason = reason;
            this.onRejectedCallbacks.forEach(fn=>fn());
        }
        
        try{
            executor(resolve, reject);
        }catch(err){
            resolve(err);
        }
    }
    then(onFulfilled, onRejected){
		console.log(onFulfilled, 'onFulfilled')
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value=>value;
        
        onRejected = typeof onRejected === "function" ? onRejected: err=>{throw err} 
    
        let promise2 =  new _Promise((resolve, reject)=>{
            if(this.state === "fulfilled"){
                setTimeout(()=>{
                    try{
                        let x = onFulfilled(this.value);
                		console.log(onFulfilled);
                        this.resolvePromise( promise2, x, resolve, reject );
                    }catch(err){
                        reject(err);
                    }
                }, 0)
            }
            if(this.state === "rejected"){
                setTimeout(()=>{
                    try{
                        let x = onRejected(this.reason);
                
                        this.resolvePromise( promise2, x, resolve, reject );
                    }catch(err){
                        reject(err);
                    }
                }, 0);
            }
            if(this.state === "pending" ){
                
                this.onFulfilledCallbacks.push( ()=>{
                    setTimeout(()=>{
                        try{
                            let x = onFulfilled(this.value);
                    
                            this.resolvePromise( promise2, x, resolve, reject );
                        }catch(err){
                            reject(err);
                        }
                    }, 0)
                    
                } );
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = onRejected(this.reason);
                    
                            this.resolvePromise( promise2, x, resolve, reject );
                        }catch(err){
                            reject(err);
                        }
                    }, 0)
                        
                    
                });
            }
        });
        
        return promise2
    }
    resolvePromise(promise2, x, resolve, reject){
        if(x === promise2){
            return reject( 'Chaining cycle detected for promise' );
        }
        let called;
        if(x != null && (typeof x === "object" || typeof x === "function")){
            try{
                let then = x.then;
                
                if(typeof then ==='function' ){
                    then.call(x, y => {
                        if(called)return
                        called = true;
                        this.resolvePromise( promise2, y, resolve, reject ); 
                    }, err=>{
                        if(called)return
                        called = true;
                        reject(err);
                    })
                    
                }else{
                    if(called)return;
                    resolve(x);
                    called = true;
                }
            }catch(err){
                if(called)return;
                called = true;
                reject(err);
            }
        }else{
            resolve(x);
        }
    }
}

_Promise.resolve = function(val){
    return new _Promise((resolve, reject)=>{
        resolve(val);
    })
}

_Promise.reject = function(err){
    return new _Promise((resolve, reject)=>{
        reject(err);
    })
}

_Promise.race = function(promises){
    return new _Promise((resolve, reject)=>{
        for(let i = 0; i < promises.length; i++){
            promises[i].then((val)=>{
                resolve(val);
            }, (err)=>{
                reject(err);
            })
        }
    })
}
_Promise.all = function(promises){
    let arr = [];
    let num = 0;

    function process(val, index, resolve){
        arr[index] = val;
        num++
        if(num == promises.length ){
			resolve(arr)
		};
    }
    
    return new _Promise((resolve,reject)=>{
        for(let i = 0; i < promises.length; i++){
            promises[i].then(val=>{
                process(val, i, resolve);
            }, err=>{
                reject(err)
            });
        }
    })
}


发布订阅

let EventEmitter = {
    _list: {},
    on: function(event, fn){
        _list[event] ? _list[event].push(fn) : _list[event] = [fn];
    },
    emit: function(event, ...args){
        let fns = _list[event];
        
        if(!fns)return
        
        for(let i = 0; i < fns.length; i++ ){
            fns[i].apply(this, args);
        }
    },
    off: funciton(event, fn){
        let fns = _list[event];
        if(!fns)return true;
        
        if(!fn){
            delete _list[event]
        }else{
            for(let i = 0; i < fns.length; i++){
                if(fns[i] === fn){
                    fns.split(i, 1);
                }
            }
        }
        return true
    }
    
}

输入url都发生了什么

  1. 用户输入合成url 搜索内容,还是如何url规则。合成完整的url
  2. url强求过程 DNS解析,获取IP地址,利用IP地址和服务器建立TCP链接,然后发起http请求
  3. 计算DOM树
  4. 生成DOM树后,根据CSS样式表,计算出DOM树所有节点的样式
  5. 计算布局信息,合成布局树。
  6. 生成图层树
  7. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图
  8. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  9. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

BFC

  1. 内部的Box会在垂直方向,一个接一个地放置。

  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。

  3. 每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

  4. BFC的区域不会与float box重叠。

  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

  6. 计算BFC的高度时,浮动元素也参与计算。 如何创建

  7. float的值不是none

  8. position的值不是static或者relative

  9. display的值是inline-block、table-cell、flex、table-caption或者inline-flex

  10. overflow的值部位visible BFC的作用

  11. 利用BFC避免margin重叠

  12. 清除浮动