call-apply
call 和 apply 方法在 指定this 和 一个或多个参数的前提下执行函数
- call和apply的区别在于,apply方法传递参数是数组形式的
1. 调用call或者apply方法的都是函数。
因此第一步要先判断它是否为函数类型,不是则抛出错误:
TypeError: xxx.call is not a function
2. 给参数中指定的thisArg新增一个属性去记录执行的函数(this)
(获取调用bind 或 apply方法的函数,call函数内部的 this正指向它)
通过 2 就可以解决指定this了,那如何将指定的参数传递给执行的函数呢 ?
(通过字符串的拼接和eval即可解决此问题)
3. 拼接参数
4. 用eval去执行拼接好的参数和函数
// 非严格模式下
Function.prototype.call2 = function(thisArg) {
if (typeof(this) !== 'function') {
throw new Error('TypeError: ' + this.name + '.call is not a function')
}
thisArg = thisArg | window
thisArg.func = this
var args = []
var len = arguments.length
for (var i = 1; i < len; i ++) {
args.push('arguments[' + i + ']')
}
var res = eval('thisArg.func(' + args.toString() + ')')
delete thisArg.func
return res
}
- apply因为指定参数的形式为数组,那么就不用字符串拼接参数了
// 非严格模式下
Function.prototype.apply2 = function(thisArg,arr) {
if (typeof(this) !== 'function') {
throw new Error('TypeError: ' + this.name + '.call is not a function')
}
thisArg = thisArg | window
thisArg.func = this
var res = eval('thisArg.func(' + arr.toString() + ')')
delete thisArg.func
return res
}
create
create 方法返回了指定原型的实例对象
- 参照继承中的圣杯模式,通过新建一个对象作为中转站,从而实现互不影响的继承
Object.create2 = function(thisArg) {
var F = function(){}
F.prototype = thisArg
return new F()
}
bind
bind 方法返回了一个指定 this 和 传递了初始参数 的绑定函数
- 注意:如果返回的绑定函数作为构造函数时,指定的this失效,并且它的实例对象能继承(互不影响)原函数的原型
- 在bind方法的实现中,可以通过前面的 apply和call方法来指定this 和 初始参数以及后续传入绑定函数的参数
1. 判断调用bind方法的是否为函数类型,如果不是,则抛出错误
TypeError: Bind must be called on a function
2. 记录原函数以及指定的thisArg
记录初始参数以及绑定函数中传入的后续参数
3. 创建绑定函数,该函数内部调用原函数,返回它的返回值。
用apply方法改变this指向以及传入初始参数和后续参数。
而指定的this需要判断绑定函数是否作为构造函数,
作为构造函数时this指向该实例对象,否则指向window(直接调用)
4. 实现实例对象继承原函数的原型
5. 返回绑定函数
// 非严格模式下
Function.prototype.bind2 = function(thisArg) {
if (typeof(this !== 'function')) {
throw new Error('TypeError: Bind must be called on a function')
}
thisArg = thisArg | window
var self = this // 记录原函数
var args = Array.prototype.slice.call(arguments,1) // 记录初始参数
var fBound = function() {
var boundArgs = Array.prototype.slice.call(arguments,1) // 记录后续参数
return self.apply(this instanceof fBound ? this : thisArg,args.concat(boundArgs))
}
/*
var F = new Function(){
F.prototype = self.prototype
fBound.prototype = new F()
*/
fBound.prototype = Object.create(self.prototype)
return fBound
}