Function.prototype.call()
先看下call的表现,第一个🌰
1 | var cat = { |
实现:
第一版本实现:
1 | Function.prototype.myCall = function () { |
这里将传入的ctx
添加了eat
方法,并不是我想要的,于是我想到了创建一个新的对象。
第二版本实现:
我得为传入的对象创建一个副本,马上想到Object.create()方法
,这个方法可以让我通过obj.__proto__
访问到原型上的我需要用到的其他属性,又不会改变原来的对象,并且在调用过obj.fn()
之后,obj
就会被回收销毁,看没有副作用。但是之后出现了使用这个方式创建副本的缺点。
1 | Function.prototype.myCall = function () { |
第二个🌰,为call方法加入多个参数
1 | function Parent(a,b,c){ |
第三版本实现,稍微改造一下myCall
方法
1 | Function.prototype.myCall = function () { |
发现问题了,打印的child
只有属性d
,这是因为在调用Parent.myCall(this,a,b,c);
的时候,传入的this问题,如图:
但是在myCall
方法里,创建的obj.__proto__ = ctx
,而obj.fn()
调用的时候,Parent方法里this指向这个新创建的对象,如图:
所以使用Object.create()
方法似乎不是一个很好的选择
第四版本实现:改变一下myCall
方法中创建对象的方式
1 | Function.prototype.myCall = function () { |
Object()
与new Object()
是一样的。稍微测试一下,看起来似乎没什么问题
看下一个🌰,发现一个有趣事情
1 | console.log(window.a);//undefined |
看下MDN的介绍得知,当传入的第一个参数为null或者undefined,就会默认指向全局,也就导致了上面的这些值暴露到了window对象上。
上面的实现使用了展开运算符,虽然大部分浏览器都支持es6,但是总是感觉还是得用个什么东西来替代一下。
首先,看一下展开运算符通过Babel转成es5的代码
1 | var arr = [1,2,3]; |
然后发现它是通过apply展开的。
然后想到了几个蠢办法,于是想想我还是放弃了,不过首先了解到arguments是个类数组,缺少Array的方法,可以将它转成数组。
1 | Array.prototype.slice.call(arr); |
同时arguments是可以枚举的。
Function.prototype.apply()
这个方法与call方法只是接收参数不一样,其他基本一致,稍微改造一下myCall
方法成myApply
方法
1 | Function.prototype.myApply = function () { |
我如此大胆的直接使用 [...arguments].slice(1)[0]
,是因为将arguments展开,即使没有传入参数,也是个空数组,调用slice方法会返回一个数组,我可以直接取第一个数据
1 | function test(){ |
Function.prototype.bind()
举个🌰
1 | function getFood(){ |
实现myBind
方法:
1 | Function.prototype.myBind = function () { |
总结
1.在严格模式下和Node环境,是没有window对象的。
2.ctx.fn可能会覆盖之前的同名fn方法,所以最好加个单独标识。
###
附件题
1 | var big = '123'; |