JavaScript(call、apply)|青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第7天
call、apply、bind 区别
共同点
- 功能一致,可以改变this指向
- 语法都是函数
区别
- 1.call() apply() 可以立即执行。 bind不会立即执行,bind返回的是一个函数,不会立即执行
- 2.参数不同: apply 第二个参数是数组。 call和bind有多个参数,需要一个一个写
手写实现call方法
首先要明白call、apply、bind 哪里来的?每个JavaScript函数其实都是Function对象,而function对象是构造函数,
构造函数是有原型对象的,也就是function.prototype
我们想要实现,就需要在prototype中添加新的和call一样的属性(方法)
call()
使用call进行this绑定,就相当于做了this的隐式绑定,在对象里添加了调用this的方法
如下code1 到 code2 的变化,当函数go引用有上下文的时候,就会把函数go调用中的this
绑定到这个上下的对象 MyObj 中了
// code1
function go() {
console.log('练习两年半的'+this.name);
}
var MyObj = {
name: '个人练习生',
}
go.call( MyObj );
/********************* */
// code2
var MyObj = {
name: '个人练习生',
go: function() {
console.log('练习两年半的'+this.name);
}
}
MyObj.go();
现在开始在新的call函数中完成这个效果(往原型对象中添加新的方法)
function go() {
console.log('练习两年半的'+this.name);
}
var MyObj = {
name: '个人练习生'
}
Function.prototype.newCall = function(obj) {
console.log(this);
}
go.newCall(MyObj);
上述代码的输出是?
返回的是go函数,而不是MyObj对象,因为在执行newCall时,遇到的this是在go中执行的,因此返回的是 go 函数
现在如果在newCall里 给obj添加一个函数,赋值为go,并且执行一遍,是不是就是把this绑定给传入的obj了呢?
function go() {
console.log('练习两年半的'+this.name);
}
var MyObj = {
name: '个人练习生'
}
Function.prototype.newCall = function(obj) {
// 改动的代码 start
obj.f = this; // 给obj重新添加了一个方法 赋值为 this(go()方法)
obj.f(); // 执行调用
delete obj.p; // 不需要真的添加,执行完毕后删除
// 改动的代码 end
}
go.newCall(MyObj);
call的其他参数的实现,使用解构数组的方法
function go(a,b,c,d) {
console.log('练习两年半的 '+this.name);
console.log(a,b,c,d);
}
var MyObj = {
name: '个人练习生'
}
Function.prototype.newCall = function(obj) {
obj.f = this; // 给obj重新添加了一个方法 赋值为 this(go()方法)
var newArr = []; // 创建一个新数组来存放 arguments
for(var i=1;i<arguments.length;i++) { // 从1开始是因为第一个参数是 this
newArr.push(arguments[i]);
}
console.log(newArr);
obj.f(...newArr); // 执行调用
delete obj.p; // 不需要真的添加,执行完毕后删除
}
go.newCall(MyObj,'唱','跳','rap','篮球');
看起来已经非常完美了,但是,距离我们实现自己的call函数还有一个的问题,如果我们第一个参数传入null,就会报错,但是原生的call则不会
// 添加如下代码
var obj = obj || windows;
最终版本
使用es6语法简化后,并给函数一个返回值
function go(a,b,c,d) {
return {
name: this.name,
a:a,
b:b,
c:c,
d:d
}
}
const MyObj = {
name: '个人练习生'
}
Function.prototype.newCall = function(obj,...arr) {
let newObj = obj || windows;
newObj.f = this; // 给obj重新添加了一个方法 赋值为 this(go()方法)
const result = newObj.f(...arr); // 执行调用
delete newObj.f; // 不需要真的添加,执行完毕后删除
return result;
}
const a = go.newCall(MyObj,'唱','跳','rap','篮球');
console.log(a);