JS開發者應懂的33個概念系列6--this,bind,call,apply

Wendydesigner發表於2019-01-24

JS開發者應懂的33個概念系列6--this,bind,call,apply

話說,只有掌握了JS中this用法才算真正的跨過了JS的門檻

this是什麼?

指向物件的this

想必大家都記得那句話“this 永遠指向最後呼叫它的那個物件”。這也就是隱式繫結的規則,函式被呼叫時先看一看點號左側。

var cat = {
    name: "kitty",
    getName: function() {
        console.log(this.name);
    }
}
cat.getName(); // "kitty"
複製程式碼

為了避免不必要的麻煩,這也是最常用的一種方式。但是樹林大了,什麼鳥都有,我們也要去了解更多的用法,才能以不變應萬變。

指向window的this

什麼樣子的this是會指向window呢?

我們對上面的例子進行一個小小的改動

var cat = {
    name: "kitty",
    getName: function() {
        console.log(this.name);
    }
}
var realName = cat.getName;
realName(); // undefined
複製程式碼

當然,我們按照前面的口訣,查詢呼叫這個函式的物件的時候,即這個函式被呼叫時點號的左側,並未發現沒有任何物件,對於此口訣已經不實用,我們已然找不到this是誰了?那這個時候this指向那裡呢?那就是window,而在全域性中並沒有定義 name屬性,所以我們會得到: undefined。 如果我們規定使用了ES5中的"use strict"的嚴格模式,那麼我們就會收到報錯:

Uncaught TypeError: Cannot read property 'name' of undefined
複製程式碼

使用ES6箭頭函式時的this

為了避免ES5中的this的問題,ES6引入了箭頭函式,箭頭函式的this指向的是定義時的物件,而非執行時。看個例子:

var name = "miao";
var cat = {
    name: "kitty",
    getName: function() {
        console.log(this.name);
    },
    getArrowName: () => {
        console.log(this.name);
    }
}
cat.getName(); // "kitty"
cat.getArrowName(); // "miao"
複製程式碼

ES6中的this規則:箭頭函式中沒有 this 繫結,必須通過查詢作用域鏈來決定其值,如果箭頭函式被非箭頭函式包含,則 this 繫結的是最近一層非箭頭函式的 this,否則,this 為 undefined

使用new時的this

function getName(name) {
    this.name = name;
}
var newName = new getName("kitty");
console.log(newName.name); // "kitty"

複製程式碼

new的實現原理

new getName("kitty")
其實就做了這樣的工作:
(function(){
    var obj = {};
    obj.__proto__ = getName.prototype;
    var result = getName.call(obj,"kitty");
    return (typeof result === 'object' && result) ? result : obj;
})()
複製程式碼

使用call時的this

上面提到call,它是可以改變this的指向。使用和表現方式如下:

var cat = {
    name: "kitty",
    getAge: function(age, voice) {
        console.log(this.name + " is " + age + " years old. " + voice);
    },
}
cat.getAge(5, "miao"); // "kitty is 5 years old. miao"
var dog = {
    name: "blank"
}
cat.getAge.call(dog, 2, "wangwang") // "blank is 2 years old. wangwang"
複製程式碼

call的實現原理

劃重點:面試題常考型別,小本本記一下:

Function.prototype.newCall = function(content) {
  // 當call傳入的物件是null的時候,或者其他一些型別的時候,函式會報錯。
  if(typeof content === 'object') {
    content = content || window;
  } else {
    content = Object.create(null);
  } 
  content.fn = this;
  var keys = Array.from(arguments);
  var arg = keys.slice(1);
  content.fn(...arg);
  delete content.fn;
}
var cat = {
  name: "Kitty",
  getAge: function(age, voice) {
      console.log(this.name + " is " + age + " years old. " + voice);
  },
}
var dog = {
  name: "Blank"
}
cat.getAge.newCall(dog, 2, "wangwang");
複製程式碼

使用apply時的this

call相比較,其實就是傳參的型別不同而已:

var cat = {
    name: "kitty",
    getAge: function(age, voice) {
        console.log(this.name + " is " + age + " years old. " + voice);
    },
}
var dog = {
    name: "blank"
}
cat.getAge.apply(dog, [2, "wangwang"]) // "blank is 2 years old. wangwang"
複製程式碼

apply的實現原理

Function.prototype.newApply = function(content, params) {
  // 當call傳入的物件是null的時候,或者其他一些型別的時候,函式會報錯。
  if(typeof content === 'object') {
    content = content || window;
  } else {
    content = Object.create(null);
  }
  content.fn = this;
  content.fn(...params);
  delete content.fn;
}


var cat = {
  name: "Kitty",
  getAge: function(age, voice) {
      console.log(this.name + " is " + age + " years old. " + voice);
  },
}
var dog = {
  name: "Blank"
}
cat.getAge.newApply(dog, [5, "wangwangwang"]);
複製程式碼

使用bind時的this

bind同樣可以改變this的指向,只不過其返回的是個函式

var cat = {
    name: "kitty",
    getAge: function(age, voice) {
        console.log(this.name + " is " + age + " years old. " + voice);
    },
}
var dog = {
    name: "blank"
}
cat.getAge.bind(dog, 2, "wangwang")(); // "blank is 2 years old. wangwang"
複製程式碼

bind的實現原理

// 使用了ES6的擴充套件運算子
Function.prototype.newBind = function(content, ...params) {
  var obj = this;
  return function(...otherParams) {
    obj.call(content, ...params, ...otherParams);
  }
}


var cat = {
  name: "Kitty",
  getAge: function(age, voice) {
      console.log(this.name + " is " + age + " years old. " + voice);
  },
}
var dog = {
  name: "Blank"
}
var newGetAge = cat.getAge.newBind(dog, 10, "wuwuwu")
newGetAge("wuwuwu");
複製程式碼

相關文章