Pengikatan fungsi mungkin merupakan perkara yang anda kurang ambil perhatian apabila anda mula menggunakan JavaScript, tetapi apabila anda menyedari bahawa anda memerlukan penyelesaian tentang cara mengekalkan konteks ini dalam fungsi lain, anda Apa yang anda perlukan sebenarnya ialah Function.prototype. bind(), tetapi anda mungkin masih tidak menyedari perkara ini.
Kali pertama anda menghadapi masalah ini, anda mungkin tergoda untuk menetapkan ini kepada pembolehubah supaya anda boleh terus merujuk kepadanya selepas menukar konteks. Ramai orang memilih untuk menggunakan diri, _ini atau konteks sebagai nama pembolehubah (sesetengahnya juga menggunakan itu). Kaedah-kaedah ini semuanya berguna dan sudah tentu tiada salahnya. Tetapi ada cara yang lebih baik dan lebih khusus.
Apakah masalah sebenar yang perlu kita selesaikan?
Dalam kod contoh berikut, kita boleh menyimpan konteks secara sah ke dalam pembolehubah:
var myObj = { specialFunction: function () { }, anotherSpecialFunction: function () { }, getAsyncData: function (cb) { cb(); }, render: function () { var that = this; this.getAsyncData(function () { that.specialFunction(); that.anotherSpecialFunction(); }); } }; myObj.render();
Jika kami hanya menggunakan this.specialFunction() untuk memanggil kaedah, kami akan menerima ralat berikut:
Uncaught TypeError: Objek [objek global] tidak mempunyai kaedah 'specialFunction'
Kita perlu menyimpan rujukan kepada konteks objek myObj untuk pelaksanaan fungsi panggil balik. Memanggil that.specialFunction() membolehkan kami mengekalkan konteks skop dan melaksanakan fungsi kami dengan betul. Walau bagaimanapun, terdapat cara yang lebih mudah dan bersih menggunakan Function.prototype.bind():
render: function () { this.getAsyncData(function () { this.specialFunction(); this.anotherSpecialFunction(); }.bind(this)); }
Apa yang baru kita buat?
.bind() mencipta fungsi Apabila fungsi ini dipanggil, kata kunci ini akan ditetapkan kepada nilai yang dihantar (di sini merujuk kepada parameter yang diluluskan semasa memanggil bind()). Oleh itu, kami meneruskan dalam konteks yang dikehendaki, ini (sebenarnya myObj), kepada fungsi .bind(). Kemudian, apabila fungsi panggil balik dilaksanakan, ini akan menunjuk ke objek myObj.
Jika anda berminat untuk mengetahui rupa Function.prototype.bind() secara dalaman dan cara ia berfungsi, berikut ialah contoh yang sangat mudah:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope); }; }
Terdapat juga kes penggunaan yang sangat mudah:
var foo = { x: 3 } var bar = function(){ console.log(this.x); } bar(); // undefined var boundFunc = bar.bind(foo); boundFunc(); // 3
Kami mencipta fungsi baharu, dan apabila ia dilaksanakan, ia akan ditetapkan kepada foo - bukan skop global seperti semasa kami memanggil bar().
Sokongan penyemak imbas
Sokongan Versi Penyemak Imbas
Chrome 7
Firefox (Gecko) 4.0 (2)
Internet Explorer 9
Opera 11.60
Safari 5.1.4
Seperti yang anda lihat, Function.prototype.bind malangnya tidak disokong dalam IE8 dan ke bawah, jadi jika anda tidak mempunyai sandaran, anda mungkin menghadapi masalah.
Nasib baik, Rangkaian Pembangun Mozilla (perpustakaan sumber yang hebat) menyediakan alternatif yang kukuh untuk penyemak imbas yang tidak melaksanakan kaedah .bind() itu sendiri:
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; }; }
Mod yang berkenaan
Apabila mempelajari mata teknikal, saya mendapati bahawa perkara yang berguna bukan sahaja untuk mengkaji dan memahami konsep secara menyeluruh, tetapi juga untuk melihat sama ada terdapat aplikasinya dalam kerja yang sedang dijalankan, atau sesuatu yang hampir dengannya. Saya berharap beberapa contoh di bawah digunakan pada kod anda atau menyelesaikan masalah yang anda hadapi.
PENANGAN KLIK (fungsi pengendali klik)
Satu kegunaan adalah untuk merekodkan peristiwa klik (atau melakukan tindakan selepas klik), yang mungkin memerlukan kami menyimpan beberapa maklumat dalam objek, seperti:
var logger = { x: 0, updateCount: function(){ this.x++; console.log(this.x); } }
Kami boleh menentukan fungsi pemprosesan klik dengan cara berikut, dan kemudian memanggil kaedah updateCount() dalam objek logger.
document.querySelector('button').addEventListener('click', function(){ logger.updateCount(); });
Tetapi kita mesti mencipta fungsi tanpa nama tambahan untuk memastikan kata kunci ini dalam fungsi updateCount() mempunyai nilai yang betul.
Kita boleh menggunakan cara yang lebih bersih berikut:
document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));
Kami bijak menggunakan fungsi .bind() yang mudah untuk mencipta fungsi baharu dan mengikat skopnya pada objek logger.
SETTIMEOUT
Jika anda telah menggunakan enjin templat (seperti Bar Hendal) atau terutamanya beberapa rangka kerja MV* (dari pengalaman saya, saya hanya boleh bercakap tentang Backbone.js), maka anda mungkin mengetahui perbincangan di bawah tentang mengakses DOM baharu serta-merta selepas memberikan templat Masalah yang dihadapi semasa menggunakan nod.
Katakan kita mahu membuat instantiate pemalam 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();
Anda mungkin mendapati ia berkesan - tetapi bukan setiap masa kerana terdapat masalah. Ini soal persaingan: sesiapa yang sampai ke sana dahulu menang. Kadangkala pemaparan didahulukan, dan kadangkala pemalam diutamakan. [Nota Penterjemah: Jika proses pemaparan belum selesai (Nod DOM belum ditambahkan pada pepohon DOM), maka find('select') tidak akan dapat mencari nod yang sepadan untuk melaksanakan instantiasi. 】
Kini, mungkin tidak ramai yang tahu, kita boleh menggunakan sedikit hack berdasarkan setTimeout() untuk menyelesaikan masalah.
Mari kita tulis semula sedikit kod kami untuk menyatakan pemalam jQuery kami dengan selamat selepas nod DOM dimuatkan:
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); } //
以上所述就是本文的全部内容了,希望大家能够喜欢。