上篇文章寫的是js的繼承,其中就提到了經過調用call實現繼承,那麼call是怎麼作到的了?git
call,apply在咱們日常的開發中使用的頻率不高不低,可是它們倒是一個很是重要的知識點github
咱們經過一個實例來看一下它們的做用數組
var name = '小明' //這裏不能使用let,const由於它們聲明的變量沒有掛載到window
function fn() {
console.log(this.name)
}
fn() // 小明
複製代碼
const person = {
name: '張三'
}
fn.call(person) // 張三
fn.apply(person) // 張三
複製代碼
從上面代碼咱們能夠獲得三條結論bash
這裏面我着重講一下第三點,其實經過實例已近看的很清楚了,對象person實際上是沒有打印本身名字的方法的,可是經過apply/call調用後臨時生成了一個打印名字方法,得到返回值後,刪除掉這個方法,再把返回值returnapp
有了上面的理解以後咱們再來看一下ES5對apply的描述和規範函數
當以 thisArg 和 argArray 爲參數在一個 func 對象上調用 apply 方法,採用以下步驟:
Function.prototype.apply (thisArg, argArray)
1. 若是 IsCallable(func) 是 false, 則拋出一個 TypeError 異常 .
2. 若是 argArray 是 null 或 undefined, 則返回提供 thisArg 做爲 this 值並以空參數列表調用 func 的 [[Call]] 內部方法的結果。
3. 若是 Type(argArray) 不是 Object, 則拋出一個 TypeError 異常 .
4. 令 len 爲以 "length" 做爲參數調用 argArray 的 [[Get]] 內部方法的結果。
5. 令 n 爲 ToUint32(len).
6. 令 argList 爲一個空列表 .
7. 令 index 爲 0.
8. 只要 index < n 就重複
a. 令 indexName 爲 ToString(index).
b. 令 nextArg 爲以 indexName 做爲參數調用 argArray 的 [[Get]] 內部方法的結果。
c. 將 nextArg 做爲最後一個元素插入到 argList 裏。
d. 設定 index 爲 index + 1.
9. 提供 thisArg 做爲 this 值並以 argList 做爲參數列表,調用 func 的 [[Call]] 內部方法,返回結果。
apply 方法的 length 屬性是 2。
注意: 在外面傳入的 thisArg 值會修改併成爲 this 值。thisArg 是 undefined 或 null
時它會被替換成全局對象,全部其餘值會被應用 ToObject 並將結果做爲 this 值,這是第三版引入的更改。
複製代碼
ECMAScript 5.1中文文檔
ECMAScript 英文文文檔測試
第四條到第八條我看了一下中文文檔沒有看懂,去看英文文檔也沒有看懂,因此就跑去參考了一下大佬的文章,他們也都忽略了,因此咱們着重看這幾條以外的ui
function getWindow(){
return this
}
Function.prototype.newApply = function apply(thisArg,argArray) {
//第一條:若是調用者不是一個方法
if(typeof this !== "function"){
throw new TypeError("調用者不是一個方法")
}
//第二條:判斷參數是null或者undefind
if(typeof argArray ==="undefined" || typeof argArray === null){
argArray = []
}
//第三條:判斷參數是否是一個對象
if(!(argArray instanceof Object)){
throw new TypeError("參數要是對象")
}
// 在外面傳入的 thisArg 值會修改併成爲 this 值
if(typeof thisArg === 'undefined' || thisArg === null){
thisArg = getWindow();
}
//參考開篇的案例咱們爲第一個參數增長一個調用者方法,上述的判斷裏面this就是調用者,而且是一個方法
let __fn = 'fn'
thisArg[__fn] = this
//第九條:提供 thisArg 做爲 this 值並以 argList 做爲參數列表,調用 func 的 [[Call]] 內部方法,返回結果
let result = thisArg[__fn](...argArray)
delete thisArg[__fn]
return result
}
複製代碼
###測試一下this
var name = '小明'
function fn() {
console.log(this.name)
}
const person = {
name: '李四'
}
fn.newApply(person) // 李四
fn.newApply(person) // 李四
複製代碼
固然這個實現比較粗糙,由於咱們能取得對象得名字__fn可能與thisArg上面得函數名重複,咱們就再也不處理了,有興趣得同窗能夠本身去試一下ES6得symbol,來改進一下es5
###實現一下 call
Function.prototype.callFn = function call(thisArg){
var argsArray = [];
var argumentsLength = arguments.length;
for(var i = 0; i < argumentsLength - 1; i++){
// argsArray.push(arguments[i + 1]);
argsArray[i] = arguments[i + 1];
}
console.log('argsArray:', argsArray);
return this.applyFn(thisArg, argsArray);
}
複製代碼
其實call和apply功能徹底是同樣得
惟一得區別是apply的第二個參數是數組,而且只有兩個參數