js进阶

js进阶修炼手册

Posted by franki on May 25, 2019

js-forward

手写一个call函数

es5实现

Function.prototype.call2 = function() {
    var args = arguments;
    var context = args[0] || window;
    context.fn = this;
    var newArr = [];
    for(var i=0; i<args.length; i++) {
        newArr.push(args[i]);
    }
    context.fn(newArr.slice(1).join(','));
    delete context.fn;
}

var test = function(a, b) {console.log(a, b);}

test.call2({}, 1, 2);
// 1, 2

es6实现

Function.prototype.call2 = function() {
    const args = [...arguments];
    const context = args.shift();
    context.fn = this;
    context.fn(...args);
    delete context.fn;
}

const test = function(a, b) {
    console.log(a, b);
}

test.call2({}, 1, 2);
// 1, 2

手写一个apply

es5实现

Function.prototype.apply2 = function() {
    var args = arguments;
    var context = args[0] || window;
    var param = agrs[1];
    context.fn = this;
    context.fn(param.join(','));
    delete context.fn;
}

const test = function(a, b) {
    console.log(a, b);
}

test.apply2({}, [1, 2]);
// 1, 2

es6实现

Function.prototype.apply2 = function() {
    var args = [...arguments];
    var context = args[0] || window;
    context.fn = this;
    var param = args[1];
    context.fn(param.join(','));
    delete context.fn;
}
const test = function(a, b) {
    console.log(a, b);
}

test.apply2({}, [1, 2]);
// 1, 2

手写一个bind

es5实现

Function.prototype.bind = function(context) {
    var fn = this;
    var argsParent = Array.prototype.slice.call(arguments, 1);
    return function() {
        fn.call(context, ...argsParent.concat(Array.prototype.slice.call(arguments)));
    }
}

var test1 = function(a, b) {
    console.log(a, b);
}
test1.bind({})(1, 2)
// 1 2

es6实现

Function.prototype.bind = function() {
    const fn = this;
    const argsParent = [...arguments];
    return function() {
        fn.call(...argsParent, ...arguments);
    }
}

const test1 = funciton(a, b) {console.log(a, b);}
test1.bind({})(1, 2);
// 1, 2

遍历dom树

function traversal(node) {
    if (node && node.nodeType === 1) {
        console.log(node.tagName);
    }
    var i = 0,
        childNodes = node.childNodes,
        item;

    for (; i<childNodes.length; i++) {
        item = childNodes[i];
        if (item.nodeType === 1) {
            traversal(item);
        }
    }
}

一次性插入1000个div

createDocumentFragment方法创建了一虚拟的节点对象,节点对象包含了所有属性和方法。 当你想要提取文档的一部分,改变、增加,或者删除某些内容及插入到文档末尾可以使用 createDocumentFrament方法

var oFrag = document.createDocumentFragment();
for (var i=0; i<1000; i++) {
    var op = document.createElement("div");
    var oText = document.createTextNode(i);
    op.appendChild(oText);
    oFrag.appendChild(op);
}
document.body.appendChild(oFrag);

简述new的实现过程

function new(func) {
    let target = {};
    target._proto_ = func.prototype;
    let res = func.call(target);
    if (typeof(res) === 'object' || typeof(res) === 'function') {
        reuturn res;
    }
    return target;
}

手写一个promise

大体思路: 1 定义一个构造函数Promise2,传递一个函数参数 2 构造函数里面定义一个resolve、reject方法,用于传递出去给外面函数参数调用 3 构造函数定义一个then方法用于处理结果,也可以返回promise2 4 构造函数定义catch、finally分别处理错误回调和失败回调

function Promise2(fn) {
    this.onCatchCb = null;
    this.onFinallyCb = null;
    this.promiseValue = null;
    this.onFulfilledCb = null;
    this.onRejectedCb = null;
    this.thenResultPromise = null;
    this.promiseStatus = 'pending'; // pending, fulfilled, rejected
    this.timer = null;
    var _this = this;
    var resolve = function(data) {
        _this.promiseValue = data;
        try {
            _this.onFulfilledCb &&
                (_this.thenResultPromise = _this.onFulfilledCb(data));
        } catch(e) {
            _this.onCatchCb && _this.onCatchCb(e);
        }
        _this.onFinallyCb && _this.onFinallyCb();
        _this.promiseStatus = 'fulfilled';
    };

    var reject = function(error) {
        _this.promiseValue = error;
        try {
            _this.onRejectedCb ? (_this.thenResultPromise = _this.onRejectedCb(error)) :
                _this.onCatchCb && _this.onCatchCb(error);
        } catch(e) {
            _this.onCatchCb && _this.onCatchCb(e);
        }
        _this.onFinallyCb && _this.onFinallyCb();
        _this.promiseStatus = 'rejected';
    };

    if (typeof fn === 'function') {
        fn(resolve, reject);
    } else {
        resolve();
    }
}

Promise2.prototype = {
    then: function(onFulfilled, onRejected) {
        var _this = this;
        onFulfilled && (_this.onFulfilledCb = onFulfilled);
        onRejected && (_this.onRejectedCb = onRejected);
        return new Promise2(function(resolve2, reject2) {
            _this.timer = setInterval(function() {
                try {
                    if (_this.promiseStatus !== 'pending' &&
                    _this.thenResultPromise.promiseStatus !== 'pending') {
                        if (_this.thenResultPromise.promiseStatus === 'fulfilled') {
                            resolve2(_this.thenResultPromise.promiseValue);
                        } else if (_this.thenResultPromise.promiseStatus === 'rejected') {
                            reject2(_this.thenResultPromise.promiseValue);
                        }
                        _this.timer && clearInterval(_this.timer);
                    }
                } catch(e) {
                    reject2(e);
                    _this.timer && clearInterval(_this.timer);
                }
            }, 10);
        });
    },
    catch: function(onCatch) {
        this.onCatchCb = onCatch;
        return this;
    },
    finally: function(onFinally) {
        this.onFinallyCb = onFinally;
    }
};

// test
var myPromise = new Promise2(function(resolve, reject) {
    console.log('-----流程开始-----');
    setTimeout(function() {
        resolve('promise初始化resolve成功');
    }, 500);
}).then(function(successMsg) {
    return new Promise2(function(resolve, reject) {
        setTimeout(function() {
            console.log('promise then 1 收到 resolveMessage: ' + successMsg);
            reject('from then 1');
        }, 3000);
    });
}).then(function(successMsg) {
    return new Promise2(function(resolve, reject) {
        setTimeout(function() {
            console.log('myPromise then 2 收到 resolveMessage:'+successMsg);
            resolve('from then 2');
        }, 2000);
    });
}, function(failMsg) {
    return new Promise2(function(resolve, reject) {
        setTimeout(function() {
            console.log('myPromise then 2 收到 rejectMessage:'+failMsg);
            resolve('from then 2');
        }, 1000);
    });
}).then(function(successMessage) {
    return new Promise2(function(resolve, reject) {
        setTimeout(function() {
        console.log(
            'myPromise then 3 收到 resolveMessage:' + successMessage
        )
        reject('from then 3')
        }, 1000)
    });
}).catch(function(error) {
    console.log('caught rejected data:');
    console.log(error);
}).finally(function() {
    console.log('-----------全部处理完毕!-----------');
});


// 运行过程 promise

深拷贝

shallow clone 与 deep clone 前者是把一个对象的每个值赋值到一个新对象,新对象与源对象是共用一个内存地址,故改变新对象的下的值,源对象的对应的值也会发生改变。 后者是通过递归解决clone问题,这种方式让新对象与源对象摆脱依赖,缺点是拷贝的速度更慢,代价更大。

function deepClone(source) {
    if (!source) {
        return source;
    }
    let target = {};
    for (const key in source) {
        if (typeof source[key] === 'object') {
            target[key] = deepClone(source[key]);
        } else {
            target[key] = source[key];
        }
    }
    return target;
}

总结

任何事物,透过现象看本质,做到知其然更知所以然,放到javascript中也是一样的。原理性的东西虽然不会让你代码看上去简洁,但是反过来想想,如果你是设计api的人,只提供一个接口文档,别人直接调用,简单方便,但是这个过程你已经帮别人做了大部分的事,总结来说,原理性的东西对于一个人提高代码组织、架构设计能力是一个必备过程。目前自己对这方面也是有很大的欠缺,所以勤能补拙、查漏补缺,尽量提高自己在这方面的能力。谨记:万丈高楼平地起,坚实的地基才是重中之重,大厦华丽的外表只不过是点缀罢了。