apply 接收一個(gè)數(shù)組參數(shù)
Function.prototype.my_apply=function(target){
const cxt = target||window;
//將當(dāng)前被調(diào)用的方法定義再ctx.fun上
//為了能以對(duì)象調(diào)用的形式綁定this
cxt.func = this;
const res = arguments[1]?cxt.func(...arguments[1]):cxt.func();
//否則會(huì)對(duì)傳入對(duì)象造成污染(添加該方法)
delete cxt.func;
return res;
}
call 接收的是一個(gè)列表
function call(target){
const cxt = target||window;
//將當(dāng)前被調(diào)用的方法定義再ctx.fun上
//為了能以對(duì)象調(diào)用的形式綁定this
cxt.func = this;
const args = Array.from(arguments).slice(1);
const res = args.length>0?cxt.func(...args):cxt.func();
delete cxt.func;
return res;
}
apply 不會(huì)立即執(zhí)行而是返回一個(gè)函數(shù)
function baz (fn,obj) {
return function(){
return fn.apply(obj,arguments);
}
}
詳解bind
var obj = {};
console.log(obj);
console.log(typeof Function.prototype.bind); // function
console.log(typeof Function.prototype.bind()); // function
console.log(Function.prototype.bind.name); // bind
console.log(Function.prototype.bind().name); // bound
1龄捡、bind是Functoin原型鏈中Function.prototype的一個(gè)屬性,每個(gè)函數(shù)都可以調(diào)用它慷暂。
2聘殖、bind本身是一個(gè)函數(shù)名為bind的函數(shù),返回值也是函數(shù)行瑞,函數(shù)名是bound 奸腺。(打出來(lái)就是bound加上一個(gè)空格)。
知道了bind是函數(shù)血久,就可以傳參突照,而且返回值'bound '也是函數(shù),也可以傳參氧吐,就很容易寫(xiě)出例子2:
后文統(tǒng)一 bound 指原函數(shù)original bind之后返回的函數(shù)讹蘑,便于說(shuō)明。
var obj = {
name: '軒轅Rowboat',
};
function original(a, b){
console.log(this.name);
console.log([a, b]);
return false;
}
var bound = original.bind(obj, 1);
var boundResult = bound(2); // '軒轅Rowboat', [1, 2]
console.log(boundResult); // false
console.log(original.bind.name); // 'bind'
console.log(original.bind.length); // 1
console.log(original.bind().length); // 2 返回original函數(shù)的形參個(gè)數(shù)
console.log(bound.name); // 'bound original'
console.log((function(){}).bind().name); // 'bound '
console.log((function(){}).bind().length); // 0
由此可以得出結(jié)論2:
1筑舅、調(diào)用bind的函數(shù)中的this指向bind()函數(shù)的第一個(gè)參數(shù)座慰。
2、傳給bind()的其他參數(shù)接收處理了翠拣,bind()之后返回的函數(shù)的參數(shù)也接收處理了版仔,也就是說(shuō)合并處理了。
3误墓、并且bind()后的name為bound + 空格 + 調(diào)用bind的函數(shù)名邦尊。如果是匿名函數(shù)則是bound + 空格。
4优烧、bind后的返回值函數(shù)蝉揍,執(zhí)行后返回值是原函數(shù)(original)的返回值。
5畦娄、bind函數(shù)形參(即函數(shù)的length)是1又沾。bind后返回的bound函數(shù)形參不定弊仪,根據(jù)綁定的函數(shù)原函數(shù)(original)形參個(gè)數(shù)確定。
根據(jù)結(jié)論2:我們就可以簡(jiǎn)單模擬實(shí)現(xiàn)一個(gè)簡(jiǎn)版bindFn
// 第一版 修改this指向杖刷,合并參數(shù)
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + 'must be a function');
}
// 存儲(chǔ)函數(shù)本身
var self = this;
// 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組
var args = [].slice.call(arguments, 1);
var bound = function(){
// bind返回的函數(shù) 的參數(shù)轉(zhuǎn)成數(shù)組
var boundArgs = [].slice.call(arguments);
// apply修改this指向励饵,把兩個(gè)函數(shù)的參數(shù)合并傳給self函數(shù),并執(zhí)行self函數(shù)滑燃,返回執(zhí)行結(jié)果
return self.apply(thisArg, args.concat(boundArgs));
}
return bound;
}
// 測(cè)試
var obj = {
name: '軒轅Rowboat',
};
function original(a, b){
console.log(this.name);
console.log([a, b]);
}
var bound = original.bindFn(obj, 1);
bound(2); // '軒轅Rowboat', [1, 2]
但我們知道函數(shù)是可以用new來(lái)實(shí)例化的役听。那么bind()返回值函數(shù)會(huì)是什么表現(xiàn)呢。
參照mdn實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版的
Function.prototype.bindfun=function(){
const thatFun = this;
const thatTarget=arguments[0]
let args = [].slice.call(arguments,1);
const bound = function(){
let boundArgs=[].slice.call(arguments);
return thatFun.apply(thatTarget,args.concat(boundArgs))
}
return bound;
}
let f=fun.bindfun(o1,1);
f(2)
接下來(lái)看例子3:
var obj = {
name: '軒轅Rowboat',
};
function original(a, b){
console.log('this', this); // original {}
console.log('typeof this', typeof this); // object
this.name = b;
console.log('name', this.name); // 2
console.log('this', this); // original {name: 2}
console.log([a, b]); // 1, 2
}
var bound = original.bind(obj, 1);
var newBoundResult = new bound(2);
console.log(newBoundResult, 'newBoundResult'); // original {name: 2}
從例子3種可以看出this
指向了new bound()
生成的新對(duì)象表窘。
可以分析得出結(jié)論3:
1典予、bind
原先指向obj
的失效了,其他參數(shù)有效乐严。
2瘤袖、new bound
的返回值是以original
原函數(shù)構(gòu)造器生成的新對(duì)象。original
原函數(shù)的this
指向的就是這個(gè)新對(duì)象昂验。
另外前不久寫(xiě)過(guò)一篇文章:面試官問(wèn):能否模擬實(shí)現(xiàn)JS的new操作符捂敌。簡(jiǎn)單摘要:
new做了什么:
1.創(chuàng)建了一個(gè)全新的對(duì)象。
2.這個(gè)對(duì)象會(huì)被執(zhí)行[[Prototype]]
(也就是__proto__
)鏈接既琴。
3.生成的新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this占婉。
4.通過(guò)new
創(chuàng)建的每個(gè)對(duì)象將最終被[[Prototype]]
鏈接到這個(gè)函數(shù)的prototype
對(duì)象上。
5.如果函數(shù)沒(méi)有返回對(duì)象類型Object
(包含Functoin
,Array
,Date
,RegExg
,Error
)甫恩,那么new
表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象逆济。
所以相當(dāng)于new
調(diào)用時(shí),bind
的返回值函數(shù)bound
內(nèi)部要模擬實(shí)現(xiàn)new
實(shí)現(xiàn)的操作填物。
話不多說(shuō)啄骇,直接上代碼脆粥。
// 第三版 實(shí)現(xiàn)new調(diào)用
Function.prototype.bindFn = function bind(thisArg){
if(typeof this !== 'function'){
throw new TypeError(this + ' must be a function');
}
// 存儲(chǔ)調(diào)用bind的函數(shù)本身
var self = this;
// 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組
var args = [].slice.call(arguments, 1);
var bound = function(){
// bind返回的函數(shù) 的參數(shù)轉(zhuǎn)成數(shù)組
var boundArgs = [].slice.call(arguments);
var finalArgs = args.concat(boundArgs);
// new 調(diào)用時(shí),其實(shí)this instanceof bound判斷也不是很準(zhǔn)確涵卵。es6 new.target就是解決這一問(wèn)題的莱褒。
if(this instanceof bound){
// 這里是實(shí)現(xiàn)上文描述的 new 的第 1, 2, 4 步
// 1.創(chuàng)建一個(gè)全新的對(duì)象
// 2.并且執(zhí)行[[Prototype]]鏈接
// 4.通過(guò)`new`創(chuàng)建的每個(gè)對(duì)象將最終被`[[Prototype]]`鏈接到這個(gè)函數(shù)的`prototype`對(duì)象上击困。
// self可能是ES6的箭頭函數(shù),沒(méi)有prototype广凸,所以就沒(méi)必要再指向做prototype操作阅茶。
if(self.prototype){
// ES5 提供的方案 Object.create()
// bound.prototype = Object.create(self.prototype);
// 但 既然是模擬ES5的bind,那瀏覽器也基本沒(méi)有實(shí)現(xiàn)Object.create()
// 所以采用 MDN ployfill方案 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
function Empty(){}
Empty.prototype = self.prototype;
bound.prototype = new Empty();
}
// 這里是實(shí)現(xiàn)上文描述的 new 的第 3 步
// 3.生成的新對(duì)象會(huì)綁定到函數(shù)調(diào)用的`this`谅海。
var result = self.apply(this, finalArgs);
// 這里是實(shí)現(xiàn)上文描述的 new 的第 5 步
// 5.如果函數(shù)沒(méi)有返回對(duì)象類型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`)脸哀,
// 那么`new`表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象。
var isObject = typeof result === 'object' && result !== null;
var isFunction = typeof result === 'function';
if(isObject || isFunction){
return result;
}
return this;
}
else{
// apply修改this指向扭吁,把兩個(gè)函數(shù)的參數(shù)合并傳給self函數(shù)撞蜂,并執(zhí)行self函數(shù)盲镶,返回執(zhí)行結(jié)果
return self.apply(thisArg, finalArgs);
}
};
return bound;
}