zoukankan      html  css  js  c++  java
  • Prototype源码浅析——Function.prototype部分(二)

    接着上一部分:Prototype源码浅析——Function.prototype部分(一)

    粗略回顾,前面的

    一个例子,两条道路:

    function handler(greet){
    console.log(greet,this.name);
    }
    var obj = {
    name : 'xesam'
    }

    第一、用call或者apply来改变handler的作用域,handler.call(obj,'hello'),这种情况下需要另一个函数来包装一下。

    第二、将handler变成obj的一个方法,让其能这么调用obj.handler()

    第一部分说的是第一条道路,现在就说第二条道路。

    既然要将handler变成obj的一个方法,当然直接抢是不可能的,obj.handler的handler只是我定义的一个同名方法(要定义成其他的如xx都可以)而已,跟例子定义的function handler还是有本质区别的。

    我们暂且把这种操作叫做“方法化”(非官方),取个名称比如methodize(Prototype里面的真实方法名)。

    参考bind的说明,具体实现应该是这样的:

        Function.prototype.methodize = function(){
    var _this = handler;//在例子中代表handler
    return function(){
    var caller = obj;//在例子中代表obj
    //arguments实际参数
    var args = [caller].concat(Array.prototype.slice.call(arguments));
    _this.apply(obj,args);
    }
    }

    换成通用的形式后调用就是:

        function handler(){
    console.log(this.name);
    }
    var obj = {
    name : 'xesam'
    }

    Function.prototype.methodize = function(){
    var _this = this;//在例子中代表handler
    return function(){
    var caller = this;//在例子中代表obj
    //arguments实际参数
    var args = [this].concat(Array.prototype.slice.call(arguments));
    _this.apply(this,args);
    }
    }
    obj.handler = handler.methodize();
    obj.handler();


    不过真生的Prototype中的实现方式有一点点的细微区别:

      function methodize() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
    var a = update([this], arguments);
    return __method.apply(null, a);//这里apply的第一个参数并不是this,而是null
    };
    }

    因此原版中更明显的调用的方式是:

        function handler_1(obj){ //这样之后和hangdler_1(obj_1)的效果一样了,不过obj_1.handler_()显得更面向对象而已,也用在了面向对象上面
    console.log(obj.name);
    }
    var obj_1 = {
    name : 'xesam'
    }
    obj_1.handler = handler_1.methodize();
    obj_1.handler();

    其实如果理解了bind,这一个操作也好理解,整个Function的扩展这一部分,就是利用匿名函数和作用域的变换来实现的,灵活的同时也不怎么好理解。

    把一个简单的事情复杂化,这样做的主要意图单看Function这一部分根本是匪夷所思,后面的其他部分会再提到的。

    另一个容易让人迷糊的方法wrap,号称wrap体现了面向方面的程序设计的本质。通俗来说就是将方法本身作为新方法的第一个参数来包装,主要是可以调用包装前和包装好后的方法,后面在Class部分,可以让你调用父类或者子类的方法。

    具体代码和bind差不多,所以我直接给出来了:

        Function.prototype.wrap =function(wrapper){
    var _this = this;
    return function(){
    var params = [_this.bind(this)].concat(Array.prototype.slice.call(arguments, 0));
    return wrapper.apply(this, params);
    }
    }

    我们还是用实例说话:

        Function.prototype.bind = function(context) {
    var __method = this, args = Array.prototype.slice.call(arguments, 1);
    return function() {
    var b = args.concat(Array.prototype.slice.call(arguments, 0));
    return __method.apply(context, b);
    }
    }
    Function.prototype.wrap =function(wrapper){
    var _this = this;
    return function(){
    var params = [_this.bind(this)].concat(Array.prototype.slice.call(arguments, 0));
    wrapper.apply(this, params);
    }
    }
    function handler(){
    console.log('自定义的handler',this.name);
    }
    var obj = {
    name:'小西山子'
    }
    obj.handler = handler.wrap(function(handlerBinded,content){ //这里可以用上面说的方法化的,不过避免麻烦,我直接用脑对象来处理
    if(content){
    console.log(this.name); //this的指向
    console.log(content);
    }else{
    console.log(this.name); //this的指向
    handlerBinded(); //this的指向
    }
    });
    obj.handler();
    obj.handler('test');

    具体的还是到Class里面再说吧。

    其他的方法里面包括两个关于延时的:delay和defer

    说到延时方法有二:

    第一:循环XXXXX次,然后再执行,就像sleep()的效果一样,或者死循环,就像die()的效果一样

    第二:setTimeout和setInterval。

    第一种方法主要是用来模拟sleep()做个测试什么的,不考虑

    第二种方法如果只要延时执行,setTimeout就可以了。

    还是一个例子:

    function handler(){
    console.log('执行');
    }

    想让handler延时1s执行,不多想:

    setTimeout(function(){
    handler();
    },1000)

    如果想要方法的形式,也不多想,像bind一样把参数什么的往里面丢就可以了:

    Function.prototype.delay = function(timeout){
    var _this = this;
    var _params = Array.prototype.slice.call(arguments,1);
    var timer = setTimeout(function(){
    _this.call(_this,_params);
    },timeout);
    return timer;
    }
    handler.delay(1000);

    至于defer,就是对delay的一个小包装而已。

    delay中的timeout是可选的,defer直接给timeout赋值0.01(有点要说明的是我的实现里面timeout单位是ms,Prototype把timeout单位是s);

    所以

    handler.defer()  -->handler.delay(10);//注意单位,这里用的是我实现的单位。

    这两个延时函数唯一值得指出的就是defer(10)中的那个10,延迟10ms然后执行(其实际效果就是js解释器一有空闲就立马执行,起到一个防止阻塞当前代码的作用)。

    平时见得多的可能是这种形式:

        setTimeout(function(){

    },0)



    关于js定时器的机制可以看些别的文章:

    【转载】关于setTimeout,理解JavaScript定时机制

    关于setTimeout的第三个参数以及小应用

    最后一个方法就是获取方法形参:argumentNames。

        function handler(x,y){

    }
    handler.argumentNames();//['x','y']

    说这个函数只需要理解handler.toString()就行,handler.toString()会返回handler的字符串形式(还包括注释哦)。

        function handler(x,//x_1
    y/* y_1 */){
    }
    console.log(handler.toString());
    打印结果:
    'function handler(x,//x_1
    y/* y_1 */){
    }'

    因此我们只需要用正则替换注释内容以及空白就行。

        Function.prototype.argumentNames = function(){
    var tmp = this.toString().match(/^[\s\()]*function[^(]*\(([^)]*)\)/)[1];//获取参数部分
    var tmp_1 = tmp.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g,''); //剔除注释
    var tmp_2 = tmp_1.replace(/\s+/g,''); //剔除空白
    return tmp_2.split(',')
    }

    Prototype里面还有一点判断的部分,看看就行了:

        Function.prototype.argumentNames = function(){
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
    .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
    .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;//这里是判断参数是否空
    }

    到此,函数部分处理update和merge两个没有提到之外,所有的方法都在这里了。

    有什么错误之处,还请指出,谢谢

    转载请注明来自小西山子【http://www.cnblogs.com/xesam/
    本文地址:http://www.cnblogs.com/xesam/archive/2011/12/18/2292739.html

  • 相关阅读:
    ECharts之柱状图 饼状图 折线图
    Vue自定义指令(directive)
    HDU 1231 最大连续子序列
    POJ 2533 Longest Ordered Subsequence
    HDU 1163 Eddy's digital Roots
    HDU 2317 Nasty Hacks
    HDU 2571 命运
    HDU 4224 Enumeration?
    HDU 1257 最少拦截系统
    HDU 2740 Root of the Problem
  • 原文地址:https://www.cnblogs.com/xesam/p/2292739.html
Copyright © 2011-2022 走看看