先分析下3个方法的作用
改变this指向
首先我们知道,对象上的方法,在调用时,this是指向对象的。
ler 0 = {
fn:function(){
console.log(this);
}
}
知道了这点,我们就可以实现改变this的指向了。
// 函数原型上添加myCall方法,来模拟call
Function.prototype.myCall = function(obj){
// 我们要让传入的obj成为,函数调用时的this值,
obj._fn_ = this; //在obj上添加_fn_属性,值是this(需调用此方法的那个函数对象)
obj._fn_(); // 在obj上调用函数,那函数的this值就是obj
delete obj._fn_; //删除obj的_fn_的属性,去除影响。
// _fn_ 只是个属性名,你可以随意起名,但是要注意可能会覆盖obj上本来就有的属性。
}
下面测试一下。
let test = {
name:'test',
}
let o = {
name:'o',
fn:function(){
console.log(this.name,...arguments);
}
}
o.fn();// "o"
o.fn.call(test);//
o.fn.myCall(test)
现在,改变this的值,实现了。
传入参数
Function.prototype.myCall = function(obj,arguments){
let obj._fn_ = this;
obj._fn_(...arguments);
delete obj._fn_;
}
let test = {
name:'test'
}
let o = {
name:'o',
fn(){
console.log(this.name,...arguments);
}
}
o.fn();// o
o.fn.myCall(test,1,2,3)// 0,1,2,3
o.fn.call(test,1,2,3)// 0,1,2,3
不用ES6就比较麻烦了
* 用eval方法
eval方法,会传入的字符串,当做JS代码进行解析,执行。
Function.prototype.myCall = function(obj){
var arg = [];
for(let i = 1; i < arguments.length; i++){
arg.push('arguments['+i+]');
*******
// 这里要push 这行字符串 而不是直接push值
// 因为直接push值会导致一些问题
// 例如:push一个数组[1,2,3]
// 在下面 eval调用时,进行字符串拼接,JS为了将数组转换为字符串
// 会去调用数组的toString()方法,变为'1,2,3'就不是一个数组了,相当于是3个参数
// 而push这行字符串,eval方法,运行代码会自动去arguments里获取值,eval会自动去除数组的外框。
******
}
obj._fn_ = this;
eval('obj._fn_('+arg+')')//字符串拼接,JS会调用arg数组的toString方法,这样就传入了所有参数。
delete obj._fn_;
}
// 测试
let test = {
name:'test',
}
let o = {
name:'o',
fn:function(){
console.log(this.name,...arguments);//这里把参数显示一下
}
}
o.fn(1,['a','b'],3) // "o" 1 ["a","b"] 3
o.fn.call(test,1,['a','b'],3) // "test" 1 ["a","b"] 3
o.fn.myCall(test,1,['a','b'],3) // "test" 1 ["a","b"] 3
// 没问题
返回函数值
很简单,变量保存一下
Function.prototype.myCall = function(obj,...arg){
let val ;
obj._fn_ = this;
val = obj._fn_(...arg); //不能直接return obj._fn_(...arg) 这样就不delete属性了
delete obj._fn_;
return val;
}
Function.prototype.myCall = function(obj){
let arg = [];
let val ;
for(let i = 1 ; i<arguments.length ; i++){ // 从1开始
arg.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接,JS会调用arg数组的toString()方法,这样就传入了所有参数
delete obj._fn_;
return val;
}
传参检测
传入的obj如果是null,undefined应该为window。如果是string,number,boolean应该转换为对象。
* 介意自己加入一下判断,为了方便观看,我就先不加了。
if(obj === null||obj === undefined){
obj = window;
}else{
obj = Object(obj);
}
其实apply和call差不多,没多大区别
* 利用已经写好的myCall来实现
// ES6
Function.prototype.myApply = function(obj,arr){
let args = [];
for(let i = 0 ; i// 其实直接 ...arr 传参也可以 但是效果就和aplly有微小差别了
return this.myCall(obj, ...args);
}
// ES3
Function.prototype.myApply = function(obj,arr){
let args = [];
for(let i = 0 ; i'arr[' + i + ']' ); // 这里也是push 字符串
}
return eval( 'this.myCall(obj,' + args + ')' );
}
Function.prototype.myApply = function(obj,arr){
let args = [];
let val ;
for(let i = 0 ; i'arr[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + args + ')' )
delete obj._fn_;
return val
}
Array.apply({},{length:3});
// 返回 [undefined, undefined, undefined]
Array.myApply({},{length:3});
// 返回 [undefined, undefined, undefined
****返回正常****
Function.prototype.myBind = function(obj,...arg1){ //rest参数
return (...arg2)=>{ //返回箭头函数,this绑定调用这个方法myBind的函数对象。这个和箭头函数的指向有关
return this.myApply(obj,arg1.concat(arg2)) //将参数合并
}
}
// 其实没什么说的
Function.prototype.myBind = function(obj,...arg1){
return (...arg2) => {
let args = arg1.concat(arg2);
let val ;
obj._fn_ = this;
val = obj._fn_( ...args );
delete obj._fn_;
return val
}
}
测试下
let test = {
name:'test'
}
let o = {
name:'o',
fn:function(){
console.log(this.name, ...arguments); //这里把参数显示一下
}
}
//myBind
b = o.fn.myBind(test,1,2)
b() // "test" 1 2
b(3,4) // "test" 1 2 3 4
// bind
b = o.fn.bind(test,1,2)
b() // "test" 1 2
b(3,4) // "test" 1 2 3 4
三个方法的我写的代码
Function.prototype.myCall = function(obj){
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
let arg = [];
let val ;
for(let i = 1 ; i<arguments.length ; i++){
arg.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + arg + ')' )
delete obj._fn_;
return val
}
Function.prototype.myApply = function(obj,arr){
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
let args = [];
let val ;
for(let i = 0 ; i'arr[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + args + ')' )
delete obj._fn_;
return val
}
Function.prototype.myFind = function(obj){
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
let _this = this;
let argArr = [];
let arg1 = [];
for(let i = 1 ; i<arguments.length ; i++){
arg1.push( arguments[i] );
argArr.push( 'arg1[' + (i - 1) + ']' ) ;
}
return function(){
let val ;
for(let i = 0 ; i<arguments.length ; i++){
argArr.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = _this;
console.log(argArr);
val = eval( 'obj._fn_(' + argArr + ')' ) ;
delete obj._fn_;
return val
};
}
作者:泡沫的快乐
链接:https://www.jianshu.com/p/3b69fb0d4c2f
來源:简书