成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JS 中 this 在各個場景下的指向

WrBug / 2399人閱讀

摘要:聲明了一個函數(shù),并且將它作為一個構(gòu)造函數(shù)調(diào)用構(gòu)造函數(shù)調(diào)用構(gòu)造函數(shù)調(diào)用是函數(shù)的構(gòu)造函數(shù)調(diào)用。構(gòu)造函數(shù)中的在構(gòu)造函數(shù)調(diào)用中指向新創(chuàng)建的對象構(gòu)造函數(shù)調(diào)用的上下文是新創(chuàng)建的對象。來看看下面示例中的上下文正在進(jìn)行構(gòu)造函數(shù)調(diào)用,其中上下文是。

為了保證的可讀性,本文采用意譯而非直譯。

想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!

1. this 的奧秘

很多時候, JS 中的 this 對于咱們的初學(xué)者很容易產(chǎn)生困惑不解。 this 的功能很強(qiáng)大,但需要一定付出才能慢慢理解它。

對Java、PHP或其他標(biāo)準(zhǔn)語言來看,this 表示類方法中當(dāng)前對象的實(shí)例。大多數(shù)情況下,this 不能在方法之外使用,這樣就比較不會造成混淆。

在J要中情況就有所不同: this表示函數(shù)的當(dāng)前執(zhí)行上下文,JS 中函數(shù)調(diào)用主要有以下幾種方式:

函數(shù)調(diào)用: alert("Hello World!")

方法調(diào)用: console.log("Hello World!")

構(gòu)造函數(shù): new RegExp("d")

隱式調(diào)用: alert.call(undefined, "Hello World!")

每種調(diào)用類型以自己的方式定義上下文,所以就很容易產(chǎn)生混淆。

此外,嚴(yán)格模式也會影響執(zhí)行上下文。

理解this關(guān)鍵是要清楚的知道函數(shù)調(diào)用及其如何影響上下文。

本文主要說明函數(shù)的調(diào)用方式及如何影響 this,并且說明執(zhí)行上下文的常見陷阱。

在開始之前,先知道幾個術(shù)語:

調(diào)用函數(shù)正在執(zhí)行創(chuàng)建函數(shù)體的代碼,或者只是調(diào)用函數(shù)。 例如,parseInt函數(shù)調(diào)用是parseInt("15")。

函數(shù)調(diào)用:執(zhí)行構(gòu)成函數(shù)主體的代碼:例如,parseInt函數(shù)調(diào)用是parseInt("15")。

調(diào)用的上下文:指 this 在函數(shù)體內(nèi)的值。 例如,map.set("key", "value")的調(diào)用上下文是 map。

函數(shù)的作用域:是在函數(shù)體中可訪問的變量、對象和函數(shù)的集合。

2.函數(shù)調(diào)用

當(dāng)一個表達(dá)式為函數(shù)接著一個(,一些用逗號分隔的參數(shù)以及一個時,函數(shù)調(diào)用被執(zhí)行,例如parseInt("18")。

函數(shù)調(diào)用表達(dá)式不能是屬性方式的調(diào)用,如 obj.myFunc(),這種是創(chuàng)建一個方法調(diào)用。再如 [1,5].join(",")不是函數(shù)調(diào)用,而是方法調(diào)用,這種區(qū)別需要記住哈,很重要滴。

函數(shù)調(diào)用的一個簡單示例:

function hello(name) {
  return "Hello " + name + "!";
}
// 函數(shù)調(diào)用
const message = hello("World");
console.log(message); // => "Hello World!"

hello("World")是函數(shù)調(diào)用: hello表達(dá)式等價于一個函數(shù),跟在它后面的是一對括號以及"World"參數(shù)。

一個更高級的例子是IIFE(立即調(diào)用的函數(shù)表達(dá)式)

const message = (function(name) {
   return "Hello " + name + "!";
})("World");
console.log(message) // => "Hello World!"

IIFE也是一個函數(shù)調(diào)用:第一對圓括號(function(name) {...})是一個表達(dá)式,它的計算結(jié)果是一個函數(shù)對象,后面跟著一對圓括號,圓括號的參數(shù)是“World”。

2.1. 在函數(shù)調(diào)用中的this
this 在函數(shù)調(diào)用中是一個全局對象

局對象由執(zhí)行環(huán)境決定。在瀏覽器中,thiswindow 對象。

在函數(shù)調(diào)用中,執(zhí)行上下文是全局對象。

再來看看下面函數(shù)中的上下文又是什么鬼:

function sum(a, b) {
   console.log(this === window); // => true
   this.myNumber = 20; // 將"myNumber"屬性添加到全局對象
   return a + b;
}
// sum() is invoked as a function
// sum() 中的 `this` 是一個全局對象(window)
sum(15, 16);     // => 31
window.myNumber; // => 20

在調(diào)用sum(15,16)時,JS 自動將this設(shè)置為全局對象,在瀏覽器中該對象是window

當(dāng)this在任何函數(shù)作用域(最頂層作用域:全局執(zhí)行上下文)之外使用,this 表示 window 對象

console.log(this === window); // => true
this.myString = "Hello World!";
console.log(window.myString); // => "Hello World!"




2.2 嚴(yán)格模式下的函數(shù)調(diào)用 this 又是什么樣的
this 在嚴(yán)格模式下的函數(shù)調(diào)用中為 undefined

嚴(yán)格模式是在 ECMAScript 5.1中引入的,它提供了更好的安全性和更強(qiáng)的錯誤檢查。

要啟用嚴(yán)格模式,函數(shù)頭部寫入use strict 即可。

啟用后,嚴(yán)格模式會影響執(zhí)行上下文,this 在常規(guī)函數(shù)調(diào)用中值為undefined。 與上述情況2.1相反,執(zhí)行上下文不再是全局對象。

嚴(yán)格模式函數(shù)調(diào)用示例:

function multiply(a, b) {
  "use strict"; // 啟用嚴(yán)格模式
  console.log(this === undefined); // => true
  return a * b;
}
multiply(2, 5); // => 10

當(dāng)multiply(2,5)作為函數(shù)調(diào)用時,thisundefined。

嚴(yán)格模式不僅在當(dāng)前作用域中有效,在內(nèi)部作用域中也是有效的(對于在內(nèi)部聲明的所有函數(shù)):

function execute() {
   "use strict"; // 開啟嚴(yán)格模式  
   function concat(str1, str2) {
     // 嚴(yán)格模式仍然有效  
     console.log(this === undefined); // => true
     return str1 + str2;
   }
   // concat() 在嚴(yán)格模式下作為函數(shù)調(diào)用
   // this in concat() is undefined
   concat("Hello", " World!"); // => "Hello World!"
}
execute();

"use strict"被插入到執(zhí)行體的頂部,在其作用域內(nèi)啟用嚴(yán)格模式。 因?yàn)楹瘮?shù)concat是在執(zhí)行的作用域中聲明的,所以它繼承了嚴(yán)格模式。

單個JS文件可能包含嚴(yán)格和非嚴(yán)格模式。 因此,對于相同的調(diào)用類型,可以在單個腳本中具有不同的上下文行為:

function nonStrictSum(a, b) {
  // 非嚴(yán)格模式
  console.log(this === window); // => true
  return a + b;
}
function strictSum(a, b) {
  "use strict";
  // 啟用嚴(yán)格模式
  console.log(this === undefined); // => true
  return a + b;
}

nonStrictSum(5, 6); // => 11
strictSum(8, 12); // => 20

2.3 陷阱:this 在內(nèi)部函數(shù)中的時候

函數(shù)調(diào)用的一個常見陷阱是,認(rèn)為this在內(nèi)部函數(shù)中的情況與外部函數(shù)中的情況相同。

正確地說,內(nèi)部函數(shù)的上下文只依賴于它的調(diào)用類型,而不依賴于外部函數(shù)的上下文。

要將 this 設(shè)置為所需的值,可以通過 .call().apply()修改內(nèi)部函數(shù)的上下文或使用.bind()創(chuàng)建綁定函數(shù)。

下面的例子是計算兩個數(shù)的和:

const numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       console.log(this === numbers); // => false
       return this.numberA + this.numberB;
     }
     return calculate();
   }
};
numbers.sum(); // => NaN 

sum()是對象上的方法調(diào)用,所以sum中的上下文是numbers對象。calculate函數(shù)是在sum中定義的,你可能希望在calculate()this也表示number對象。

calculate()是一個函數(shù)調(diào)用(不是方法調(diào)用),它將this作為全局對象window(非嚴(yán)格模下)。即使外部函數(shù)sum將上下文作為number對象,它在calculate里面沒有影響。

sum()的調(diào)用結(jié)果是NaN,不是預(yù)期的結(jié)果5 + 10 = 15,這都是因?yàn)闆]有正確調(diào)用calculate

為了解決這個問題,calculate函數(shù)中上下文應(yīng)該與 sum 中的一樣,以便可以訪問numberAnumberB屬性。

一種解決方案是通過調(diào)用calculator.call(this)手動將calculate上下文更改為所需的上下文。

const numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     function calculate() {
       console.log(this === numbers); // => true
       return this.numberA + this.numberB;
     }
     // 使用 .call() 方法修改上下文
     return calculate.call(this);
   }
};
numbers.sum(); // => 15

call(this)像往常一樣執(zhí)行calculate函數(shù),但 call 會把上下文修改為指定為第一個參數(shù)的值。

現(xiàn)在this.numberA + this.numberB相當(dāng)于numbers.numberA + numbers.numberB。 該函數(shù)返回預(yù)期結(jié)果5 + 10 = 15。

另一種就是使用箭頭函數(shù)

const numbers = {
   numberA: 5,
   numberB: 10,
   sum: function() {
     console.log(this === numbers); // => true
     const calculate = () => {
       console.log(this === numbers); // => true
       return this.numberA + this.numberB;
     }
     return calculate();
   }
};
numbers.sum(); // => 15
3.方法調(diào)用

方法是存儲在對象屬性中的函數(shù)。例如

const myObject = {
  // helloFunction 是一個方法
  helloFunction: function() {
    return "Hello World!";
  }
};
const message = myObject.helloFunction();

helloFunctionmyObject的一個方法,要調(diào)用該方法,可以這樣子調(diào)用 :myObject.helloFunction。

當(dāng)一個表達(dá)式以屬性訪問的形式執(zhí)行時,執(zhí)行的是方法調(diào)用,它相當(dāng)于以個函數(shù)接著(,一組用逗號分隔的參數(shù)以及)。

利用前面的例子,myObject.helloFunction()是對象myObject上的一個helloFunction的方法調(diào)用。[1, 2].join(",")/s/.test("beautiful world")也被認(rèn)為是方法調(diào)用。

區(qū)分函數(shù)調(diào)用和方法調(diào)用非常重要,因?yàn)樗鼈兪遣煌念愋?。主要區(qū)別在于方法調(diào)用需要一個屬性訪問器形式來調(diào)用函數(shù)(obj.myFunc()obj["myFunc"]()),而函數(shù)調(diào)用不需要(myFunc())。

["Hello", "World"].join(", "); // 方法調(diào)用
({ ten: function() { return 10; } }).ten(); // 方法調(diào)用
const obj = {};
obj.myFunction = function() {
  return new Date().toString();
};
obj.myFunction(); // 方法調(diào)用

const otherFunction = obj.myFunction;
otherFunction();     // 函數(shù)調(diào)用
parseFloat("16.60"); // 函數(shù)調(diào)用
isNaN(0);            // 函數(shù)調(diào)用

理解函數(shù)調(diào)用和方法調(diào)用之間的區(qū)別有助于正確識別上下文。

3.1 方法調(diào)用中 this 是腫么樣
在方法調(diào)用中,this是擁有這個方法的對象

當(dāng)調(diào)用對象上的方法時,this就變成了對象本身。

創(chuàng)建一個對象,該對象有一個遞增數(shù)字的方法

const calc = {
  num: 0,
  increment: function() {
    console.log(this === calc); // => true
    this.num += 1;
    return this.num;
  }
};
// method invocation. this is calc
calc.increment(); // => 1
calc.increment(); // => 2

調(diào)用calc.increment()使increment函數(shù)的上下文成為calc對象。所以使用this.num來增加num屬性是有效的。

再來看看另一個例子。JS對象從原型繼承一個方法,當(dāng)在對象上調(diào)用繼承的方法時,調(diào)用的上下文仍然是對象本身

const myDog = Object.create({
  sayName: function() {
     console.log(this === myDog); // => true
     return this.name;
  }
});
myDog.name = "Milo";
// 方法調(diào)用 this 指向 myDog
myDog.sayName(); // => "Milo"

Object.create()創(chuàng)建一個新對象myDog,并根據(jù)第一個參數(shù)設(shè)置其原型。myDog對象繼承sayName方法。

執(zhí)行myDog. sayname()時,myDog是調(diào)用的上下文。

在EC6 class 語法中,方法調(diào)用上下文也是實(shí)例本身

class Planet {
  constructor(name) {
    this.name = name;    
  }
  getName() {
    console.log(this === earth); // => true
    return this.name;
  }
}
var earth = new Planet("Earth");
// method invocation. the context is earth
earth.getName(); // => "Earth"
 

3.2 陷阱:將方法與其對象分離

方法可以從對象中提取到一個多帶帶的變量const alone = myObj.myMethod。當(dāng)方法多帶帶調(diào)用時,與原始對象alone()分離,你可能認(rèn)為當(dāng)前的this就是定義方法的對象myObject。

如果方法在沒有對象的情況下調(diào)用,那么函數(shù)調(diào)用就會發(fā)生,此時的this指向全局對象window嚴(yán)格模式下是undefined

下面的示例定義了Animal構(gòu)造函數(shù)并創(chuàng)建了它的一個實(shí)例:myCat。然后setTimout()在1秒后打印myCat對象信息

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;  
  this.logInfo = function() {
    console.log(this === myCat); // => false
    console.log("The " + this.type + " has " + this.legs + " legs");
  }
}
const myCat = new Animal("Cat", 4);
// The undefined has undefined legs 
setTimeout(myCat.logInfo, 1000); 

你可能認(rèn)為setTimout調(diào)用myCat.loginfo()時,它應(yīng)該打印關(guān)于myCat對象的信息。

不幸的是,方法在作為參數(shù)傳遞時與對象是分離,setTimout(myCat.logInfo)以下情況是等效的:

setTimout(myCat.logInfo);
// 等價于
const extractedLogInfo = myCat.logInfo;
setTimout(extractedLogInfo);

將分離的logInfo作為函數(shù)調(diào)用時,this是全局 window,所以對象信息沒有正確地打印。

函數(shù)可以使用.bind()方法與對象綁定,就可以解決 this 指向的問題。

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;  
  this.logInfo = function() {
    console.log(this === myCat); // => true
    console.log("The " + this.type + " has " + this.legs + " legs");
  };
}
const myCat = new Animal("Cat", 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo.bind(myCat), 1000);

myCat.logInfo.bind(myCat)返回一個新函數(shù),它的執(zhí)行方式與logInfo完全相同,但是此時的 this 指向 myCat,即使在函數(shù)調(diào)用中也是如此。

另一種解決方案是將logInfo()方法定義為一個箭頭函數(shù):

function Animal(type, legs) {
  this.type = type;
  this.legs = legs;  
  this.logInfo = () => {
    console.log(this === myCat); // => true
    console.log("The " + this.type + " has " + this.legs + " legs");
  };
}
const myCat = new Animal("Cat", 4);
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo, 1000);
4. 構(gòu)造函數(shù)調(diào)用

當(dāng)new關(guān)鍵詞緊接著函數(shù)對象,(,一組逗號分隔的參數(shù)以及)時被調(diào)用,執(zhí)行的是構(gòu)造函數(shù)調(diào)用如new RegExp("d")。

聲明了一個Country函數(shù),并且將它作為一個構(gòu)造函數(shù)調(diào)用:

function Country(name, traveled) {
   this.name = name ? name : "United Kingdom";
   this.traveled = Boolean(traveled); 
}
Country.prototype.travel = function() {
  this.traveled = true;
};
// 構(gòu)造函數(shù)調(diào)用
const france = new Country("France", false);
// 構(gòu)造函數(shù)調(diào)用
const unitedKingdom = new Country;

france.travel(); // Travel to France

new Country("France", false)Country函數(shù)的構(gòu)造函數(shù)調(diào)用。它的執(zhí)行結(jié)果是一個name屬性為"France"的新的對象。 如果這個構(gòu)造函數(shù)調(diào)用時不需要參數(shù),那么括號可以省略:new Country。

從ES6開始,JS 允許用class關(guān)鍵詞來定義構(gòu)造函數(shù)

class City {
  constructor(name, traveled) {
    this.name = name;
    this.traveled = false;
  }
  travel() {
    this.traveled = true;
  }
}
// Constructor invocation
const paris = new City("Paris", false);
paris.travel();

new City("Paris")是構(gòu)造函數(shù)調(diào)用。這個對象的初始化由這個類中一個特殊的方法constructor來處理。其中,this指向新創(chuàng)建的對象。

構(gòu)造函數(shù)創(chuàng)建了一個新的空的對象,它從構(gòu)造函數(shù)的原型繼承了屬性。構(gòu)造函數(shù)的作用就是去初始化這個對象。 可能你已經(jīng)知道了,在這種類型的調(diào)用中,上下文指向新創(chuàng)建的實(shí)例。

當(dāng)屬性訪問myObject.myFunction前面有一個new關(guān)鍵詞時,JS會執(zhí)行構(gòu)造函數(shù)調(diào)用而不是原來的方法調(diào)用。

例如new myObject.myFunction():它相當(dāng)于先用屬性訪問把方法提取出來extractedFunction = myObject.myFunction,然后利用把它作為構(gòu)造函數(shù)創(chuàng)建一個新的對象: new extractedFunction()

4.1. 構(gòu)造函數(shù)中的 this
在構(gòu)造函數(shù)調(diào)用中 this 指向新創(chuàng)建的對象

構(gòu)造函數(shù)調(diào)用的上下文是新創(chuàng)建的對象。它利用構(gòu)造函數(shù)的參數(shù)初始化新的對象,設(shè)定屬性的初始值,添加事件處理函數(shù)等等。

來看看下面示例中的上下文

function Foo () {
  console.log(this instanceof Foo); // => true
  this.property = "Default Value";
}
// Constructor invocation
const fooInstance = new Foo();
fooInstance.property; // => "Default Value"

new Foo() 正在進(jìn)行構(gòu)造函數(shù)調(diào)用,其中上下文是fooInstance。 在Foo內(nèi)部初始化對象:this.property被賦值為默認(rèn)值。

同樣的情況在用class語法(從ES6起)時也會發(fā)生,唯一的區(qū)別是初始化在constructor方法中進(jìn)行:

class Bar {
  constructor() {
    console.log(this instanceof Bar); // => true
    this.property = "Default Value";
  }
}
// Constructor invocation
const barInstance = new Bar();
barInstance.property; // => "Default Value"
4.2. 陷阱: 忘了使用 new

有些JS函數(shù)不是只在作為構(gòu)造函數(shù)調(diào)用的時候才創(chuàng)建新的對象,作為函數(shù)調(diào)用時也會,例如RegExp

var reg1 = new RegExp("w+");
var reg2 = RegExp("w+");

reg1 instanceof RegExp;      // => true
reg2 instanceof RegExp;      // => true
reg1.source === reg2.source; // => true

當(dāng)執(zhí)行的 new RegExp("w+")RegExp("w+")時,JS 會創(chuàng)建等價的正則表達(dá)式對象。

使用函數(shù)調(diào)用來創(chuàng)建對象存在一個潛在的問題(不包括工廠模式),因?yàn)橐恍?gòu)造函數(shù)可能會忽略在缺少new關(guān)鍵字時初始化對象的邏輯。

下面的例子說明了這個問題:

function Vehicle(type, wheelsCount) {
  this.type = type;
  this.wheelsCount = wheelsCount;
  return this;
}
// 忘記使用 new 
const car = Vehicle("Car", 4);
car.type;       // => "Car"
car.wheelsCount // => 4
car === window  // => true

Vehicle是一個在上下文對象上設(shè)置typewheelsCount屬性的函數(shù)。

當(dāng)執(zhí)行Vehicle("Car", 4)時,返回一個對象Car,它具有正確的屬性:Car.typeCarCar.wheelsCount4,你可能認(rèn)為它很適合創(chuàng)建和初始化新對象。

然而,在函數(shù)調(diào)用中,thiswindow對象 ,因此 Vehicle("Car",4)window 對象上設(shè)置屬性。 顯然這是錯誤,它并沒有創(chuàng)建新對象。

當(dāng)你希望調(diào)用構(gòu)造函數(shù)時,確保你使用了new操作符:

function Vehicle(type, wheelsCount) {
  if (!(this instanceof Vehicle)) {
    throw Error("Error: Incorrect invocation");
  }
  this.type = type;
  this.wheelsCount = wheelsCount;
  return this;
}
// Constructor invocation
const car = new Vehicle("Car", 4);
car.type               // => "Car"
car.wheelsCount        // => 4
car instanceof Vehicle // => true

// Function invocation. Throws an error.
const brokenCar = Vehicle("Broken Car", 3);

new Vehicle("Car",4) 運(yùn)行正常:創(chuàng)建并初始化一個新對象,因?yàn)闃?gòu)造函數(shù)調(diào)用中時使用了new關(guān)鍵字。

在構(gòu)造函數(shù)里添加了一個驗(yàn)證this instanceof Vehicle來確保執(zhí)行的上下文是正確的對象類型。如果this不是Vehicle,那么就會報錯。這樣,如果執(zhí)行Vehicle("Broken Car", 3)(沒有new),我們會得到一個異常:Error: Incorrect invocation。

5. 隱式調(diào)用

使用myFun.call()myFun.apply()方法調(diào)用函數(shù)時,執(zhí)行的是隱式調(diào)用。

JS中的函數(shù)是第一類對象,這意味著函數(shù)就是對象,對象的類型為Function。從函數(shù)對象的方法列表中,.call().apply()用于調(diào)用具有可配置上下文的函數(shù)。

方法 .call(thisArg[, arg1[, arg2[, ...]]])將接受的第一個參數(shù)thisArg作為調(diào)用時的上下文,arg1, arg2, ...這些則作為參數(shù)傳入被調(diào)用的函數(shù)。

方法.apply(thisArg, [args])將接受的第一個參數(shù)thisArg作為調(diào)用時的上下文,并且接受另一個類似數(shù)組的對象[arg1, arg2, ...] 作為被調(diào)用函數(shù)的參數(shù)傳入。

下面是隱式調(diào)用的例子

function increment(number) {
  return ++number;  
}
increment.call(undefined, 10);    // => 11
increment.apply(undefined, [10]); // => 11

increment.call()increment.apply()都用參數(shù)10調(diào)用了這個自增函數(shù)。

兩者的區(qū)別是.call()接受一組參數(shù),例如myFunction.call(thisValue, "value1", "value2")。而.apply()接受的一組參數(shù)必須是一個類似數(shù)組的對象,例如myFunction.apply(thisValue, ["value1", "value2"])。

5.1. 隱式調(diào)用中的this
在隱式調(diào)用.call()或.apply()中,this是第一個參數(shù)

很明顯,在隱式調(diào)用中,this作為第一個參數(shù)傳遞給.call().apply()

var rabbit = { name: "White Rabbit" };
function concatName(string) {
  console.log(this === rabbit); // => true
  return string + this.name;
}
concatName.call(rabbit, "Hello ");  // => "Hello White Rabbit"
concatName.apply(rabbit, ["Bye "]); // => "Bye White Rabbit"

當(dāng)應(yīng)該使用特定上下文執(zhí)行函數(shù)時,隱式調(diào)用非常有用。例如為了解決方法調(diào)用時,this總是window或嚴(yán)格模式下的undefined的上下文問題。隱式調(diào)用可以用于模擬在一個對象上調(diào)用某個方法。

function Runner(name) {
  console.log(this instanceof Rabbit); // => true
  this.name = name;  
}
function Rabbit(name, countLegs) {
  console.log(this instanceof Rabbit); // => true
  Runner.call(this, name);
  this.countLegs = countLegs;
}
const myRabbit = new Rabbit("White Rabbit", 4);
myRabbit; // { name: "White Rabbit", countLegs: 4 }

Rabbit中的Runner.call(this, name)隱式調(diào)用了父類的函數(shù)來初始化這個對象。

6. 綁定函數(shù)

綁定函數(shù)是與對象連接的函數(shù)。通常使用.bind()方法從原始函數(shù)創(chuàng)建。原始函數(shù)和綁定函數(shù)共享相同的代碼和作用域,但執(zhí)行時上下文不同。

方法 myFunc.bind(thisArg[, arg1[, arg2[, ...]]])接受第一個參數(shù)thisArg作為綁定函數(shù)執(zhí)行時的上下文,并且它接受一組可選的參數(shù) arg1, arg2, ...作為被調(diào)用函數(shù)的參數(shù)。它返回一個綁定了thisArg的新函數(shù)。

function multiply(number) {
  "use strict";
  return this * number;
}
const double = multiply.bind(2);

double(3);  // => 6
double(10); // => 20

bind(2)返回一個新的函數(shù)對象double,double 綁定了數(shù)字2。multiplydouble具有相同的代碼和作用域。

.apply().call() 方法相反,它不會立即調(diào)用該函數(shù),.bind()方法只返回一個新函數(shù),在之后被調(diào)用,只是this已經(jīng)被提前設(shè)置好了。

6.1. 綁定函數(shù)中的this
在調(diào)用綁定函數(shù)時,this.bind()的第一個參數(shù)。

.bind()的作用是創(chuàng)建一個新函數(shù),調(diào)用該函數(shù)時,將上下文作為傳遞給.bind()的第一個參數(shù)。它是一種強(qiáng)大的技術(shù),使咱們可以創(chuàng)建一個定義了this值的函數(shù)。

來看看,如何在如何在綁定函數(shù)設(shè)置 this

const numbers = {
  array: [3, 5, 10],
  getNumbers: function() {
    return this.array;    
  }
};
const boundGetNumbers = numbers.getNumbers.bind(numbers);
boundGetNumbers(); // => [3, 5, 10]
// Extract method from object
const simpleGetNumbers = numbers.getNumbers;
simpleGetNumbers(); // => undefined (嚴(yán)格模式下報錯)

numbers.getNumbers.bind(numbers)返回綁定numbers對象boundGetNumbers函數(shù)。boundGetNumbers()調(diào)用時的thisnumber對象,并能夠返回正確的數(shù)組對象。

可以將函數(shù)numbers.getNumbers提取到變量simpleGetNumbers中而不進(jìn)行綁定。在之后的函數(shù)調(diào)用中simpleGetNumbers()thiswindow(嚴(yán)格模式下為undefined),不是number對象。在這個情況下,simpleGetNumbers()不會正確返回數(shù)組。

6.2 緊密的上下文綁定

.bind()創(chuàng)建一個永久的上下文鏈接,并始終保持它。 一個綁定函數(shù)不能通過.call()或者.apply()來改變它的上下文,甚至是再次綁定也不會有什么作用。

只有綁定函數(shù)的構(gòu)造函數(shù)調(diào)用才能更改已經(jīng)綁定的上下文,但是很不推薦的做法(構(gòu)造函數(shù)調(diào)用必須使用常規(guī)的非綁定函數(shù))。

下面示例創(chuàng)建一個綁定函數(shù),然后嘗試更改其已預(yù)先定義好的上下文

function getThis() {
  "use strict";
  return this;
}
const one = getThis.bind(1);
// 綁定函數(shù)調(diào)用
one(); // => 1
// 使用帶有.apply()和.call()的綁定函數(shù)
one.call(2);  // => 1
one.apply(2); // => 1
// 再次綁定
one.bind(2)(); // => 1
// 以構(gòu)造函數(shù)的形式調(diào)用綁定函數(shù)
new one(); // => Object

只有new one()改變了綁定函數(shù)的上下文,其他方式的調(diào)用中this總是等于1。

7. 箭頭函數(shù)

箭頭函數(shù)用于以更短的形式聲明函數(shù),并在詞法上綁定上下文。它可以這樣使用

const hello = (name) => {
  return "Hello " + name;
};
hello("World"); // => "Hello World"
// Keep only even numbers
[1, 2, 5, 6].filter(item => item % 2 === 0); // => [2, 6]

箭頭函數(shù)語法簡單,沒有冗長的function 關(guān)鍵字。當(dāng)箭頭函數(shù)只有一條語句時,甚至可以省略return關(guān)鍵字。

箭頭函數(shù)是匿名的,這意味著name屬性是一個空字符串""。這樣它就沒有詞法上函數(shù)名(函數(shù)名對于遞歸、分離事件處理程序非常有用)

同時,跟常規(guī)函數(shù)相反,它也不提供arguments對象。但是,這在ES6中通過rest parameters修復(fù)了:

const sumArguments = (...args) => {
   console.log(typeof arguments); // => "undefined"
   return args.reduce((result, item) => result + item);
};
sumArguments.name      // => ""
sumArguments(5, 5, 6); // => 16
7.1. 箭頭函數(shù)中的this
this 定義箭頭函數(shù)的封閉上下文

箭頭函數(shù)不會創(chuàng)建自己的執(zhí)行上下文,而是從定義它的外部函數(shù)中獲取 this。 換句話說,箭頭函數(shù)在詞匯上綁定 this。

下面的例子說明了這個上下文透明的特性:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  log() {
    console.log(this === myPoint); // => true
    setTimeout(()=> {
      console.log(this === myPoint);      // => true
      console.log(this.x + ":" + this.y); // => "95:165"
    }, 1000);
  }
}
const myPoint = new Point(95, 165);
myPoint.log();

setTimeout使用與log()方法相同的上下文(myPoint對象)調(diào)用箭頭函數(shù)。正如所見,箭頭函數(shù)從定義它的函數(shù)繼承上下文。

如果在這個例子里嘗試用常規(guī)函數(shù),它創(chuàng)建自己的上下文(window或嚴(yán)格模式下的undefined)。因此,要使相同的代碼正確地使用函數(shù)表達(dá)式,需要手動綁定上下文:setTimeout(function(){…}.bind(this))。這很冗長,使用箭頭函數(shù)是一種更簡潔、更短的解決方案。

如果箭頭函數(shù)在最頂層的作用域中定義(在任何函數(shù)之外),則上下文始終是全局對象(瀏覽器中的 window):

onst getContext = () => {
   console.log(this === window); // => true
   return this;
};
console.log(getContext() === window); // => true

箭頭函數(shù)一勞永逸地與詞匯上下文綁定。 即使修改上下文,this也不能被改變:

const numbers = [1, 2];
(function() {  
  const get = () => {
    console.log(this === numbers); // => true
    return this;
  };
  console.log(this === numbers); // => true
  get(); // => [1, 2]
  // Use arrow function with .apply() and .call()
  get.call([0]);  // => [1, 2]
  get.apply([0]); // => [1, 2]
  // Bind
  get.bind([0])(); // => [1, 2]
}).call(numbers);

無論如何調(diào)用箭頭函數(shù)get,它總是保留詞匯上下文numbers。 用其他上下文的隱式調(diào)用(通過 get.call([0])get.apply([0]))或者重新綁定(通過.bind())都不會起作用。

箭頭函數(shù)不能用作構(gòu)造函數(shù)。 將它作為構(gòu)造函數(shù)調(diào)用(new get())會拋出一個錯誤:TypeError: get is not a constructor。

7.2. 陷阱: 用箭頭函數(shù)定義方法

你可能希望使用箭頭函數(shù)來聲明一個對象上的方法。箭頭函數(shù)的定義相比于函數(shù)表達(dá)式短得多:(param) => {...} instead of function(param) {..}。

來看看例子,用箭頭函數(shù)在Period類上定義了format()方法:

function Period (hours, minutes) {  
  this.hours = hours;
  this.minutes = minutes;
}
Period.prototype.format = () => {
  console.log(this === window); // => true
  return this.hours + " hours and " + this.minutes + " minutes";
};
const walkPeriod = new Period(2, 30);  
walkPeriod.format(); // => "undefined hours and undefined minutes"

由于format是一個箭頭函數(shù),并且在全局上下文(最頂層的作用域)中定義,因此 this 指向window對象。

即使format作為方法在一個對象上被調(diào)用如walkPeriod.format()window仍然是這次調(diào)用的上下文。之所以會這樣是因?yàn)榧^函數(shù)有靜態(tài)的上下文,并不會隨著調(diào)用方式的改變而改變。

該方法返回"undefined hours和undefined minutes",這不是咱們想要的結(jié)果。

函數(shù)表達(dá)式解決了這個問題,因?yàn)槌R?guī)函數(shù)確實(shí)能根據(jù)實(shí)際調(diào)用改變它的上下文:

function Period (hours, minutes) {  
  this.hours = hours;
  this.minutes = minutes;
}
Period.prototype.format = function() {
  console.log(this === walkPeriod); // => true
  return this.hours + " hours and " + this.minutes + " minutes";
};
const walkPeriod = new Period(2, 30);  
walkPeriod.format(); // => "2 hours and 30 minutes"

walkPeriod.format()是一個對象上的方法調(diào)用,它的上下文是walkPeriod對象。this.hours等于2,this.minutes等于30,所以這個方法返回了正確的結(jié)果:"2 hours and 30 minutes"

原文:https://dmitripavlutin.com/ge...

代碼部署后可能存在的BUG沒法實(shí)時知道,事后為了解決這些BUG,花了大量的時間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個好用的BUG監(jiān)控工具 Fundebug。

總結(jié)

為函數(shù)調(diào)用對this影響最大,從現(xiàn)在開始不要問自己:

this 是從哪里來的?

而是要看看

函數(shù)是怎么被調(diào)用的?

對于箭頭函數(shù),需要想想

在這個箭頭函數(shù)被定義的地方,this是什么?

這是處理this時的正確想法,它們可以讓你免于頭痛。

交流

干貨系列文章匯總?cè)缦拢X得不錯點(diǎn)個Star,歡迎 加群 互相學(xué)習(xí)。

https://github.com/qq44924588...

我是小智,公眾號「大遷世界」作者,對前端技術(shù)保持學(xué)習(xí)愛好者。我會經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!

關(guān)注公眾號,后臺回復(fù)福利,即可看到福利,你懂的。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/106689.html

相關(guān)文章

  • this指針使用及call(),apply()及bind()的使用

    摘要:目錄一理解指針意義二用解決指針指向問題三的使用場景與少用之處一理解指針意義讓我們先理解好指針的定義引用的是函數(shù)執(zhí)行的環(huán)境對象永遠(yuǎn)指向的是最后調(diào)用它的對象,也就是看它執(zhí)行的時候是誰調(diào)用的通俗地講,就是誰調(diào)用,就指向誰我們分類舉例舉例前先看下本 目錄 一.理解this指針意義二.用call(),apply(),bind()解決指針指向問題三.bind()的使用場景與少用之處 一.理解thi...

    iKcamp 評論0 收藏0
  • this總結(jié)【1】—— this概覽

    摘要:是什么這個單詞是一個代詞,所以應(yīng)該是指代某些東西搞清楚的關(guān)鍵之處,就是要搞清楚指代了什么那么到底指代了什么呢就像你平時指著一個蘋果說指著一個香蕉說同樣,也會因?yàn)榍闆r的不同而不同在中按照常規(guī)理解,的值是什么取決于函數(shù)如何被調(diào)用然而,的值是什么 1. this是什么 this這個單詞是一個代詞,所以this應(yīng)該是 指代某些東西搞清楚this的關(guān)鍵之處,就是要搞清楚this指代了什么 那么t...

    MyFaith 評論0 收藏0
  • js基礎(chǔ)深入淺出

    摘要:當(dāng)多個事件觸發(fā)的時候,會把異步事件依次的放入里等同步事件執(zhí)行完之后,再去隊(duì)列里一個個執(zhí)行拾遺常用方法總結(jié)面試的信心來源于過硬的基礎(chǔ)參考高級程序設(shè)計你所不知道的深入淺出知識點(diǎn)思維導(dǎo)圖經(jīng)典實(shí)例總結(jié)那些剪不斷理還亂的關(guān)系 持續(xù)不斷更新。。。 基本類型和引用類型 vue props | Primitive vs Reference Types 基本類型和字面值之間的區(qū)別 基本類型和字面值相等,...

    phodal 評論0 收藏0
  • 前端經(jīng)典文章

    摘要:上周末看這篇文章時,偶有靈光,所以,分享出來給大家一起看看前端面試四月二十家前端面試題分享請各位讀者添加一下作者的微信公眾號,以后有新的文章,將在微信公眾號直接推送給各位,非常感謝。 前端切圖神器 avocode 有了這個神器,切圖再也腰不酸,腿不疼了。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,...

    lowett 評論0 收藏0
  • 你應(yīng)該要知道的JSthis

    摘要:在用創(chuàng)建對象時,指向發(fā)生改變是在第二步創(chuàng)建一個對象實(shí)例將構(gòu)造函數(shù)中的指向這個對象執(zhí)行構(gòu)造函數(shù)中的代碼返回這個新創(chuàng)建的對象箭頭函數(shù)中的箭頭函數(shù)內(nèi)部是不會綁定的,它會捕獲外層作用域中的,作為自己的值。參考你不知道的上卷 前言 this 是 JavaScript 中不可不談的一個知識點(diǎn),它非常重要但又不容易理解。因?yàn)?JavaScript 中的 this 不同于其他語言。不同場景下的 thi...

    edgardeng 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<