1.使用方法
一个小例子,解释call,apply,bind的使用
var value = '外面的值'
function consoleLogVal(x,y){
console.log(this,"this指向")
console.log(this.value)
console.log(`x:${x},y:${y}`)
}
var callObj = {
value:'callObj'
}
var bindObj = {
value:'bindObj'
}
var applyObj = {
value:'applyObj'
}
consoleLogVal('normal1','normal2');
consoleLogVal.call(callObj,'call1','call2');
consoleLogVal.bind(bindObj,'bind1','bind2')();
consoleLogVal.apply(applyObj,['apply1','apply2'])
综上所述,我们可以知道:
- call,apply,bind可以改变this指向
- 普通调用时,this指向全局window
- 使用call,apply,bind后,this指向改为传递的第一个参数
- call,bind传递参数是逗号分隔,一直向后传递,apply传递参数以数组形式
- call,apply直接调用,bind方法需要使用立即调用函数形式
2.原理解析
2.1 call实现
// 实现代码
Function.prototype.myCall = function(ctx,...args){
let handler = Symbol(); // 生成一个唯一的值,用来作为要绑定对象的属性key,存储当前调用call方法的函数
if(typeof this !== 'function'){
throw this+'.myCall is not a function'
}
// 若第一个参数为引用类型或者null
if(typeof ctx === 'object' || ctx === 'function'){
// 如果为null,则this为window
ctx = ctx || window
}else{
// 如果为undefined,则this绑定为window
if(typeof ctx === 'undefined'){
ctx = window
}else{
// 基本类型包装 1 => Number(1)
ctx = Object(ctx)
}
}
// this为当前调用call方法的函数
ctx[handler] = this;
// 执行这个函数,这时这个函数内部this绑定为ctx,存储函数执行后的返回值
let result = ctx[handler](...args)
// 删除对象上面的函数
delete ctx[handler]
// 返回值
return result
}
// 使用示例:
function consoleLogVal(x,y){
console.log(this,"this指向")
console.log(this.value)
console.log(`x:${x},y:${y}`)
}
var callObj = {
value:'callObj'
}
consoleLogVal.myCall(callObj,'call1','call2');
2.2 apply实现
Function.prototype.myApply = function(ctx,argsArr){
let handler = Symbol();
if(typeof this !== 'function'){
throw this+'.myApply is not a function!'
}
let args = [];
// 判断apply函数传递的参数是不是数组形式
if(typeof argsArr === 'object' || typeof ctx === 'function' || typeof argsArr === 'undefined'){
args = Array.isArray(argsArr)? argsArr :[];
} else {
// 如果为基本类型,如果是undefined,则无效,其它类型则抛出错误。
throw 'TypeError: CreateListFromArrayLike called on non-object'
}
if(typeof ctx === 'object'){
ctx = ctx || window
}else{
if(typeof ctx === 'undefined'){
ctx = window
}else{
ctx = Object(ctx)
}
}
ctx[handler] = this; // 在对象中加入这个方法
// 执行该方法,得到返回值
let result = ctx[handler](...args)
// 删除新增的方法,恢复本来样貌的对象
delete ctx[handler]
return result
}
var applyObj = {
value:'applyObj'
}
consoleLogVal.myApply(applyObj,['apply1','apply2'])
2.2 bind实现
我们根据使用结果可知,在使用bind函数时,需要自执行一下,所以返回的是一个函数
Function.prototype.myBind=function(context,...args) {
// 这里的this为调用bind方法的函数。
let thisFunc=this;
// 如果调用bind的变量不是Function类型,抛出异常。
if(typeof thisFunc!=='function') {
throw new TypeError('Function.prototype.bind - '+
'what is trying to be bound is not callable');
}
// 定义一个函数boundF
// 下面的”新函数“ 均为函数调用bind方法之后创建的函数。
let boundF=function() {
// 这里的 arguments 为函数经过bind方法调用之后生成的函数再调用时的实参列表
let boundFAgrs=arguments;
// 把调用bind方法时除第一个参数外的参数集合与新函数调用时的参数集合合并。当做参数传递给call方法
let totalAgrs=[...args,...arguments];
// 如果调用新函数时存在新的this,并且新的this不是全局对象,那么我们认为这里想要更改新函数this的绑定。因此让新函数的内部this绑定为当前新的this。
thisFunc.call(this && this !== window ? this : context,...totalAgrs);
}
//通过原型链继承的方式让原函数的原型和新函数的原型,都在通过new关键词构造的新对象的原型链上
// b instanceof 原函数 -> true
// b instanceof 新函数 -> true
var F=function() {};
F.prototype=thisFunc.prototype;
boundF.prototype=new F();
return boundF;
}
var bindObj = {
value:'bindObj'
}
consoleLogVal.myBind(bindObj,'bind1','bind2')();
学习用法,希望可以帮助到你,
我是Ably,你无须超越谁,只要超越昨天的自己就好~