1.apply
apply()方法指定this值和参数(参数以数组或者类数组对象的形式存在)的情况下调用某个函数,意思是它可以改变一个函数的执行环境,语法:
fn.apply(thisArg[, argsArray])
- thisArg: 在fn函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定null或undefined时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
- argsArray: 一个数组或者类数组对象,其中的数组元素作为单独的参数传给fn函数。如果该参数为null或undefined,则表示不传入任何参数。从ECMAScript 5开始可以使用类数组对象。
function fn (a,b,c) {
console.log(this.name)
console.log(a, b, c)
}
let obj = {name: '李华'}
fn.apply(obj, [1,2,3])
// 输出结果:
//李华
//1 2 3
2.call
call和apply作用一样,不同的是两者的参数传入不一样,apply第一个参数和call没有区别,但是第二个参数就不同了,apply是以数组或者类数组的形式传入的,而call是将这个数组展开,一个一个的形式传入的,说的比较绕,下面我们看一下代码示例就能理解了。语法:(注意:中括号表示可选,不是数组的意思)
fn.call(thisArg[,arg1,arg2...]);
- thisArg: 和apply第一个参数一样
- arg1:作为第一个参数传给fn函数
- arg2:作为第二个参数传给fn函数
- ......依此类推
function fn (a,b,c) {
console.log(this.name)
console.log(a, b, c)
}
let obj = {name: '李华'}
fn.call(obj, 1, 2, 3)
// 输出
//李华
//1 2 3
3.bind
bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其原本的参数。 (这里需要注意bind返回的是一个函数,bind自身不会立即执行这个函数;而apply和call立即执行这个函数,返回执行后的结果) 语法:
fn.bind(thisArg[, arg1[, arg2[, ...]]])
- thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, … (可选)当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。
function fn(a, b, c) {
console.log(this);
}
var fn1 = fn.bind({a:123});
fn(); // 输出:window
fn1(); // 输出:{a:123}
手写实现
1.手写call
function foo(name, age) {
console.log(this, name, age)
}
Function.prototype.myCall = function(thisArg, ...thisArgs){
// this -> 调用的函数对象
// thisArg -> 传入的第一个参数, 要绑定的this
// console.log(this) // -> 当前调用的函数对象
// this.apply(thisArg)
// console.log(this)
// console.log(thisArg)
// thisArg.fn = this
// 判断 thisArg是不是一个对象 如果不是 需要包装成对象
thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
// 给这个 thisArg 对象 添加一个 属性 fn 然后执行这个fn
// 这个时候的 fn的this 指向就是 当前函数
Object.defineProperty(thisArg,fn,{
configurable:true,
enumerable:false,
value:this
//thisArg.fn = this = foo
})
thisArg.fn(...otherArgs) //执行
delete thisArg.fn //删除
}
foo.myCall({ name: "why" }, 123, 456)
// foo调用myCall()的时候 foo成了myCall()的this 所以代码里 foo= this = thisArg.fn
2. 手写apply
function foo(name, age) {
console.log(this, name, age)
}
Function.prototype.myCall = function(thisArg, ...thisArgs){
// this -> 调用的函数对象
// thisArg -> 传入的第一个参数, 要绑定的this
// console.log(this) // -> 当前调用的函数对象
// this.apply(thisArg)
// console.log(this)
// console.log(thisArg)
// thisArg.fn = this
// 判断 thisArg是不是一个对象 如果不是 需要包装成对象
thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
// 给这个 thisArg 对象 添加一个 属性 fn 然后执行这个fn
// 这个时候的 fn的this 指向就是 当前函数
Object.defineProperty(thisArg,fn,{
configurable:true,
enumerable:false,
value:this
//thisArg.fn = this = foo
})
thisArg.fn(...otherArgs) //执行
delete thisArg.fn //删除
}
foo.myCall({ name: "why" }, 123, 456)
// foo调用myCall()的时候 foo成了myCall()的this 所以代码里 foo= this = thisArg.fn
3. 手写bind
function foo(name, age, height, address) {
console.log(this, name, age, height, address)
}
// 实现hybind函数
Function.prototype.hybind = function(thisArg, ...otherArgs) {
// console.log(this) // -> foo函数对象
thisArg = thisArg === null || thisArg === undefined ? window: Object(thisArg)
Object.defineProperty(thisArg, "fn", {
enumerable: false,
configurable: true,
writable: false,
value: this
})
return (...newArgs) => {
// var allArgs = otherArgs.concat(newArgs)
var allArgs = [...otherArgs, ...newArgs]
thisArg.fn(...allArgs)
}
}
var newFoo = foo.hybind("abc", "kobe", 30)