1. call 和 apply 的区别是什么,哪个性能更好一些 #

Obj.prototype.target.apply(this, ["YES"]);
Obj.prototype.target.call(this, "YES");

MDN call

Function.prototype.call = function (obj, ...arg) {
    const context = obj;
    const fn = Symbol();
    context[fn] = this;
    const ret = context[fn](...arg);
    delete context[fn];
    return ret;
}

MDN apply

Function.prototype.apply = function(obj, arg) {
      const context = obj;
      const fn = Symbol();
      context[fn] = this;
      const ret = context[fn](...arg);
      delete context[fn]
      return ret;
}
function fn1 () {
    console.log(1);
}
function fn2 () {
    console.log(2);
}
fn1.call.call(fn2);

2. ES6 代码转成 ES5 代码的实现思路是什么? #

3.数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少 #

4.XHR具体底层原理和API? #

5.prototype根本上解决的是什么问题? #

6. 编写parse函数,实现访问对象里属性的值 #

let obj = { a: 1, b: { c: 2 }, d: [1, 2, 3], e: [{ f: [4, 5, 6] }] };
let r1 = parse(obj, 'a');// = 1;
let r2 = parse(obj, 'b.c');// = 2;
let r3 = parse(obj, 'd[2]');// = 3;
let r4 = parse(obj, 'e[0].f[0]');// = 4;

function parse(obj, str) {
    return new Function('obj', 'return obj.' + str.replace(/\.(\d+)/g, '\[$1\]'))(obj);
}
function parse(obj, str) {
    str = str.replace(/\[(\d+)\]/g, '.$1');
    arr = str.split('.');
    arr.forEach(function (item) {
        obj = obj[item];
    })
    return obj;
}

console.log(r1, r2, r3, r4);

7.数组扁平化flat方法的多种实现? #

let arr = [
    [1],
    [2, 3],
    [4, 5, 6, [7, 8, [9, 10, [11]]]],
    12
];

flat
// let flattedArr = arr.flat(Infinity);
// console.log(flattedArr);


//toString
console.log(arr.toString().split(',').map(item => Number(item)));

//stringify
console.log(JSON.stringify(arr).replace(/\[|\]/g, '').split(',').map(item => Number(item)));

//while
while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr);
}
console.log(arr);

//prototype
Array.prototype.flat = function () {
    let result = [];
    let _this = this;
    function _flat(arr) {
        for (let i = 0; i < arr.length; i++) {
            let item = arr[i];
            if (Array.isArray(item)) {
                _flat(item);
            } else {
                result.push(item);
            }
        }
    }
    _flat(_this);
    return result;
}
console.log(arr.flat());

8.实现一个不可变对象 #

8.1 不可扩展 #

var obj = Object.preventExtensions({
    name: 'zhufeng'
});
var obj = { name: 'zhufeng' };
console.log(Object.isExtensible(obj));// true
Object.preventExtensions(obj);
console.log(Object.isExtensible(obj)); // false

//Object.defineProperty(obj, 'age', {value: 10});
//TypeError: Cannot define property age, object is not extensible
obj.age = 10;
console.log(obj.age); // undefined

8.2 密封 #


var obj = new Object();
Object.isExtensible(obj); // true
Object.isSealed(obj); // false
Object.seal(obj);
Object.isExtensible(obj); // false,注意 seal 后对象的 isExtensible() 也随之改变
Object.isSealed(obj); // true

var obj = { name: 'zhufeng' };
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
/**
{
  value: 'zhufeng',
  writable: true,
  enumerable: true,
  configurable: true
}
*/
Object.seal(obj);
console.log(Object.getOwnPropertyDescriptor(obj, 'name')); // seal 后 configurable 变为 false
/**
{
  value: 'zhufeng',
  writable: true,
  enumerable: true,
  configurable: false
}
*/

8.3 冻结 #


var obj = new Object();
Object.isExtensible(obj); // true
Object.isSealed(obj); // false
Object.isFrozen(obj); // false
Object.freeze(obj);
Object.isExtensible(obj); // false,注意 freeze 后对象的 isExtensible() 也随之改变
Object.isSealed(obj); // true,注意 freeze 后对象的 isSealed() 也随之改变
Object.isFrozen(obj); // true
var obj = Object.freeze({ name: 'zhufeng' });

// 直接定义新的属性会报错
Object.defineProperty(obj, 'name', {
    value: 'zhufeng'
});

obj.name = 'zhufeng';
obj.name; // undefined

delete obj.name; // 删除失败,返回 false

obj.name = 'jiagou';
obj.name; // 仍然是 "zhufeng"

9.给定一组url,利用js的异步实现并发请求,并按顺序输出结果 #

function printOrder(urlArr) {
    Promise.all(urlArr.map(url => new Promise(function (resolve, reject) {
        let xhr = new XMLHttpRequest;
        xhr.open('GET', url, true);
        xhr.responseType = 'json';
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                resolve(xhr.response);
            }
        }
        xhr.send();
    }))).then(result => {
        console.log(result);
    });
}
printOrder(['/1.json?ts=' + Date.now(), '/2.json?ts=' + Date.now()]);
function printOrder(urlArr, callback) {
    let result = {};
    function sendRequest(url, index) {
        let xhr = new XMLHttpRequest;
        xhr.open('GET', url, true);
        xhr.responseType = 'json';
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                result[index] = xhr.response;
                if (Object.keys(result).length == urlArr.length) {
                    result.length = Object.keys(result).length;
                    callback(null, Array.from(result));
                }
            }
        }
        xhr.send();
    }
    urlArr.forEach(function (url, index) {
        sendRequest(url, index);
    });
}

printOrder(['/1.json?ts=' + Date.now(), '/2.json?ts=' + Date.now()], (err, result) => {
    console.log(result);
});

10.说下防抖和节流,能实现么? #

11.说下 Reflect Proxy #

let target = {
    name: 'zhufeng',
    age: 10
}
let handler = {
    get: function (target, key) {
        return target[key];
    },
    set: function (target, key, value) {
        target[key] = value;
    }
}
let proxy = new Proxy(target, handler)
console.log(proxy.name);
proxy.age = 25;
console.log(proxy.age);
let target = {
    name: 'zhufeng',
    age: 10
}
//Object.defineProperty();
Reflect.defineProperty(target, 'home', {
    value: '北京'
})
console.log(target.home);
// 'home' in target
console.log(Reflect.has(target, 'home'));

12.写一个函数,可以控制最大并发数 #

let Semaphore = require('semaphore');
let semaphore = new Semaphore(2);
console.time('cost');
semaphore.take(function () {
    setTimeout(() => {
        console.log(1);
        semaphore.leave();
    }, 1000);
});
semaphore.take(function () {
    setTimeout(() => {
        console.log(1);
        semaphore.leave();
    }, 2000);
});
semaphore.take(function () {
    console.log(3);
    semaphore.leave();
    console.timeEnd('cost');
});
class Semaphore {
    constructor(available) {
        this.available = available;
        this.waiters = [];
        this._continue = this._continue.bind(this);
    }

    take(callback) {
        if (this.available > 0) {
            this.available--;
            callback();
        } else {
            this.waiters.push(callback);
        }
    }

    leave() {
        this.available++;
        if (this.waiters.length > 0) {
            process.nextTick(this._continue);
        }
    }

    _continue() {
        if (this.available > 0) {
            if (this.waiters.length > 0) {
                this.available--;
                const callback = this.waiters.pop();
                callback();
            }
        }
    }
}

13.说下 js模块化(commonjs/AMD/CMD/ES6) #

14. promise、async await、Generator的区别 #

15. 如何让 (a == 1 && a == 2 && a == 3) 的值为true? #

15.1 valueOf和toString #

var obj = {a:1};
//在隐式转换的过程中,调用了`toString`方法
console.log(obj == "[object Object]");//true

15.2 类型转换 #

15.2.1 对象转原始类型步骤 #

let a = new Proxy({}, {
    i: 1,
    get() {
        return () => this.i++;
    }
});
if (a == 1 && a == 2 && a == 3) {
    console.log('成功');
}
var a = {
    count:1,
    toString(){
        return this.count++;
    },
    /* valueOf(){
        return this.count++;
    } */
};
if(a == 1 && a ==2 && a == 3){
  console.log('成功');
}
Object.defineProperty(window,'a',{
    get(){
        return i++;
    }
})
if(a == 1 && a ==2 && a ==3 ){
    console.log('相等');
}
var a = [1,2,3];
a.toString = a.shift;
//a.join = a.shift;
if(a == 1 && a ==2 && a ==3 ){
    console.log('相等');
}

16. console.log([]==![]);//true #

17. +号 #

[]+{}  //"[object Object]"
{}+[]  //0
{}+0   //0
[]+0   //"0"

18. 说一下arrayBuffer 和 Buffer 的区别 以及应用场景 #

18.1 1.server.js #

node\1.server.js

let http = require('http');
let fs = require('fs');
let path = require('path');
http.createServer((req, res) => {
    if (req.url === '/') {
        let content = fs.readFileSync(path.join(__dirname, 'index.html'));
        res.setHeader('Content-Type', 'text/html');
        res.end(content);
    } else if (req.url === '/data') {
        let buffer1 = Buffer.from('abc');// 97 98 99
        let buffer2 = Buffer.from('def');// 97 98 99
        let buffer = Buffer.concat([buffer1, buffer2]);
        res.end(buffer);
    } else {
        res.end('');
    }
}).listen(8000);;

18.2 index.html #

 <script>
        var xhr = new XMLHttpRequest();
        xhr.open('GET', '/data', true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function (e) {
            buffer = xhr.response;
            //console.log(buffer);
            //单一类型数据可用对应的类型化数组直接进行解析
            /* var array = new Uint8Array(buffer);
            for (var i = 0; i < array.length; ++i) {
                console.log(array[i]);
            } */
            /* var array = new Uint8Array(buffer, 3, 3);
            for (var i = 0; i < array.length; ++i) {
                console.log(array[i]);
            } */
            var dataView = new DataView(buffer);
            console.log(dataView.getInt8(0));
            console.log(dataView.getInt8(1));
            console.log(dataView.getInt8(2));
        };
        xhr.send();
    </script>
console.log(97..toString(2));//01100001
console.log(98..toString(2));//01100010
console.log(98..toString(2) + 97..toString(2));
//parseInt就是把一个字符串,转成十进制
console.log(parseInt('0110001001100001', 2));

//两个字节组成一个数,10000000   00000001
//高位在前和高位两各种方式  大头在前, 小头在前

let buffer = Buffer.from('ab');
//61=>97  6*16+1=97
console.log(buffer);

console.log(parseInt('0X61', 16));

19. 柯理化 #

19.1 bind #

~function (prototype) {
    function bind(context = global, ...outerArgs) {
        return (...innerArgs) => {
            return this.call(context, ...outerArgs, ...innerArgs);
        }
    }
    prototype.bind = bind;
}(Function.prototype);

function sum(...args) {
    return this.prefix + args.reduce((acc, curr) => acc + curr, 0);
}
let obj = { prefix: '$' };
let bindSum = sum.bind(obj, 1, 2, 3);
console.log(bindSum(4, 5));
~(function () {
    Object.create = function (proto) {
        function F() { }
        F.prototype = proto;
        return new F();
    };

    Function.prototype.bind = function (oThis, ...outerArgs) {
        var thatFunc = this,
            fBound = function (...innerArgs) {
                return thatFunc.apply(
                    this instanceof thatFunc ? this : oThis, [...outerArgs, ...innerArgs])
            };
        fBound.prototype = Object.create(thatFunc.prototype);
        return fBound;
    }
})();

function Point(x, y) {
    this.x = x;
    this.y = y;
}

Point.prototype.toString = function () {
    return this.x + ',' + this.y;
};
var emptyObj = {};
var YAxisPoint = Point.bind(null, 1/*x*/);
var axisPoint = new YAxisPoint(2);
console.log(axisPoint.toString()); // '1,2'

console.log(axisPoint instanceof Point); // true
console.log(axisPoint instanceof YAxisPoint); // true

19.2 add #

console.log(add(1, 2, 3, 4, 5));//15
console.log(add(1)(2, 3)(4, 5));//15
console.log(add(1)(2)(3)(4)(5));//15
const add = (function (length) {
    let allArgs = [];
    function _add(...args) {
        allArgs = [...allArgs, ...args];
        if (allArgs.length >= length) {
            let sum = allArgs.reduce((acc, curr) => acc + curr, 0);
            allArgs.length = 0;
            return sum;
        } else {
            return _add;
        }
    }
    return _add;
})(5);
alert(add(1, 2, 3, 4, 5));//15
alert(add(1)(2, 3)(4));//15
alert(add(1)(2)(3));//15
function add(...args) {
    var _add = add.bind(null, ...args);
    _add.toString = function () {
        return args.reduce((sum, item) => sum + item, 0);
    }
    return _add;
}
function curry(fn, ...args) {
    return args.length < fn.length ? (...extraArgs) => curry(fn, ...args, ...extraArgs) : fn(...args)
}
function addFn(a, b, c, d, e) {
    return a + b + c + d + e;
}
let add = curry(addFn);
console.log(add(1, 2, 3, 4, 5));//15
console.log(add(1)(2, 3)(4, 5));//15
console.log(add(1)(2)(3)(4)(5));//15

20. 拷贝 #

20.1 JSON.parse #

let obj = { name: 'zhufeng', age: 10 };
console.log(JSON.parse(JSON.stringify(obj)));

20.2 浅拷贝 #

function clone(source) {
    let target = {};
    for (const key in source) {
        target[key] = source[key];
    }
    return target;
};

20.3 深拷贝 #

let obj = {
    name: 'zhufeng',
    age: 10,
    home: { name: '北京' },
    hobbies: ['抽烟', '喝酒', '烫头']
};
function clone(source) {
    if (typeof source === 'object') {
        let target = Array.isArray(source) ? [] : {};
        for (const key in source) {
            target[key] = clone(source[key]);
        }
        return target;
    }
    return source;

};
let cloned = clone(obj);
console.log(Array.isArray(cloned.hobbies));

20.4 循环引用 #

let obj = {
    name: 'zhufeng',
    age: 10,
    home: { name: '北京' },
    hobbies: ['抽烟', '喝酒', '烫头']
};
obj.obj = obj;
function clone(source, map = new Map()) {
    if (typeof source === 'object') {
        if (map.get(source)) {
            return map.get(source);
        }
        let target = Array.isArray(source) ? [] : {};
        map.set(source, target);
        for (const key in source) {
            target[key] = clone(source[key], map);
        }
        return target;
    }
    return source;

};

let cloned = clone(obj);
console.log(cloned.obj);

20.5 while #

let obj = {
    name: 'zhufeng',
    age: 10,
    home: { name: '北京' },
    hobbies: ['抽烟', '喝酒', '烫头']
};
obj.obj = obj;
function clone(source, map = new Map()) {
    if (typeof source === 'object') {
        if (map.get(source)) {
            return map.get(source);
        }
        let target = Array.isArray(source) ? [] : {};
        map.set(source, target);
        let keys = Object.keys(source);
        let length = keys.length;
        let index = 0;
        while (index < length) {
            target[keys[index]] = clone(source[keys[index]], map);
            index++;
        }
        return target;
    }
    return source;
};
function getType(source) {
    return Object.prototype.toString.call(source);
}

20.6 精确类型 #

20.6.1 判断类型方式 #

20.6.2 判断类型 #

let obj = {
    married: true,
    age: 10,
    name: 'zhufeng',
    girlfriend: null,
    boyfriend: undefined,
    flag: Symbol('man'),
    home: { name: '北京' },
    set: new Set(),
    map: new Map(),
    getName: function () { },
    hobbies: ['抽烟', '喝酒', '烫头'],
    error: new Error('error'),
    pattern: /^regexp$/ig,
    math: Math,
    json: JSON,
    document: document,
    window: window
};
obj.set.add(1);
obj.map.set('name', 'value');
obj.obj = obj;

let OBJECT_TYPES = [{}, [], new Map(), new Set(), new Error(), new Date(), /^$/].map(item => getType(item));
const MAP_TYPE = getType(new Map());
const SET_TYPE = getType(new Set());
const CONSTRUCT_TYPE = [new Error(), new Date()].map(item => getType(item));
const SYMBOL_TYPE = getType(Symbol('1'));
const REGEXP_TYPE = getType(/^$/);
function clone(source, map = new Map()) {
    let type = getType(source);
    if (!OBJECT_TYPES.includes(type)) {//基本数据类型
        return source;
    }
    if (map.get(source)) {
        return map.get(source);
    }
    if (CONSTRUCT_TYPE.includes(type)) {
        return new source.constructor(source);
    }
    let target = new source.constructor();
    map.set(source, target);

    if (SYMBOL_TYPE === type) {
        return Object(Symbol.prototype.valueOf.call(source));
    }
    if (REGEXP_TYPE === type) {
        const flags = /\w*$/;
        const target = new source.constructor(source.source, flags.exec(source));
        target.lastIndex = source.lastIndex;
        return target;
    }
    if (SET_TYPE === type) {
        source.forEach(value => {
            target.add(clone(value, map));
        });
        return target;
    }
    if (MAP_TYPE === type) {
        source.forEach((value, key) => {
            target.set(key, clone(value, map));
        });
        return target;
    }
    let keys = Object.keys(source);
    let length = keys.length;
    let index = 0;
    while (index < length) {
        target[keys[index]] = clone(source[keys[index]], map);
        index++;
    }
    return target;
};

function getType(source) {
    return Object.prototype.toString.call(source);
}
let cloned = clone(obj);
console.log(cloned);
console.log(obj.home === cloned.home);
console.log(obj.set === cloned.set);
console.log(obj.map === cloned.map);
/*
[object Boolean]
[object Number]
[object String]
[object Null]
[object Undefined]
[object Symbol]
[object Object]
[object Function]
[object Array]
[object Error]
[object RegExp]
[object Math]
[object JSON]
[object HTMLDocument]
[object Window]"
*/

庄同学提供

let obj = {
    married: true,
    age: 10,
    name: 'zhufeng',
    girlfriend: null,
    boyfriend: undefined,
    flag: Symbol('man'),
    home: { name: '北京' },
    set: new Set(),
    map: new Map(),
    getName: function () { },
    hobbies: ['抽烟', '喝酒', '烫头'],
    error: new Error('error'),
    pattern: /^regexp$/ig,
    date: new Date()
    // math: Math,
    // json: JSON,
    // document: document,
    // window: window
};
obj.set.add(1);
obj.map.set('name', 'value');
obj.obj = obj;


const getType = (o) => Object.prototype.toString.call(o);
const arrayTag = '[object Array]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const mapTag = '[object Map]'
const objectTag = '[object Object]'
const regexpTag = '[object RegExp]'
const setTag = '[object Set]'
const symbolTag = '[object Symbol]'
const objectTags = [arrayTag, objectTag, regexpTag, symbolTag, setTag, mapTag, errorTag, dateTag];
const instanceTags = [regexpTag, errorTag, dateTag];
function clone(source, map = new Map()) {
    let target;
    const sourceType = getType(source);
    if (!objectTags.includes(sourceType)) return source;
    if (map.get(source)) return map.get(source);
    const constructor = source.constructor;
    if (instanceTags.includes(sourceType)) return new constructor(source);
    if (sourceType === symbolTag) {
        return Object(Symbol.prototype.valueOf.call(source));
    }
    target = new constructor();
    map.set(source, target);
    if (sourceType === mapTag) {
        source.forEach((value, key) => target.set(key, clone(value, map)));
        return target;
    }
    if (sourceType === setTag) {
        source.forEach((value) => target.add(clone(value, map)));
        return target;
    }
    for (const key in source) {
        target[key] = clone(source[key], map);
    }
    return target;
}
const obj2 = clone(obj);

console.log('obj', obj);
console.log('obj2', obj2);