js中的call与apply
本文记录下call、apply、bind方法的作用及其区别。<!--more-->
this
js函数中的this指的是函数调用的上下文,可以理解为正常的函数调用方式是 fu.call(context)
,函数其他的调用方式都是这种方式的语法糖。context默认是undefined,在浏览器中规定全局函数当context为undefined或null,那么context就是window,也即this默认绑定。
绝大部分情况下,this与函数调用的方式有关,运行时绑定。
箭头函数的this是编译时绑定,指向调用位置最近的词法作用域,对象不构成词法作用域,词法作用域规定的是哪些代码才能够访问。
每个函数在被调用时都会自动获取两个特殊变量:this和arguments。内部函数在搜索这两个变量时只会搜索到其活动对象为止,不可能直接访问外部函数中的这两个变量。但可以通过将外部作用域中的this(arguments)对象保存在一个闭包能访问到的变量里替代匿名函数中的this(arguments)。
var obj = {
param: 123,
ff : () => console.log(this), // window, strict模式下是undefined
}
var obj = {
param: 123,
ff : function () {
(() => console.log(this)))() // obj
return () => console.log(this) // obj
},
}
显式改变this
call、apply、bind都可以改变上下文即重定义this对象(函数中的this对象改变为传入的某个参数),但是call、apply是立即执行,而bind是返回一个改变上下文的函数副本,原来函数不发生变化。
apply方法接收两个参数,一个是函数运行的作用域,另一个是参数数组;call方法与apply方法不同之处在于传递给函数的参数必须列举出来。bind参数方式同call相同。
拓展
apply可以将一个数组默认转换成一个参数列表
- 可以使用
Math.max
得到数组中最大项:Math.max.apply(null,array)
Math.min
得到数组中最小项。Array.prototype.push
合并两个数组:Array.prototype.push.apply(arr1,arr2)
模拟实现call和apply
可以从一下几点考虑来实现:
- 不传入第一个参数,默认为
window
- 改变了this指向,让新的对象可以执行该函数,那么思路就变成了给新的对象添加一个函数,然后再执行完以后删除。
call:
Function.prototype.myCall = function (context) {
var context = context || window;
// 给context添加一个属性
context.fn = this; // 这里this就是指向调用的函数
var args = [...arguments].slice(1); // call传递给函数的必须列举出来
var result = context.fn(...args);
delete context.fn;
return result;
}
apply:
Function.prototype.myApply = function (context) {
var context = context || window;
context.fn = this;
var result;
if (arguments[1]) { // apply传递的是参数数组
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn;
return result;
}
bind:
Function.prototype.myBind = function (context) {
if (typeof this != 'function) {
throw new TypeError('Error');
}
var _this = this;
var args = [...arguments].slice(1);
// 返回一个函数
return function F() {
// 因为返回一个函数,可以使用 new F(),需要判断
if (this instanceof F){
return new _this(...args, ...arguments);
}
return _this.apply(context,args.concat(...arguments));
}
}
this 指向问题
var length = 0;
function fn(){
console.log(this.length);
}
var obj = {
length: 6,
method: function(){
fn();
}
}
var objBind = {
length: 7,
method: () => {
fn()
}
}
obj.method(); // 输出?
objBind.method();
// 箭头函数的 this 可以理解为动态绑定,与调用者无关