JS面試考題記錄

TooHard發表於2019-12-15

箭頭函式和普通函式的區別!

  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. 清除浮動

相關文章