前端面试题

144 阅读3分钟

科里化函数

将多参函数变成单参函数

function curry(func, ...args){
    return function(...subArgs){
        const totalArgs = [...args, ...subArgs];
        if(totalArgs.length >= func.length){
            //参数够了
            return func(...totalArgs);
        } else {
            // 参数不够
            return curry(func,...totalArgs);
        }
    }
}

函数防抖和函数节流

节流是在规定的时间内,只运行一次函数。与函数防抖的不同是,防抖要等到不触发这个事件后多少时间,才运行函数。

function debounce(callback,time){
   let timer;
   return function(){
       clearTimeout(timer);
       timer = setTimeout(()=>{
           callback.apply(null, arguments)
       }, time)
   }
}

function throttle(callback, time){
    let timer;
    return function(){
        if(timer) return;
        timer = setTimeout(()=>{
            callback.apply(null, arguments);
            timer = null;
        },tiem)
    }
}

call,apply,bind

Function.prototype.call = function(context, ...args){
    context = context || window;
    const flag = Symbol('fn');
    context[flag] = this;
    
    context[flag](...args);
    delete context[flag];
}


Function.prototype.apply = function(context, args){
    context = context || window;
    const flag = Symbol('fn');
    context[flag] = this;
    
    context[flag](...args);
    delete context[flag];
}

Function.prototype.bind = function(context, ...args){
    context = context || window;
    const flag = Symbol('fn');
    context[flag] = this;
    
    return function(..._args){
        args = args.contat(_args);
        context[flag](...args);
        delete context[flag];
    }

}

jsonp

jsonp只能用于get请求

严重影响服务器的正常响应格式

CORS跨域:access-control-allow-origin: my.com

function jsonp(url){
    const script = document.creatElement('script');
    script.url = url;
    document.body.appendChild(script);
    script.onload = function(){
        // script元素加载完毕后移除
        script.remove();
    }
}

function callback(data){
    console.log(data);
}

jsonp('URL');

ajax

function ajax({
    data = {},
    type = 'get',
    async = true,
    url,
    success,
    error
}) {
    let xhr = null;
    if (window.XMLHttpRequest) {
        // 非IE
        xhr = new XMLHttpRequest();
    } else {
        // IE浏览器
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    if (type === 'post') {
        xhr.open(type, url, async);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencode');
        const data = toDate(data);
        xhr.send(data);
    } else {
        xhr.open(type, url + '?' + toData(data), async);
        xhr.send();
    }
    xhr.onreadstatechange = function () {
        if (xhr.readstate === 4 && xhr.status === 200) {
            if (success) {
                success(JSON.parse(xhr.responseText));
            }
        } else {
            if (error) {
                error(xhr.status);
            }
        }
    }
}

function toData(obj={}){
    const res = [];
    for (const i in obj) {
        let str = i + '=' + obj[i];
        res.push(str);
    }
    return res.join('&');
}

扁平化数组

function flat(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flat(arr[i]));
        } else {
            res = res.concat(arr[i]);
        }
    }
    return res;
}
const a = [1, [2, [3, 4]]];
console.log(flat(a));
// [ 1, 2, 3, 4 ]

克隆


function clone(obj, deep) {
    if (Array.isArray(obj)) {
        if (deep) {
            var newArr = [];
            for (var i = 0; i < obj.length; i++) {
                newArr.push(clone(obj[i], deep));
            }
            return newArr;
        } else {
            return obj.slice(); //复制数组
        }

    } else if (typeof obj === "object") {
        var newObj = {};
        for (var prop in obj) {
            if (deep) {
                newObj[prop] = clone(obj[prop], deep);
            } else {
                newObj[prop] = obj[prop];
            }

        }
        return newObj;
    } else {
        //原始类型
        return obj;
    }

}

new操作符具体做了什么

  1. 创建一个新的空对象
  2. 空对象的隐式原型(proto)指向函数的原型
  3. 改变函数上下文到新对象(改变this的指向)
  4. 对构造函数返回值有处理(返回原始类型就忽略返回空对象,返回引用类型则返回该引用类型)

根据上面四点,我们一起来验证下现有new的工作原理是不是这样。

1. 创建一个新的空对象

function Foo(){

}

console.log(new Foo()); // Foo {}

2. 空对象的隐式原型(proto)指向函数的原型

function Foo(){

}

console.log(new Foo().__proto__ === Foo.prototype); // true

3. 改变函数上下文到新对象(改变this的指向)

function Foo(){
    console.log(this);
    this.name = 'test';
}

Foo(); // 打印全局对象

new Foo(); // 打印 Foo {}

4. 对构造函数返回值有处理

返回原始类型

function Foo(){
    return '111';
}


console.log(new Foo()); // Foo {}

返回引用类型

function Foo(){
    return [1,2];
}
console.log(new Foo()); // [ 1, 2 ]
function myNew(fn, ...args) {
    // 1. 创建一个新的空对象
    const obj = {};
    // 2. 将对象的隐式原型指向函数原型
    obj.__proto__ = fn.prototype; // Object.setPrototypeOf(obj, fn.prototype);
    // 3. 将对象作为函数的上下文
    const result = fn.apply(obj, args);
    // 4. 判断函数返回值的类型
    return result instanceof Object ? result : obj;
}

function Foo(name, age) {
    this.name = name;
    this.age = age;
}

console.log(myNew(Foo, 'Mynew', 18));// Foo { name: 'Mynew', age: 18 }