call()、apply()、bind()的用法主要是纠正作用域中的this
1.call()
语法:obj1.call(obj2[,param1,param2,...])
定义:用obj2对象来代替obj1,调用obj1的方法。即将obj1应用到obj2上。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 obj2 指定的新对象。 如果没有提供 obj2参数,那么 Global 对象被用作 obj2。
原理:
Function.prototype.imitateCall = function (context) { // 赋值作用域参数,如果没有则默认为 window,即访问全局作用域对象 context = context || window // 绑定调用函数(.call之前的方法即this,前面提到过调用call方法会调用一遍自身,所以这里要存下来) context.invokFn = this // 截取作用域对象参数后面的参数 let args = [...arguments].slice(1) // 执行调用函数,记录拿取返回值 let result = context.invokFn(...args) // 销毁调用函数,以免作用域污染 Reflect.deleteProperty(context, 'invokFn') return result }
2.apply()
语法:obj1.call(obj2[,arrArg])
定义:用obj2对象来代替obj1,调用obj1的方法。即将obj1应用到obj2上。
说明:call ()和apply()作用一样,但是call()可以接收任何类型的参数,而apply()只能接收数组参数。
作用:这两个函数都是在特定的作用域中调用函数,能改变函数的作用域,实际上是改变函数体内 this 的值 。
原理:
Function.prototype.imitateApply = function (context) { // 赋值作用域参数,如果没有则默认为 window,即访问全局作用域对象 context = context || window // 绑定调用函数(.call之前的方法即this,前面提到过调用call方法会调用一遍自身,所以这里要存下来) context.invokFn = this // 执行调用函数,需要对是否有参数做判断,记录拿取返回值 let result if (arguments[1]) { result = context.invokFn(...arguments[1]) } else { result = context.invokFn() } // 销毁调用函数,以免作用域污染 Reflect.deleteProperty(context, 'invokFn') return result }
3.基本用法
function add(a,b){ return a+b; } function sub(c,d){ return c-d; } function result(){ this.addValue = null; this.subValue = null; this.showResult=function(){ alert(this.addValue); alert(this.subValue); } } function myCallTest(){//此处this指向了add,如果不使用call则this指向window对象 return add.call(this,7,2); } var r = new result(); console.log(myCallTest());//9 r.addValue = add.call(sub,4,2); //6,将add方法应用到sub上,即sub的指针指向add方法 r.subValue = sub.call(add,4,2); //2,用add对象替换sub对象,并调用sub对象的方法 r.showResult(); //在js中函数也是一个Function对象,函数名即是对象引用
4.继承特性
function add(a,b){ return a+b; } function sub(c,d){ return c-d; } function result(){ this.addValue = null; this.subValue = null; this.showResult=function(){ alert(this.addValue); alert(this.subValue); } } var r = new result(); r.addValue = add.call(r,4,2); //6,r继承add函数的所有特性 r.subValue = sub.call(r,4,2); //2,r集成sub函数的所有特性 r.showResult();
5.bind()
bind()函数也可以改变this指向,但是该函数返回了一个新的函数(绑定函数),当调用绑定函数时,会以bind的第一个参数作为当前作用域的上下文(this),之后的参数则作为原函数的属性。
var write = document.write; write.bind(document)("hello!");
这里write的this指向global或window,如果直接用write("hello!"),则非法调用(Illegal invocation),因为write的内部指向被修改为了window。所以需要bind重新调整索引,当然用call也可以。
var write = document.write; write.call(document,"hello!");
对比可以发现: call()、apply()都是立即执行的,bind()则是返回了一个函数,需再次调用才会执行。
原理:
Function.prototype.imitateBind = function (context) { // 获取绑定时的传参 let args = [...arguments].slice(1), // 定义中转构造函数,用于通过原型连接绑定后的函数和调用bind的函数 F = function () {}, // 记录调用函数,生成闭包,用于返回函数被调用时执行 self = this, // 定义返回(绑定)函数 bound = function () { // 合并参数,绑定时和调用时分别传入的 let finalArgs = [...args, ...arguments] // 改变作用域,注:aplly/call是立即执行函数,即绑定会直接调用 // 这里之所以要使用instanceof做判断,是要区分是不是new xxx()调用的bind方法 return self.call((this instanceof F ? this : context), ...finalArgs) } // 将调用函数的原型赋值到中转函数的原型上 F.prototype = self.prototype // 通过原型的方式继承调用函数的原型 bound.prototype = new F() return bound }
转 : https://blog.csdn.net/mafan121/article/details/52922149
https://www.cnblogs.com/Shd-Study/p/6560808.html