ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript の Function.prototye.bind_javascript トリック

JavaScript の Function.prototye.bind_javascript トリック

WBOY
WBOYオリジナル
2016-05-16 15:52:39917ブラウズ

関数バインディングは、JavaScript を使い始めるときにおそらく最も注意を払わないものですが、このコンテキストを別の関数で維持する方法の解決策が必要であることに気づいたとき、本当に必要なのは Function.prototype です。 bind() を実行しますが、まだこれに気づいていないかもしれません。

この問題に初めて遭遇したときは、コンテキストを変更した後も引き続き参照できるように、これを変数に設定したくなるかもしれません。多くの人は変数名として self、_this、または context を使用することを選択します (それを使用する人もいます)。これらの方法はすべて便利であり、もちろん問題はありません。しかし、より優れた、より専門的な方法があります。

私たちが解決する必要がある本当の問題は何ですか?
次のコード例では、コンテキストを変数に正当にキャッシュできます:

var myObj = {
 
  specialFunction: function () {
 
  },
 
  anotherSpecialFunction: function () {
 
  },
 
  getAsyncData: function (cb) {
    cb();
  },
 
  render: function () {
    var that = this;
    this.getAsyncData(function () {
      that.specialFunction();
      that.anotherSpecialFunction();
    });
  }
};
 
myObj.render();

単純に this.specialFunction() を使用してメソッドを呼び出すと、次のエラーが表示されます:

Uncaught TypeError: オブジェクト [オブジェクト グローバル] にはメソッド 'specialFunction' がありません
コールバック関数を実行するには、myObj オブジェクト コンテキストへの参照を保持する必要があります。 that.specialFunction() を呼び出すと、スコープのコンテキストを維持し、関数を正しく実行できます。 ただし、Function.prototype.bind() を使用するより簡単でクリーンな方法があります:

render: function () {
 
  this.getAsyncData(function () {
 
    this.specialFunction();
 
    this.anotherSpecialFunction();
 
  }.bind(this));
 
}

今何をしたの?
.bind() は関数を作成します。この関数が呼び出されるとき、その this キーワードは渡された値に設定されます (ここでは、bind() を呼び出すときに渡されるパラメータを指します)。したがって、必要なコンテキストである this (実際には myObj) を .bind() 関数に渡します。その後、コールバック関数が実行されると、これは myObj オブジェクトを指します。

Function.prototype.bind() が内部的にどのようなもので、どのように機能するかを知りたい場合は、非常に簡単な例を次に示します。

Function.prototype.bind = function (scope) {
  var fn = this;
  return function () {
    return fn.apply(scope);
  };
}

非常に単純な使用例もあります:

var foo = {
  x: 3
}
 
var bar = function(){
  console.log(this.x);
}
 
bar(); 
// undefined
 
var boundFunc = bar.bind(foo);
 
boundFunc(); 
// 3

新しい関数を作成し、それが実行されると、this は foo に設定されます。bar() を呼び出したときのようなグローバル スコープではありません。

ブラウザのサポート
ブラウザのバージョンのサポート
クロム 7
Firefox (Gecko) 4.0 (2)
Internet Explorer 9
オペラ 11.60
サファリ 5.1.4
ご覧のとおり、Function.prototype.bind は残念ながら IE8 以前ではサポートされていないため、フォールバックがない場合は問題が発生する可能性があります。

幸いなことに、Mozilla Developer Network (優れたリソース ライブラリ) は、.bind() メソッド自体を実装していないブラウザに確実な代替手段を提供します。

if (!Function.prototype.bind) {
 Function.prototype.bind = function (oThis) {
  if (typeof this !== "function") {
   
// closest thing possible to the ECMAScript 5 internal IsCallable function
   throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
  }
 
  var aArgs = Array.prototype.slice.call(arguments, 1), 
    fToBind = this, 
    fNOP = function () {},
    fBound = function () {
     return fToBind.apply(this instanceof fNOP && oThis
                 ? this
                 : oThis,
                aArgs.concat(Array.prototype.slice.call(arguments)));
    };
 
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
 
  return fBound;
 };
}

適用可能なモード

技術的なポイントを学ぶとき、その概念を徹底的に勉強して理解するだけでなく、それを目の前の仕事に応用できるか、またはそれに近いものがあるかどうかを確認することも役立つことがわかりました。以下の例のいくつかがあなたのコードに適用されるか、直面している問題が解決されることを願っています。

CLICK HANDLERS (クリックハンドラー関数)
用途の 1 つは、クリック イベントを記録する (またはクリック後のアクションを実行する) ことであり、これには次のような情報をオブジェクトに保存する必要がある場合があります。

var logger = {
  x: 0,    
  updateCount: function(){
    this.x++;
    console.log(this.x);
  }
}

次の方法でクリック処理関数を指定し、ロガー オブジェクトの updateCount() メソッドを呼び出します。

document.querySelector('button').addEventListener('click', function(){
  logger.updateCount();
});
しかし、updateCount() 関数の this キーワードが正しい値を持つようにするには、追加の匿名関数を作成する必要があります。

次のよりクリーンな方法を使用できます:

document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));

便利な .bind() 関数を巧みに使用して、新しい関数を作成し、そのスコープをロガー オブジェクトにバインドします。

SETTIMEOUT

テンプレート エンジン (Handlebars など) または特に一部の MV* フレームワーク (私の経験から言えば、Backbone.js についてしか話せません) を使用したことがある場合は、テンプレート ノードの使用時に発生する問題。

jQuery プラグインをインスタンス化するとします。

var myView = {
 
  template: '/* 一个包含 <select /> 的模板字符串*/',
 
  $el: $('#content'),
 
  afterRender: function () {
    this.$el.find('select').myPlugin();
  },
 
  render: function () {
    this.$el.html(this.template());
    this.afterRender();
  }
}
 
myView.render();

うまくいくかもしれませんが、問題があるので毎回うまくいくわけではありません。それは競争の問題です。最初にそこに着いた人が勝ちです。レンダリングが最初に行われる場合もあれば、プラグインのインスタンス化が最初に行われる場合もあります。 [翻訳者注: レンダリング プロセスが完了していない (DOM ノードが DOM ツリーに追加されていない) 場合、find('select') はインスタンス化を実行するための対応するノードを見つけることができません。 】

ご存知の方は少ないかもしれませんが、setTimeout() に基づいたちょっとしたハックを使用して問題を解決できます。

DOM ノードがロードされた後に jQuery プラグインを安全にインスタンス化するためにコードを少し書き直してみましょう:

afterRender: function () {
    this.$el.find('select').myPlugin();
  },
 
  render: function () {
    this.$el.html(this.template());
    setTimeout(this.afterRender, 0);    
  }

然而,我们获得的是 函数 .afterRender() 不能找到 的错误信息。

我们接下来要做的,就是将.bind()使用到我们的代码中:

//
 
  afterRender: function () {
    this.$el.find('select').myPlugin();
  },
 
  render: function () {
    this.$el.html(this.template());
    setTimeout(this.afterRender.bind(this), 0);    
  }
 
//

以上所述就是本文的全部内容了,希望大家能够喜欢。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。