首先我要先搞懂这三个方法怎么用
1. call( )
语法
fun.call(thisArg, arg1, arg2, ...)
第一个参数理解为要在哪里执行fun(运行时指定的this值,他不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象),后面的一串参数意思是执行fun时的传参
返回值是你调用的方法(fun)的返回值,若该方法没有返回值,则返回undefined
实现call开始
// 首先没有给一个传入参数形式context
Function.prototype._call(context){
// 就要判断实际调用有没有这个参数
let context = context ? context : window
// 此时这个context就替代了上面的target
context.func = this
let result = context.func(args)
// 拿参数这步省略了,无错,按照我自己的写法这样也可以
/*
let [target, ...args] = [...arguments]
判断了传入的目标对象是null等情况,再赋值window实际也能成功
if(target == null || undefined){
target = window;
}
*/
return result
// 别忘记删除方法,这句写在return前面
delete context.func
}
// 最终我自己不看答案写出的结果如下,实测可以 不知道有没有潜在问题
Function.prototype._call = function(){
// let cont = context ? context : window
let [target, ...args] = [...arguments]
if(target == null || undefined){
target = window;
}
target.fn = this
let result = target.fn(...args)
delete target.fn
return result
}
2.apply( )
语法
fun.apply(obj,args)
第一个参数仍然理解为要在哪里执行fun,第二个参数是一个数组。
两种方法意思是一样的,只是传参方式不同。call后面可以给很多,而apply就只能给一个数组,在实际情况中按需使用吧。(我今天看懂继承再看看能多说点什么,还有试一下apply的返回是不是一样)
实现apply开始
Function.prototype._apply = function(){
// 一切和call都差不多
let [target, ...args] = [...arguments]
// 考虑不传参 作用域的情况
if(target == null || undefined){
target = window
}
target.fn = this
let result
// 考虑参数存在情况。
if(args){
// 这里踩坑了,实际调用过程还是会把数组转化成非数组的 要加上...
result = target.fn(...args)
}else{
result = target.fn()
}
delete target.fn
return result
// 有了call做铺垫基本还行,主要是判断args是否存在,还有参数是 数组的问题
}
3.bind( )
bind 是返回新的函数,以便稍后调用;apply 、call 则是立即调用原函数。也就是说 要定义一个新的变量 把bind返回的方法赋予给这个变量再去执行
语法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
第一个参数我已经可以理解了,这里有一个新特性,就是如果bind返回的函数以new的形式去调用,第一个参数会被忽略,this仍然指向被调用的函数(fun)
arg1, arg2, … (可选)当绑定函数被调用时,要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
也就是说bind可以实现预设一些函数参数的功能(有啥用)
以下有两个例子来表达上面两点,搜来的:
function original(x){
this.a=1;
this.b =function(){return this.a + x}
}
var obj={
a:10
}
var newObj = original.bind(obj,2) //传入了一个实参2
var newObjChild = new newObj()
console.log(newObj.a) //输出 1, 说明返回的函数用作构造函数时obj(this的值)被忽略了
console.log(newObj.b()) //输出3 ,说明传入的实参2传入了原函数original
var sum = function(x,y) { return x + y };
var succ = sum.bind(null, 1); //让this指向null,其后的实参也会作为实参传入被绑定的函数sum
succ(2) // => 3: 可以看到1绑定到了sum函数中的x
另外的,bind和函数柯里化好像有点关系,一会儿回家讨论下
实现bind开始
bind好难我自己写不出来,只好一遍一遍看了
Function.prototype._bind = function(context){
if(typeof this !== 'function'){
throw new TypeError('被绑定的对象需要是函数')
}
var self = this // 保存起原指针(作用域)
var args = [].slice.call(arguments, 1)
console.log(arguments)
// =====> bind被调用时的传入参数
fBound = function(){ // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
console.log(arguments)
// =====> bind返回的函数fBound被调用时传入的参数
return self.apply(this instanceof fBound ? this : context, args.concat([].slice.call(arguments)))
/* 如果被Fbound被用作了构造函数,
构造出的实例一定是Fbound类型,
如果没有,那么当前的this和Fbound没有任何关系。
参数为bind被调用时和Fbound被调用时的参数联合 */
}
// -----------------------------------------------
var func = function(){} // 这个到底是干嘛的??
//维护原型关系
console.log(this.prototype) // 一个constructor指向原函数
console.log(func.prototype) // 什么都没有
if(this.prototype){
func.prototype = this.prototype
}
//使fBound.prototype是func的实例,返回的fBound若作为new的构造函数,新对象的__proto__就是func的实例
fBound.prototype = new func()
// ------------------------------框住的我先不看了 不能理解
return fBound
}
留一个最新的MDN的polyfill
Function.prototype.bind = function() {
var thatFunc = this, thatArg = arguments[0];
var args = slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - ' +
'what is trying to be bound is not callable');
}
return function(){
var funcArgs = args.concat(slice.call(arguments))
return thatFunc.apply(thatArg, funcArgs);
};
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。