js手写apply
首先我们知道apply方法是js的内置方法,同时是改变this指向的方法,话不多说,开整
1.新写的方法应该挂在Function的原型对象上,这样所有的函数实例都可以使用该方法
Function . prototype. newApply = function ( ) {
}
2.context可以理解为调用apply(context)时传递进来的对象
3.同时context保证有值,如果调用者没有传递进来,则使用window兜底
Function . prototype. newApply = function ( context ) {
context= context|| window
}
4.通过Symbol生成一个唯一的key值,这里为了理解方便,就叫它fn,这是为了避免context中含有同名属性而导致被覆盖
5.通过读取变量的形式给context[fn]赋值,请思考,这里的this是指的谁呢?这里先按下不表(答案在测试6中)
Function . prototype. newApply = function ( context ) {
context= context|| window
let fn = Symbol ( )
context[ fn] = this
}
6.本函数是可以接收参数的,但是不需要写形参,直接用arguments接收实参即可,如果对于arguments有疑惑的,可以先去学习一下函数的内置对象arguments
7.并且参照apply方法的第二个参数为一个数组或者是一个类数组对象,这里调用函数的时候判断有没有参数,有就将参数传递进去
8.接着销毁该变量
9.返回结果
Function . prototype. newApply = function ( context ) {
context= context|| window
let fn = Symbol ( )
context[ fn] = this
let result = arguments[ 1 ]
? context[ fn] ( arguments[ 1 ] ) : context[ fn] ( )
delete context[ fn]
return result
}
测试
1.测试使用两个对象,两个对象分别含有a属性和test方法,同时在test方法中打印测试数据
2.通过 let res1 = obj.fn.newApply(obj1,[1]) 去做测试
let obj = {
a : 'obj' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj'
}
}
let obj1 = {
a : 'obj1' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj1'
}
}
let res1 = obj. test. newApply ( obj1, [ 1 ] )
3.首先查看打印的res1,可以发现输出了obj对象中test函数返回的值,说明obj的test函数调用成功,如果不设置返回值则是undefined(测试图片1)
Function . prototype. newApply = function ( context ) {
console. log ( 'context' , context) ;
context= context|| window
let fn= Symbol ( )
context[ fn] = this
console. log ( context) ;
let result= arguments[ 1 ]
? context[ fn] ( arguments[ 1 ] ) : context[ fn] ( )
delete context[ fn]
console. log ( context) ;
return result
}
let obj = {
a : 'obj' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj'
}
}
let obj1 = {
a : 'obj1' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj1'
}
}
let res1 = obj. test. newApply ( obj1, [ 1 ] )
console. log ( 'res1' , res1) ;
测试图片 1
4.context为传入的obj1对象(测试图片2)
5.添加了Symbol类型的context打印结果(测试图片3)
Function . prototype. newApply = function ( context ) {
console. log ( 'context' , context) ;
context= context|| window
let fn= Symbol ( )
context[ fn] = this
console. log ( context) ;
let result= arguments[ 1 ]
? context[ fn] ( arguments[ 1 ] ) : context[ fn] ( )
delete context[ fn]
console. log ( context) ;
return result
}
let obj = {
a : 'obj' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj'
}
}
let obj1 = {
a : 'obj1' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj1'
}
}
let res1 = obj. test. newApply ( obj1, [ 1 ] )
console. log ( 'res1' , res1) ;
测试图片2
测试图片3
6.this到底是谁,又是怎样改变this指向的?(测试图片4)
6.1.此时的this是obj的test函数
6.2.this从哪里来,从obj.test.apply,也就是apply前面的那一串来的
6.3.context[fn]=this 相当于将obj对象的test函数赋值给了obj1对象的fn属性,但是并没有调用
6.4.最后result中通过contextfn 调用函数,其实就是obj2.fn(arguments),通过对象调用函数,那么函数内部的this应该指向该对象,也就是obj2
6.5**.真相大白**
apply说穿了就是将obj的test拿出来作为obj1的属性,然后再通过obj1来调用该函数,从而达到改变this指向的目的
测试图片4
7. 其他的一些测试,输出了this.a的值为obj1,在测试6中已经解释过原因,本质上通过obj1.test()调用,此时的this肯定是obj1
8.输出一下test中的arguments,可以看出之前通过
obj.fn.newApply(obj1,[1])调用传递的第2个参数的值存放到了arguments里面
扩展
1.MDN文档描述,第二个参数需要是数组或者是类数组,所以再作出一点限制
2.当传递的实参数目大于2个,则抛出异常,此处异常是apply方式调用出现这种情况的异常的仿写
3.限制第二个参数为数组或类数组,此处只做了数组的简单处理,异常抛出同样为apply方法的仿写
Function . prototype. newApply = function ( context ) {
if ( arguments. length> 2 ) throw new TypeError ( 'CreateListFromArrayLike called on non-object' )
else if ( ! Array. isArray ( arguments[ 1 ] ) ) throw new TypeError ( 'CreateListFromArrayLike called on non-object' )
console. log ( 'context' , context) ;
context= context|| window
let fn= Symbol ( )
console. log ( this ) ;
context[ fn] = this
console. log ( context) ;
let result= arguments[ 1 ]
? context[ fn] ( arguments[ 1 ] ) : context[ fn] ( )
delete context[ fn]
console. log ( context) ;
return result
}
let obj = {
a : 'obj' ,
test ( ) {
console. log ( 'this.a@@@' , this . a) ;
console. log ( arguments) ;
return '我是obj'
}
}
let obj1 = {
a : 'obj1' ,
test ( ) {
console. log ( this . a) ;
console. log ( arguments) ;
return '我是obj1'
}
}
let res1 = obj. test. newApply ( obj1, [ 1 , 2 ] )
console. log ( 'res1' , res1) ;
以上为本人的个人见解,如有问题,烦请指正