真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

深入學(xué)習(xí)js函數(shù)的隱式參數(shù)arguments和this

前言

成都創(chuàng)新互聯(lián)專注于嘉禾企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站建設(shè),商城網(wǎng)站制作。嘉禾網(wǎng)站建設(shè)公司,為嘉禾等地區(qū)提供建站服務(wù)。全流程定制設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

在函數(shù)調(diào)用時(shí),arguments和this會(huì)被靜默的傳遞給函數(shù),并可以在函數(shù)體內(nèi)引用它們,借以訪問函數(shù)相關(guān)的一些信息。
其中arguments是一個(gè)類數(shù)組結(jié)構(gòu),它保存了調(diào)用時(shí)傳遞給函數(shù)的所有實(shí)參;this是函數(shù)執(zhí)行時(shí)的上下文對(duì)象, 這個(gè)對(duì)象有些讓人感到困惑的行為。 下面分別對(duì)他們進(jìn)行討論。

1. arguments

1.1 背景

JavaScript 允許函數(shù)在調(diào)用時(shí)傳入的實(shí)參個(gè)數(shù)和函數(shù)定義時(shí)的形參個(gè)數(shù)不一致, 比如函數(shù)在定義時(shí)聲明了 n 個(gè)參數(shù), 在調(diào)用函數(shù)時(shí)不一定非要傳入 n 個(gè)參數(shù),例如:

// 1. 定義有一個(gè)形參的函數(shù)fn()
function fn(arg){}
// 2. 在調(diào)用時(shí)傳入 0 個(gè)或 多個(gè)參數(shù),并不會(huì)報(bào)錯(cuò)
fn(); // 傳入 0 個(gè)參數(shù)
fn(1,'a',3); // 傳入多個(gè)參數(shù)

1.2 arguments 與 形參的對(duì)應(yīng)關(guān)系

arguments是個(gè)類數(shù)組結(jié)構(gòu),它存儲(chǔ)了函數(shù)在調(diào)用時(shí)傳入的所有實(shí)參, 通過訪問它的length屬性可以得到其中保存的實(shí)參的個(gè)數(shù),并可以通過arguments[n]按順序取出傳入的每個(gè)參數(shù)(n=1,2,..,arguments.length-1)。
參數(shù)在arguments中保存的順序和傳入的順序相同, 同時(shí)也和形參聲明的順序相同,例如:

function fn(arg1, arg2, arg3){
console.log(arg1 === arguments[0]); // true
console.log(arg2 === arguments[1]); // true
console.log(arg3 === arguments[2]); // true
}
fn(1,2,3); // 調(diào)用

當(dāng)傳入的實(shí)參多于形參個(gè)數(shù)時(shí),想要獲得多余出的實(shí)參,就可以用arguments[n]來獲取了, 例如:

// 定義只有一個(gè)形參的函數(shù)
function fn(arg1){ 
console.log('length of arguments is:',arguments.length);
console.log('arguments[0] is:', arguments[0]); // 獲取傳入的第一個(gè)實(shí)參, 也就是形參 arg1 的值
console.log('arguments[1] is:', arguments[1]); // 獲取第二個(gè)實(shí)參的值, 沒有形參與其對(duì)應(yīng)
console.log('arguments[2] is:', arguments[2]); // 獲取第二個(gè)實(shí)參的值, 沒有形參與其對(duì)應(yīng)
}
fn(1,2,3); // 傳入 3 個(gè)實(shí)參
// 可以得到實(shí)際上傳入的實(shí)參的個(gè)數(shù)并取出所有實(shí)參
// length of arguments is: 3
// arguments[0] is: 1
// arguments[1] is: 2
// arguments[2] is: 3

1.3 arguments 與 形參的值相互對(duì)應(yīng)

在非嚴(yán)格模式下, 修改arguments中的元素值會(huì)修改對(duì)應(yīng)的形參值;同樣的,修改形參的值也會(huì)修改對(duì)應(yīng)的arguments中保存的值。下面的實(shí)驗(yàn)可以說明:

function fn(arg1, arg2){
// 1. 修改arguments元素,對(duì)應(yīng)的形參也會(huì)被修改
arguments[0] = '修改了arguments';
console.log(arg1); 
// 2. 修改形參值,對(duì)應(yīng)的arguments也會(huì)被修改
arg2 = '修改了形參值';
console.log(arguments[1]); 
}
fn(1,2);
// '修改了arguments'
// '修改了形參值'

但是,在嚴(yán)格模式下不存在這種情況, 嚴(yán)格模式下的arguments和形參的值之間失去了對(duì)應(yīng)的關(guān)系:

'use strict'; // 啟用嚴(yán)格模式
function fn(arg1, arg2){
// 修改arguments元素,對(duì)應(yīng)的形參也會(huì)被修改
arguments[0] = '修改了arguments';
console.log(arg1);
// 修改形參值,對(duì)應(yīng)的arguments也會(huì)被修改
arg2 = '修改了形參值';
console.log(arguments[1]);
}
fn(1,2);
// 1
// 2

注意: arguments 的行為和屬性雖然很像數(shù)組, 但它并不是數(shù)組,只是一種類數(shù)組結(jié)構(gòu):

function fn(){
console.log(typeof arguments); // object
console.log(arguments instanceof Array); // false
}
fn();

1.4 為什么要了解 arguments

在ES6中, 可以用靈活性更強(qiáng)的解構(gòu)的方式(...符號(hào))獲得函數(shù)調(diào)用時(shí)傳入的實(shí)參,而且通過這種方式獲得的實(shí)參是保存在真正的數(shù)組中的,例如:

function fn(...args){ // 通過解構(gòu)的方式得到實(shí)參
console.log(args instanceof Array); // args 是真正的數(shù)組
console.log(args); // 而且 args 中也保存了傳入的實(shí)參
}
fn(1,2,3);
// true
// Array(3) [1, 2, 3]

那么在有了上面這種更加靈活的方式以后,為什么還要了解arguments呢? 原因是在維護(hù)老代碼的時(shí)候可能不得不用到它。

2. 函數(shù)上下文: this

在函數(shù)調(diào)用時(shí), 函數(shù)體內(nèi)也可以訪問到 this 參數(shù), 它代表了和函數(shù)調(diào)用相關(guān)聯(lián)的對(duì)象,被稱為函數(shù)上下文。
this的指向受到函數(shù)調(diào)用方式的影響, 而函數(shù)的調(diào)用方式可以分成以下4種:

  1. 直接調(diào)用, 例如: fn()
  2. 作為對(duì)象的方法被調(diào)用, 例如: obj.fn()
  3. 被當(dāng)做一個(gè)構(gòu)造函數(shù)來使用, 例如: new Fn()
  4. 通過函數(shù) call() 或者 apply() 調(diào)用, 例如: obj.apply(fn) / obj.call(fn)

下面分別討論以上 4 種調(diào)用方式下 this 的指向.

2.1 直接調(diào)用一個(gè)函數(shù)時(shí) this 的指向

有些資料說在直接調(diào)用一個(gè)函數(shù)時(shí), 這個(gè)函數(shù)的 this 指向 window, 這種說法是片面的, 只有在非嚴(yán)格模式下而且是瀏覽器環(huán)境下才成立, 更準(zhǔn)確的說法是:在非嚴(yán)格模式下, this值會(huì)指向全局上下文(例如在瀏覽器中是window, Node.js環(huán)境下是global)。而在嚴(yán)格模式下, this 的值是 undefined。實(shí)驗(yàn)代碼如下:

// 非嚴(yán)格模式
function fn(){
console.log(this);
}
fn(); // global || Window

嚴(yán)格模式下:

'use strict';
function fn(){
console.log(this);
}
fn(); // undefined

總結(jié): 在直接調(diào)用一個(gè)函數(shù)時(shí), 它的 this 指向分成兩種情況: 在非嚴(yán)格模式下指向全局上下文, 在嚴(yán)格模式下指向 undefined.

2.2 被一個(gè)對(duì)象當(dāng)做方法調(diào)用

當(dāng)函數(shù)被一個(gè)對(duì)象當(dāng)成方法調(diào)用時(shí), 這個(gè)函數(shù)的 this 會(huì)指向調(diào)用它的對(duì)象。代碼驗(yàn)證如下:

// 定義一個(gè)對(duì)象
let xm = {
getThis (){ // 定義一個(gè)函數(shù)
return this; // 這個(gè)函數(shù)返回自己的 this 指向
}
}
let thisOfFunc = xm.getThis(); // 通過對(duì)象調(diào)用函數(shù)得到函數(shù)的 this 指向
console.log(thisOfFunc === xm); // true, 函數(shù)的this指向調(diào)用它的對(duì)象本身

因?yàn)檫@個(gè)原因, 對(duì)象的屬性可以通過this來訪問, 如果給 xm 加上一個(gè) name 屬性, 則通過 xm.name可以得到這個(gè)屬性值, 也可以在函數(shù)中通過 this.name 得到屬性值, 即 this.name 就是 vm.name, 進(jìn)一步, this===xm。 實(shí)驗(yàn)如下:

let xm = {
name: '小明', // 給 xm 加一個(gè)屬性, 可以通過 xm.name 訪問到
getName (){ 
return this.name; // 返回 this 的指向的 name 屬性
}
}
console.log(xm.name, xm.getName()); // 小明 小明

2.3 被作為構(gòu)造函數(shù)來調(diào)用時(shí)

2.3.1 不要像使用普通函數(shù)一樣使用構(gòu)造函數(shù)

構(gòu)造函數(shù)本質(zhì)上是函數(shù), 只是在被 new 操作符調(diào)用時(shí)一個(gè)函數(shù)才被稱為構(gòu)造函數(shù)。然而話雖如此, 但是由于寫出一個(gè)構(gòu)造函數(shù)的目的是用他來創(chuàng)建一個(gè)對(duì)象, 所以還要有一些約定俗成的東西來限制這個(gè)概念, 避免把構(gòu)造函數(shù)當(dāng)成普通函數(shù)來使用。例如, 構(gòu)造函數(shù)雖然能被直接調(diào)用, 但是不要這樣做,因?yàn)檫@是一個(gè)普通函數(shù)就可以做到的事情,例如:

function Person(name){
this.name = name;
return 1; // 不要這樣對(duì)待構(gòu)造函數(shù)
}
let n = Person(); // 不要這樣使用構(gòu)造函數(shù)

2.3.2 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí)發(fā)生了什么
當(dāng)使用 new 關(guān)鍵字來調(diào)用構(gòu)造函數(shù)的最終結(jié)果是產(chǎn)生了一個(gè)新對(duì)象, 而產(chǎn)生新對(duì)象的過程如下:

  1. 創(chuàng)建一個(gè)空對(duì)象 {}
  2. 將該對(duì)象的prototype鏈接到構(gòu)造函數(shù)的prototype上
  3. 將這個(gè)新對(duì)象作為 this 的指向
  4. 如果這個(gè)構(gòu)造函數(shù)沒有返回一個(gè)引用類型的值, 則將上面構(gòu)造的新對(duì)象返回

上面的內(nèi)容如果需要完全理解, 還需要了解原型相關(guān)的內(nèi)容。這里只需要關(guān)注第3、4步就可以了,即:將this綁定到生成到的新對(duì)象上,并將這個(gè)新對(duì)象返回, 進(jìn)一步下結(jié)論為:使用構(gòu)造函數(shù)時(shí), this 指向生成的對(duì)象, 實(shí)驗(yàn)結(jié)果如下:

function Person(){
this.getThis = function(){ // 這個(gè)函數(shù)返回 this
return this;
}
}
let p1 = new Person(); // 調(diào)用了構(gòu)造函數(shù)并返回了一個(gè)新的對(duì)象
console.log(p1.getThis() === p1); // true
let p2 = new Person();
console.log(p2.getThis() === p2); // true

2.3.3 結(jié)論

從上面的內(nèi)容可以得到如下的結(jié)論: 當(dāng)函數(shù)作為構(gòu)造函數(shù)使用時(shí), this 指向返回的新對(duì)象

2.4 通過 call() 或者 apply() 調(diào)用時(shí)

使用函數(shù) call 和 apply 可以在調(diào)用一個(gè)函數(shù)時(shí)指定這個(gè)函數(shù)的 this 的指向, 語法是:

fn.call(targetThis, arg1, arg2,..., argN)
fn.apply(targetThis, [arg1, arg2,.., argN])
fn: 要調(diào)用的函數(shù)
targetThis: 要把 fn 的 this 設(shè)置到的目標(biāo)
argument: 要給 fn 傳的實(shí)參

例如定義一個(gè)對(duì)象如下:

let xm = {
name: '小明',
sayName(){
console.log(this.name);
}
};
xm.sayName(); // 對(duì)象調(diào)用函數(shù)輸出 '小明'

上面定義了一個(gè)對(duì)象, 對(duì)象的 name 屬性為'小明'; sayName 屬性是個(gè)函數(shù), 功能是輸出對(duì)象的 name 屬性的值。根據(jù)2.2部分可知 sayName 這個(gè)函數(shù)的 this 指向 xm 對(duì)象, this.name 就是 xm.name。下面定義一個(gè)新對(duì)象, 并把 xm.sayName 這個(gè)函數(shù)的 this 指向新定義的對(duì)象。

新定義一個(gè)對(duì)象 xh:

let xh = {
name: '小紅'
};

對(duì)象 xh 只有 name 屬性, 沒有 sayName 屬性, 如果想讓 xh 也使用 sayName 函數(shù)來輸出自己的名字, 那么就要在調(diào)用 sayName 時(shí)讓它的 this 指向小紅, 以達(dá)到 this.name 等于 xh.name 的目的。 這個(gè)目的就可以通過 call 和 apply 兩個(gè)函數(shù)來實(shí)現(xiàn)。 以call 函數(shù)為例來實(shí)現(xiàn)這個(gè)需求, 只需要這樣寫就可以了:

xm.sayName.call(xh); // 小紅
xm.sayName.apply(xh); // 小紅

其中fn為xm.sayName; targetThis為xh, 這是因?yàn)閠argetThis的指向就是xh, 此結(jié)論可以由 2.2部分 的內(nèi)容得到。

2.4.1 call 和 apply 的區(qū)別

call 和 apply 的區(qū)別僅僅是要傳給fn的參數(shù)的形式不同:對(duì)于apply,傳給fn的參數(shù)argument是個(gè)數(shù)組,數(shù)組由所有參數(shù)組成;對(duì)于call,傳給fn的參數(shù)argument直接是所有參數(shù)的排列, 直接一個(gè)個(gè)寫入就可以。

例如要傳給函數(shù)fn三個(gè)參數(shù): 1、2、3. 則對(duì)于 call和apply調(diào)用的方法分別是:

fn.call(targetThis, 1, 2, 3); // 把 1,2,3直接傳入
fn.apply(targetThis, [1,2,3]); // 把1,2,3合成數(shù)組后作為參數(shù)

2.5 箭頭函數(shù) 和 bind 函數(shù)

箭頭函數(shù)和bind函數(shù)對(duì)于this的處理與普通函數(shù)不同, 要單獨(dú)拿出來說。

2.5.1 箭頭函數(shù)

與傳統(tǒng)函數(shù)不同, 箭頭函數(shù)本身不包含this, 它的 this 繼承自它定義時(shí)的作用域鏈的上一層。而且箭頭函數(shù)不能作為構(gòu)造函數(shù),它也沒有文章 第1部分 所說的arguments屬性。

下面用一個(gè)例子引出箭頭函數(shù)中this的來源:

function Person(){
this.age = 24;
setTimeout(function(){
console.log(this.age); // undefined
console.log(this === window); // true
}, 1000);
}
var p = new Person(); // 創(chuàng)建一個(gè)實(shí)例的時(shí)候就立即執(zhí)行了定時(shí)器

可以看到, 在定時(shí)器內(nèi)定義的普通匿名函數(shù)無法訪問到 Person 的 age 屬性, 這是因?yàn)閟etTimeout是個(gè)全局函數(shù), 它的內(nèi)部的this指向的是window, 而 window 上沒有 age 這個(gè)屬性, 所以就得到了 undefined。 從下面this === window 為 true 也說明了匿名函數(shù)中this指向的是window。

將普通的函數(shù)換成箭頭函數(shù)之后可以看到如下結(jié)果:

function Person(){
this.age = 24;
setTimeout(() => {
console.log(this.age); // 24
console.log(this === p); // true
}, 1000);
}
var p = new Person();

由上面的代碼可以看出箭頭函數(shù)內(nèi)的 this 指向?qū)嵗?p, 即它的 this 指向的是定義時(shí)候的作用域鏈的上一層。

說明: 這個(gè)例子僅用來引出箭頭函數(shù)的this指向的來源, 不要像這樣使用構(gòu)造函數(shù)。

2.5.2 bind函數(shù)

bind函數(shù)的作用是根據(jù)一個(gè)舊函數(shù)而創(chuàng)建一個(gè)新函數(shù),語法為newFn = oldFn.bind(thisTarget)。它會(huì)將舊函數(shù)復(fù)制一份作為新函數(shù), 然后將新函數(shù)的this永遠(yuǎn)綁定到thisTarget指向的上下文中, 然后返回這個(gè)新函數(shù), 以后每次調(diào)用這個(gè)新函數(shù)時(shí), 無論用什么方法都無法改變這個(gè)新函數(shù)的 this 指向。例如:

// 創(chuàng)建一個(gè)對(duì)象有 name 和 sayName 屬性
let p1 = {
name: 'P1',
sayName(){ 
console.log(this.name); // 訪問函數(shù)指向的 this 的 name 屬性
}
}
p1.sayName(); // P1
// 創(chuàng)建一個(gè)對(duì)象 p2, 并把這個(gè)對(duì)象作為bind函數(shù)綁定的this
let p2 = {
name: 'P2'
}
// 將p1的 sayName 函數(shù)的 this 綁定到 p2 上, 生成新函數(shù) sayP2Name 并返回
let sayP2Name = p1.sayName.bind(p2); 
// 由于此時(shí) sayP2Name 的內(nèi)部 this 已經(jīng)綁定了 p2, 
// 所以即使是按 文章2.1部分 所說的直接調(diào)用 sayP2Name, 它的 this 也是指向 p2 的, 并不是指向全局上下文或者 undefined
sayP2Name(); // P2
// 定義新對(duì)象, 嘗試將 sayP2Name 的 this 指向到 p3 上
let p3 = {
name: 'P3'
}
// 嘗試使用 call和apply 函數(shù)來將 sayP2Name 函數(shù)的 this 指向p3,
// 但是由于 sayP2Name 函數(shù)的this 已經(jīng)被bind函數(shù)永遠(yuǎn)綁定到p2上了, 所以this.name仍然是p2.name
sayP2Name.call(p3); // P2
sayP2Name.apply(p3); // P2

通過以上內(nèi)容可知一旦通過 bind 函數(shù)綁定了 this, 就再也無法改變 this 的指向了.

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


網(wǎng)頁名稱:深入學(xué)習(xí)js函數(shù)的隱式參數(shù)arguments和this
瀏覽路徑:http://weahome.cn/article/iejsoh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部