这是老生常谈的手写了,今天想自己试着实现一下,做个笔记。
call 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Function .prototype.myCall = function (context ) { if (context === undefined || context === null ) { context = window } else { context = Object (context) } const fn = Symbol ('uniqueFn' ) context[fn] = this let arg = [...arguments].slice(1 ) let result = context[fn](...arg) delete context[fn] return result }
判断函数上下文网上也有写法是:
1 2 context = context ? Object (context) : window context = context || window
但不是很严谨,因为遇到空字符串,0
,false
时,context
也会被判断为window
。
apply 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Function .prototype.myApply = function (context ) { if (context === undefined || context === null ) { context = window } else { context = Object (context) } function isArrayLike (o ) { if ( o && typeof o === 'object' && isFinite (o.length) && o.length >= 0 && o.length === Math .floor(o.length) && o.length < Math .pow(2 , 32 ) ) { return true } return false } const fn = Symbol ('uniqueFn' ) context[fn] = this let args = arguments [1 ] let result if (!Array .isArray(args) && !isArrayLike(args)) { throw new Error ('CreateListFromArrayLike called on non-object' ) } else { args = Array .from(args) result = context[fn](...args) } delete context[fn] return result }
bind 方法 实现 bind 要额外考虑一个问题:方法一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Function .prototype.myBind = function (context ) { if (typeof this !== 'function' ) { throw new Error ('Expected this is a function' ) } let self = this let args = [...arguments].slice(1 ) let fn = function (...innerArg ) { const isNew = this instanceof fn return self.apply(isNew ? this : Object (context), args.concat(innerArg)) } if (self.prototype) { fn.prototype = Object .create(self.prototype) } return fn }