0

私の質問は...TestObjのCallMeLaterTestObj関数では、「this」はウィンドウオブジェクトであり、TestObjではありません。これを再構築して、CallMeLater関数内で、function() { v.CallMeLaterTestObj(); }新しいブラウザーへのサポートが制限されているため、呼び出しをクロージャーでラップしたり、バインド関数を使用したりする必要がないようにするにはどうすればよいですか。2つの目的:

  • オブジェクト内の関数呼び出しで「this」を保持する
  • 同じ値を共有しないように、個別のオブジェクトごとに「値」の個別の値を維持します。

    // public api、privateメソッド、private変数、publicフィールドをエミュレートします。

    //質問の新しい部分

  • バインディング関数とプロトタイプ表記を含むように書き直されました。すべての新しいオブジェクトが取得するベースオブジェクトにBinding関数をどのように移動しますか?

  • これは、両方の長所を活用するために私ができる限り近いものです。このアプローチの落とし穴が何であるか私にはわかりませんが

        var BaseObject = function ()
        {
            _getBinding = function (method)
            {
                var _self = this;
                return function ()
                {
                    _self[method].apply(_self, arguments);
                };
            };
            return {
                CallInline: _getBinding
            }
        }();
    
    
        var TestObj = function (value)
            {
                $.extend(this, BaseObject);
                // public var
                this._value = value;
            };
    
        TestObj.prototype = function()
        {
            var privateVar = false;
            // these are private
            _giveMe = function () {
                return this._value;
            },
            _callMeLaterTestObj = function () {
                console.log('I am ' + this.constructor.name + ' my value is ' + this._value);
            };
    
            // public API
            return {
                GiveMe : _giveMe,
                CallMeLaterTestObj : _callMeLaterTestObj
            }
    
        }();
    
        function CallMeLater(v, i)
        {
            setTimeout(v.CallInline('CallMeLaterTestObj'), 10);
        }
    
    
    
        var V1 = new TestObj(1);
        var V2 = new TestObj(2);
        var V3 = new TestObj(3);
    
    
        console.log('V1= ' + V1.GiveMe());
        console.log('V2= ' + V2.GiveMe());
        console.log('V3= ' + V3.GiveMe());
        console.log('---');
    
        V1.CallMeLaterTestObj();
    
        console.log('---');
    
4

2 に答える 2

1

いいえ、できません。それがまさにその方法です。ところで、古いブラウザでも利用できるように、メソッドを簡単にシムすることができます。bind

別の方法は、実際の関数を常にバインドする必要があることがわかっている場合は、クロージャーをプロトタイプメソッドに移動することです。

TestObj.prototype.getCallMeLaterTestObj = function () {
    var that = this;
    return function() {
        console.log('I am ' + that.constructor.name + ' my value is ' + that._value);
    };
};
setTimeout(v.getCallMeLaterTestObj(), 10);

ところで、プロトタイプにはconstructorプロパティがないため、ログは期待どおりに機能しません。

thisあなたの唯一のチャンスは、キーワードを完全に避けることです:

TestObj = function() {
    var privateVar = false; // these are private static
    function TestObj(value) {
        function giveMe() {
            return value;
        }
        function callMeLaterTestObj() {
            console.log('I am TestObj my value is ' + giveMe());
        }
        this._value = value;
        this.giveMe = giveMe;
        this.callMeLaterTestObj = callMeLaterTestObj;
        /* you could do this as well:
        return {
           _value: value,
           giveMe: giveMe,
           callMeLaterTestObj: callMeLaterTestObj
        }; */
    }
    return TestObj;
})();
var v = new TestObj;
setTimeout(v.callMeLater, 10);

ただし、これはプロトタイプの継承をまったく使用しないため、メモリ効率はあまり高くありません。

于 2012-11-29T16:16:58.200 に答える
1

私はあなたが探しているのはこれだと思います:

function TestObj(value) {
    var _value = value;

    this.giveMe = function() {
        return _value;
    };

    this.callMeLaterTestObj = function() {
        console.log('I am ' + this.constructor.name + ' my value is ' + _value);
    };

    return this;
};

function callMeLater(v, i) {
    setTimeout(function() {
        v.callMeLaterTestObj();
    }, 10);    
}

var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);

console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');

callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);​

function name()Constructor.nameにアクセスするには、構文ではなく構文を使用して関数を宣言する必要がありvar name = function()ます。

プライベート変数を保持し、パブリックAPIを公開するにthisは、関数内のプロパティとしてパブリック変数を公開します。

this必ずコンストラクター関数から戻って動作させてください。

また、クラス名(TestObjが1つ)の場合はCamelCaseの命名規則に従い、変数/メソッド/オブジェクトなどの場合はlowerCamelCaseの命名規則に従うことをお勧めします。どの変数がインスタンスで、どの変数がクラスであるかを明確に保つのに役立ちます。

ここで期待されるコンソール出力をテストして確認します

ノート

setTimeoutのクロージャーでのラップに関してv.callMeLaterTestObj()、この手法は完全にクロスブラウザー互換です。問題はありません。

このbind方法は新しいものですが、古いブラウザでそれをシムするライブラリはたくさんあります。私の個人的なお気に入りはアンダースコアです。

注2

どこかでクロージャにラップせずにsetTimeoutでオブジェクトのメソッドを呼び出すことはできませんが、必要に応じて、ジェネリックbind関数(UnderscoreやjQueryなどによって提供される)を使用せずにクラスのクロージャを抽象化できます。次のようなクラスで「自分でロール」します。

function TestObj(value) {

    var _value = value;
    var _self = this;

    this.giveMe = function() {
        return _value;
    };

    this.callMeLaterTestObj = function() {
        console.log('I am ' + this.constructor.name + ' my value is ' + _value);
    };

    this.getBinding = function(method) {
        var _self = this;
        return function() {
            _self[method].apply(_self, arguments);
        };
    };

    return this;
};

function callMeLater(v, i) {
    setTimeout(v.getBinding('callMeLaterTestObj'), 10);    
}

var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);

console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');

callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);​

説明:

メソッドをに渡すときは参照によって渡すため、何らかのバインディングを使用する必要がありsetTimeoutます。したがって、setTimeoutが認識するのは関数であり、それが存在していたオブジェクトではありません。そのため、のコンテキストが失われますthis

したがって、setTimeoutはデフォルトのスコープ(つまりブラウザウィンドウ)で関数を実行するためthis、インライン匿名関数を介して、またはapplyメソッドを使用して「リセット」するクロージャを返すことにより、参照によって戻る方法が必要thisです。

注3

独自のバインドメソッドが必要で、それを提供するライブラリを含めたり、すべてのクラスに含めたりしたくない場合は、アンダースコアからこれを使用できます。これは、新しいブラウザのネイティブメソッドに従います。

function bind(func, context) {
  var bound, args;
  if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  if (!_.isFunction(func)) throw new TypeError;
  args = slice.call(arguments, 2);
  return bound = function() {
    if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
    ctor.prototype = func.prototype;
    var self = new ctor;
    var result = func.apply(self, args.concat(slice.call(arguments)));
    if (Object(result) === result) return result;
    return self;
  };
};

次に、次のように使用します。

function callMeLater(v, i) {
    setTimeout(bind(v.callMeLaterTestObj, v), 10);    
}

これはすべてのブラウザでうまく機能します。

于 2012-11-29T16:36:54.120 に答える