每一个函数(普通函数/构造函数/内置类)都是Function这个内置类的实例,所以:函数._proto_===Function.prototype,函数可以直接>调取Function原型上的方法;
call / apply / bind
- 原型上提供的三个公有属性方法
- 每一个函数都可以调用这个方法执行
- 这些方法都是用来改变函数中的THIS指向的
function fn(){}
fn.call(); //=>fn函数基于原型链找到Function.prototype上的call方法,并且让其执行(执行的是call方法:方法中的this是fn)
fn.call.call(); //=>fn.call就是Function.prototype上的call方法,也是一个函数,只要是函数就能用原型上的方法,所以可以继续调用call来执行;
Function.prototype.call = function $1(){
//...
}
fn.call => $1
fn.call() => $1() this:fn
fn.call.call() => $1.call() => 继续让call执行,this:$1
实例.方法():都是找到原型上的内置方法,让内置方法先执行(只不过执行的时候做了一些事情会对实例产生改变,而这也是这些内置方法的作用),内置方法中的THIS一般都是当前操作的实例
call
语法: 函数.call([context],[params1],...)
函数基于原型链找到Function.prototype.call这个方法,并且把它执行,在call方法执行的时候完成了一些功能;
1、让当前函数执行;
2、把函数中的THIS指向改为第一个传递给call的实参;
3、把传递给call其余的实参当做参数信息传递给当前函数;
如果执行call一个实参都没有传递,非严格模式下是让函数中的this执行window,严格模式下指向undefined;
//需求: 让FN执行的时候,方法中的THIS指向obj;
window.name = 'window';
let obj = {
name:'obj'
}
let fn = function(n,m){
console.log(this.name);
}
fn.call(obj); // obj
fn.call(); //一个实参都没有传递,非严格模式下是让函数中的this执行window,严格模式下指向undefined;
fn.call(null) //=> this:window 严格下是NULL(第一个参数传递的是null/undefined/不传,非严格模式下this指向window,严格模式下传递的是谁this就是谁,不传this是undefined;
fn.call(obj,1,2) //this指向obj,u=1,m=2;
fn() //window
// fn(); //window
// obj.fn(); // Uncaught TypeError: obj.fn is not a function
//因为obj中没有fn这个属性;
obj.fn = fn;
obj.fn(); // => obj
delete obj.fn;
console.log(obj)
练习题
function fn1(){console.log(1);}
function fn2(){console.log(2);}
fn1.call(fn2);
fn1.call.call(fn2);
Function.prototype.call(fn1);
Function.prototype.call.call(fn1);
apply方法
和call方法一样,都是把函数执行,并且改变里面的this关键字的,唯一的区别就是传递给函数参数的方式不同
- call是一个个传参
- apply是按照数组传参
let obj={name:'OBJ'};
let fn=function(n,m){
console.log(this.name);
}
//=>让fn方法执行,让方法中的this变为obj,并且传递10/20
fn.call(obj,10,20);
fn.apply(obj,[10,20]);
bind方法
和call/apply一样,也是用来改变函数中的this关键字的,只不过基于bind改变this,当前方法并没有被执行,类似于预先改变this;
let obj={name:'OBJ'};
function fn(){
console.log(this.name);
}
document.body.onclick=fn; //=>当事件触发,fn中的this:BODY
//=>点击BODY,让FN中的THIS指向OBJ
//document.body.onclick=fn.call(obj); //=>基于call/apply这样处理,不是把fn绑定给事件,而是把fn执行后的结果绑定给事件
document.body.onclick=function(){
//this:BODY
fn.call(obj);
}
document.body.onclick=fn.bind(obj); //=>bind的好处是:通过bind方法只是预先把fn中的this修改为obj,此时fn并没有执行呢,当点击事件触发才会执行fn(call/apply都是改变this的同时立即把方法执行) =>在IE6~8中不支持bind方法 预先做啥事情的思想被称为“柯理化函数”
内置call/apply/bind实现的办法
~ function () {
/*生成随机函数名:时间戳的方式*/
function queryRandomName() {
let time = new Date().getTime();
return '$ran' + time;
};
/*
* call:改变函数中的THIS指向
* @params
* context 可以不传递,传递必须是引用类型值(因为后面要给它加$fn的属性)
*/
/*模拟CALL方法改变函数中的THIS*/
function changeThisCall(context = window, ...ary) {
let _this = this,
result = null,
ran = queryRandomName();
context[ran] = _this;
result = context[ran](...ary);
delete context[ran];
return result;
};
//APPLY方法
function changeThisApply(context, arg = []) {
let _this = this,
result = null,
ran = queryRandomName();
context[ran] = _this;
result = context[ran](...arg);
delete context[ran];
return result;
}
// =>bind方法
//=>bind方法在IE6~8中不兼容,接下来我们自己基于原生JS实现这个方法
function myBind(context=window,...arg) {
let _this = this,
outerArg = arg;
return function anonymous(...arg) {
let innerArg = arg;
_this.apply(context, outerArg.concat(innerArg));
}
};
// 把方法挂在函数的原型上;
["changeThisCall", "changeThisApply","myBind"].forEach(item => {
Function.prototype[item] = eval(item);
});
}();
call练习题解析