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

  • 相关阅读:
    使用springboot缓存
    Something wrong with EnCase v8 index search results
    Is it a full physical image???
    FB的新专利竟要监看使用者的脸
    How to image a CD/DVD ROM and generate hash value
    Mobile game forensics
    物理提取大绝招”Advanced ADB”???
    How to trace the Geolocation of network traffic
    App forensics
    专做手机取证的大厂被人给黑了
  • 原文地址:https://www.cnblogs.com/xesam/p/2292739.html
Copyright © 2011-2022 走看看