Rumah > hujung hadapan web > tutorial js > Helah this_javascript yang menakjubkan dalam Javascript

Helah this_javascript yang menakjubkan dalam Javascript

WBOY
Lepaskan: 2016-05-16 15:18:59
asal
1180 orang telah melayarinya

Ini dalam Javascript adalah mekanisme yang sama sekali berbeza daripada bahasa lain, yang mungkin mengelirukan sesetengah jurutera yang menulis bahasa lain.

1. Tersalah anggap bahawa ini menunjukkan fungsi itu sendiri

Menurut tatabahasa bahasa Inggeris ini, adalah mudah untuk memahami perkara ini muncul dalam fungsi sebagai fungsi itu sendiri. Dalam JavaScript, fungsi adalah warga kelas pertama dan sememangnya boleh menyimpan nilai atribut apabila dipanggil. Tetapi jika anda menggunakannya secara tidak betul, ia boleh menyebabkan ketidakselarasan dengan jangkaan sebenar. Untuk keadaan tertentu, sila lihat kod di bawah

  function fn(num){
    this.count++;
  }
  
  fn.count = 0;
  
  for(var i=0;i<3;i++){
    fn(i);
  }
  console.log(fn.count); // 0
Salin selepas log masuk

Jika ini dalam fungsi fn menunjuk ke fungsinya sendiri, maka nilai atribut atribut count harus berubah, tetapi sebenarnya ia kekal tidak berubah. Untuk masalah ini, sesetengah orang akan menggunakan skop untuk menyelesaikannya, seperti menulis

  var data = {
    count:0
  };
  
  function fn(num){
    data.count++;
  }
  
  for(var i=0;i<3;i++){
    fn(i);
  }
  
  console.log(data.count);  //3
Salin selepas log masuk

Atau lebih lanjut secara langsung,

  function fn(num){
    fn.count++;
  }
  
  fn.count = 0;
  
  for(var i=0;i<3;i++){
    fn(i);
  }
  
  console.log(fn.count);//3
Salin selepas log masuk

Walaupun kedua-dua kaedah menghasilkan keputusan yang betul, ia mengelakkan masalah di mana ini terikat. Jika prinsip kerja sesuatu perkara tidak jelas, ia selalunya akan membawa kepada sakit kepala dan sakit, mengakibatkan kod hodoh dan kebolehselenggaraan yang lemah.

2. Peraturan mengikat ajaib ini

2.1 Peraturan mengikat lalai

Yang pertama adalah yang paling biasa pengikatan ini, lihat kod di bawah

  function fn(){
    console.log(window === this); //浏览器环境
  }
  fn(); //true
Salin selepas log masuk

Fungsi fn dipanggil terus dalam skop global tanpa sebarang pengubahsuaian lain Dalam kes ini, pengikatan lalai ini digunakan apabila fungsi dipanggil, menunjuk ke objek global.

Ini menjelaskan dengan jelas bahawa ini dalam contoh pertama menunjukkan pembolehubah global dalam fungsi fn, jadi this.count adalah bersamaan dengan window.count (dalam persekitaran penyemak imbas), dan sudah tentu ia tidak menjejaskan kiraan fungsi fn mempunyai kesan.

Satu perkara yang perlu diambil perhatian ialah situasi di atas hanya boleh berlaku dalam mod tidak ketat (mod ketat, ini akan terikat kepada tidak ditentukan secara lalai). Untuk mengelakkan pencemaran pembolehubah global.

2.2 Peraturan mengikat tersirat

Jika fungsi dipanggil dengan objek sebagai konteks, pengikatan ini akan berubah. ini akan terikat pada objek yang memanggil fungsi ini, lihat kod berikut:

  var obj = {
    a:1,
    fn:function(){
      console.log(this.a);
    }
  }
  
  obj.fn(); //1
Salin selepas log masuk

Walaupun pengisytiharan fungsi tiada dalam objek, penunjuk ini masih akan berubah

  function fn(){
    console.log(this.a);
  }
  var obj = {
    a:1,
    fn:fn
  }
  obj.fn(); //1
Salin selepas log masuk

Ia boleh dilihat bahawa pengikatan ini tidak berkaitan dengan lokasi definisi fungsi, tetapi dengan pemanggil dan kaedah panggilan.

Di bawah peraturan mengikat tersirat, terdapat beberapa perkara istimewa yang perlu diberi perhatian.

2.2.1 Objek berbilang lapisan memanggil penunjuk ini

  function fn(){
    console.log(this.a);
  }
  var obj = {
    a:1,
    obj2:obj2
  }
  var obj2 = {
    a:2,
    obj3:obj3
  }
  var obj3 = {
    a:3,
    fn:fn
  }
  
  obj.obj2.obj3.fn(); //3
Salin selepas log masuk

Di bawah rujukan objek berbilang peringkat, ini menunjukkan kepada objek fungsi yang dipanggil.

2.2.2 Tugasan tersirat mungkin hilang

Lihat kod di bawah

  function fn(){
    console.log(this);
  }
  var  obj = {
    fn:fn
  }
  
  var fun = obj.fn;
  fun(); //window
Salin selepas log masuk

Walaupun fn merujuk kepada obj.fun, kaedah memanggil fungsi masih tanpa sebarang pengubahsuaian, jadi ini masih terikat pada tetingkap.
Terdapat satu lagi situasi yang mudah untuk semua orang terlepas pandang, iaitu, apabila lulus parameter, tugasan tersirat sebenarnya akan dilakukan.

 function fn(){
    console.log(this);
  }
  
  function doFn(fn){
    fn();
  }
  
  var obj = {
    fn:fn
  }
  
  doFn(obj.fn); //window
Salin selepas log masuk

Pengikatan tersirat ini bukanlah kaedah yang sangat disyorkan, kerana ia berkemungkinan besar akan hilang Jika terdapat keperluan untuk pengikatan ini dalam perniagaan, adalah disyorkan untuk menggunakan pengikatan eksplisit.

2.3 Peraturan mengikat yang jelas

Ikatan eksplisit menggunakan kaedah guna dan panggil pada prototaip fungsi untuk mengikat ini. Penggunaannya adalah untuk lulus objek yang anda ingin ikat sebagai parameter pertama.

  function fn(){
    console.log(this);
  }
  
  var obj = {};
  
  fn.call(obj); //{}    
Salin selepas log masuk

Kadangkala anda ingin mengikat fungsi ini pada objek, tetapi tidak perlu memanggilnya dengan segera. Dalam kes ini, ia tidak boleh dilakukan secara langsung menggunakan panggilan atau gunakan.

  function fn(){
    console.log(this);
  }
  
  function bind(fn){
    fn();
  }
  
  var obj = {
    fn:fn
  }
  
  bind.call(obj,fn); //window
Salin selepas log masuk

Contoh di atas nampaknya berfungsi, tetapi sebenarnya, fungsi bind ini terikat pada objek obj, tetapi fn masih dipanggil tanpa sebarang pengubahsuaian, jadi fn masih merupakan kaedah pengikatan lalai.

  function fn(){
    console.log(this);
  }
  
  function bind(fn,obj){
    return function(){
      fn.apply(obj,arguments);
    }
  }
  
  var obj = {
    fn:fn
  }
  
  var fun = bind(fn,obj);
  fun(); //obj
Salin selepas log masuk

这样调用,就可以将灵活多变的 this ,牢牢的控制住了,因为 fn 的调用方式为 apply 调用。所以,this 就被绑定在传入的 obj 对象上,在 ES5 当中,函数的原型方法上多了一个 bind。效果与上面的函数基本一致,具体用法限于篇幅就不多说了。

2.4 new 绑定

new 是一个被很多人误解的一个关键字,但实际上 javascript 的 new 与传统面向对象的语言完全不同。
个人把 new 理解为一种特殊的函数调用,当使用 new 关键字来调用函数的时候,会执行下面操作,

  • 创建一个全新的对象
  • 将空对象的 __proto__ 指向构造函数的 prototype
  • 将新对象的 this 绑定到调用的函数上
  • 如果函数返回值为基本类型或者为 this又或者不返回任何值,那么将会返回这个创建的新对象,如果返回了一个对象,那么则会返回这个对象,而不会返回创建的新对象。
  function fn(a){
    this.a = a;
  }
  fn.prototype.hi = function(){
    console.log('hi')
  }
  
  var obj = new fn(2);
  
  console.log(obj);


  function fn(a){
    this.a = a;
    return {};
  }
  
  var obj = new fn(2);
  
  console.log(obj); //{}

Salin selepas log masuk

2.5 特殊的传参

null 和 undefined 也是可以作为 this 的绑定对象的,但是实际上应用的是默认的绑定。
但是这种传参的实际效用是什么呢?
常见的用法是将一个数组展开,作为参数传入参数。比如

  function fn(a,b){
    console.log('a:',a,'b:',b);
  }
  
  fn.apply(null,[1,2]); // a: 1 b: 2
Salin selepas log masuk

但是这种用法会有一个坑,那就是如果函数存在了 this ,那么就会应用默认的绑定规则,将 this 绑定在全局对象上,发生于预期不一致的情况。为了代码更加稳健,可以使创建一个比空对象更空的对象。

var obj = Object.create(null);
console.log(obj.__proto__); //undefined

var obj2 = {}
console.log(obj2.__proto__); //Object {}

Salin selepas log masuk

Object原型上有一个 create 方法,这个方法会创建一个对象,然后将对象的原型指向传入的参数,所以传入 null 的话,产生一个没有 prototype 的对象,所以会比空对象更加"空"。

所以传入这个对象,会比传入 null 更加安全。

var obj = Object.create(null);

fn.apply(obj,[1,2]);

Salin selepas log masuk

2.6 根据作用域来决定 this 的绑定

在 ES6 当中,出现了一个新的函数类型,箭头函数。

如果使用箭头函数,那么就不会使用上面提到的四种 this 绑定方式,而是根据作用域来决定

比较常见的是用于事件函数和定时器的情况。

下面是比较常见的传统 this 写法

  function fn(){
    var _this = this;
    setTimeout(function(){
      console.log(_this.a);
    },100)
  }

  var obj = {
    a:2
  }
  
  fn.call(obj); //2
  

Salin selepas log masuk

如果使用箭头函数则可以这么写

  function fn(){
    setTimeout(()=>{
      //this 来源于 fn 函数的作用域
      console.log(this.a);
    },100)
  }

  var obj = {
    a:2
  }
  
  fn.call(obj); //2

Salin selepas log masuk

2.7 事件函数当中 this 的绑定机制

如果是在事件函数当中,this 的绑定是指向触发事件的 DOM 元素的,

$('body')[0].addEventListener('click',function(){
  console.log(this);
},false);
Salin selepas log masuk

点击 body 元素之后,控制台则会显示 body 元素

3. 小结

如果想判断一个函数的 this 绑定在哪里,首先是找到函数的调用位置,之后是按照规则来判断。

  • 如果函数调用时没有任何修饰条件,那么在严格模式下则会绑定到 undefined ,非严格模式下会绑定到全局。
  • 如果是用对象做上下文,来对函数进行调用,那么则会绑定到调用的这个对象上。
  • 如果是用 call 或者 apply 方法来进行调用的,则会绑定到第一个传入参数上。
  • 如果是使用 new 关键字来调用函数的,则会绑定到新创建的那个对象上.
  • 如果是在事件函数内,则会绑定到触发事件的那个DOM元素上。

以上就是关于Javascript中神奇的this的相关介绍,希望对大家的学习有所帮助。

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan