1.区别
- apply、call、bind 三者都是用来改变函数的 this 对象的指向的;
- apply、call、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
- apply、call 两者都可以利用后续参数传参; 但是传参的方式不一样,apply是数组,call是正常传参形式。
- bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用
2. bind
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
let obj = {};
function test(...params){
console.log(this === obj); //true
console.log('我接收第二个以后的参数:'+params)
}
let result = test.bind(obj,1,'aaaa',[4,5]);
result();
手写bind
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
2. apply
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。
function Person(name,age){
this.name = name;
this.age = age;
}
function Student(name,age,grade){
//apply用法
Person.apply(this,arguments);//arguments指该函数的所有参数(数组)
//call用法
//Person.call(this,name,age);
this.grade = grade
}
let student = new Student('猪猪侠',12,'一年级')
console.log(student.name); //猪猪侠
手写apply
Function.prototype.myApply = function(context){
if(typeof this !== 'function'){
throw new TypeError('not function')
}
context = context || window;
context.fn = this;
let result;
//arguments是JS内置对象,取函数形参arguments[0]指第一形参this指向,arguments[1]剩余参数
//如果arguments[0]是null、undefind,this指向window
if(arguments[1]){
result = context.fn(...arguments[1]); //相当于push(...[1,2,3]),result是数组长度
}else{
result = context.fn();
}
delete context.fn
return result
}
let arr1 = [1,2,3];
let arr2 = [];
arr2.push.myApply(arr2,arr1);
console.log(arr2) //[1, 2, 3]
手写apply es6版
Function.prototype.myApply = function (context, args=[]) {
if(typeof this !== 'function'){
throw new TypeError('not function')
}
context = context || window; //这里默认不传就是给window,也可以用es6给参数设置默认参数
const key = Symbol(); //给context新增一个独一无二的属性以免覆盖原有属性
context[key] = this
let result = context[key](...args);//通过隐式绑定的方式调用函数
delete context[key];//删除添加的属性
return result; //返回函数调用的返回值
}
3. call
call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
手写call
Function.prototype.myCall = function(context){
if(typeof this!== 'function'){
throw new TypeError('not function')
}
context = context || window;
context.fn = this;
let arr = [...arguments].slice(1); //删除第一个参数(用于改变this指向)
let result = context.fn(...arr);
delete context.fn
return result
}
let arr = [];
arr.push.myCall(arr,1,2,8,9);
console.log(arr) //[1, 2, 8, 9]
手写call es6版
Function.prototype.myApply = function (context, ...args) {
if(typeof this !== 'function'){
throw new TypeError('not function')
}
context = context || window; //这里默认不传就是给window,也可以用es6给参数设置默认参数
const key = Symbol(); //给context新增一个独一无二的属性以免覆盖原有属性
context[key] = this
let result = context[key](...args);//通过隐式绑定的方式调用函数
delete context[key];//删除添加的属性
return result; //返回函数调用的返回值
}