JavaScript中call,apply,bind的区别与实现
作者:XerDemo
这篇文章主要介绍了JavaScript中call,apply,bind的区别与实现,文章通过围绕主题思想展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
区别
- call、apply、bind相同点:都是改变this的指向,传入的第一个参数都是绑定this的指向,在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined
- call和apply唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组
- bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
- 值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
call实现
对象context想调用一个它没有的方法f 怎么办呢?f.call(context)
通过call来借用方法f ,怎么做到的呢?
- 对象context添加f方法
- 对象context执行f方法
Function.prototype.myCall = function(context, ...arg) { // 如果第一个参数传入的是undefined和null,context为window对象 context = context || window; // 为context对象添加函数bar context.fn = this; // this:bar,this指向调用myCall的bar // context对象执行函数bar,并返回结果 return context.fn(...arg); } // 测试一下 var value = 2; var obj = { value: 1 } function bar(name, age) { console.log(this.value); return { value: this.value, name: name, age: age } } bar.myCall(null); // 2 console.log(bar.myCall(obj, 'kevin', 18)); //1 // Object { // value: 1, // name: 'kevin', // age: 18 // }
apply实现
apply和call唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组
Function.prototype.myApply = function(context, arg) { // 如果第一个参数传入的是undefined和null,context为window对象 context = context || window; // context对象添加函数bar context.fn = this; // this:bar,this指向调用myCall的函数bar // context对象执行函数bar,并返回结果 let result = null; if (!arg) { // 没有传入数组 result = context.fn(); }else{ // 传入了参数数组 result = context.fn(...arg); } return result; } // 测试一下 var value = 2; var obj = { value: 1 } function bar(name, age) { console.log(this.value); return { value: this.value, name: name, age: age } } bar.myApply(null); // 2 console.log(bar.myApply(obj, ['kevin', 18])); // 1
bind实现
- bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
- 值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
bind实现最为复杂,因为经过bind绑定过的函数,既可以被当作普通函数调用,又可以被当作构造函数调用
- bind 返回的函数 作为普通函数调用
// bind 返回的函数 作为普通函数调用 let bindFun = normalFun.myBind(obj, '我是参数传进来的name') // this:obj bindFun('我是参数传进来的age')
- bind 返回的函数 作为构造函数调用,绑定的 this 值obj会失效,this指向实例对象a
// bind 返回的函数 作为构造函数调用 let bindFun = Person.myBind(obj, '我是参数传进来的name') // this:obj let a = new bindFun('我是参数传进来的age') // this:a
bind 返回的函数 作为普通函数调用 代码实现
// bind 返回的函数 作为普通函数调用 Function.prototype.myBind = function (context, ...args){ // 如果第一个参数传入的是undefined和null,context为window对象 context = context || window; // context对象(obj)添加函数normalFun context.fn = this; // this:normalFun, context.fn === normalFun,下面出现context.fn都可以直接看成normalFun // bind返回的函数 return function (...innerArgs) { // bind 返回的函数 作为普通函数被执行 context.fn(...[...args,...innerArgs]); //相当于normalFun(...[...args,...innerArgs]) } } // 测试 let obj = { objName: '我是obj传进来的name', objAge: '我是obj传进来的age' } // 普通函数 function normalFun(name, age) { console.log(name); //'我是第一次参数传进来的name被args接收' console.log(age); //'我是第二次参数传进来的age被innerArgs接收' console.log(this === obj); // true,this指向obj console.log(this.objName); //'我是obj传进来的name' console.log(this.objAge); //'我是obj传进来的age' } // bind 返回的函数 作为普通函数调用 let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj bindFun('我是第二次参数传进来的age被innerArgs接收');
bind 返回的函数 作为构造函数调用
// bind 返回的函数 再经过new调用 Function.prototype.myBind = function (context, ...args){ // 如果第一个参数传入的是undefined和null,context为window对象 context = context || window; // context对象添加函数Person context.fn = this; // this:Person,context.fn:Person,_this:Person let _this = this; // bind返回的函数 const result = function (...innerArgs) { if (this instanceof _this ) { // this:a (new出来的实例对象) , _this:Person // 为实例对象a添加Person方法 this.fn = _this; // 实例对象a执行Person方法 this.fn(...[...args,...innerArgs]); } } result.prototype = Object.create(this.prototype); // 为什加这一句?看原型图下面会解释 return result; } // 测试 function Person(name, age) { console.log(name); //'我是第一次参数传进来的name被args接收' console.log(age); //'我是第二次参数传进来的age被innerArgs接收' console.log(this); //构造函数this指向实例对象 } // 构造函数原型的方法 Person.prototype.say = function() { console.log(123); } let obj = { objName: '我是obj传进来的name', objAge: '我是obj传进来的age' } // bind 返回的函数 作为构造函数调用 let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a a.say() //123
画以下两条语句的原型图方便理解
let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a
当执行下面语句时,原型图如下:
let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj
当执行下面语句时,bindFun就是result看代码,原型图如下:
let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a
在这里实例对象a还需要继承构造函数Person的原型,所以加上了
result.prototype = Object.create(this.prototype);
原型图最终如下:
bind代码最终实现
Function.prototype.myBind = function (context, ...args){ // 如果第一个参数传入的是undefined和null,context为window对象 context = context || window; // context对象添加函数Person context.fn = this; // this:Person,context.fn:Person,_this:Person let _this = this; // bind返回的函数 const result = function (...innerArgs) { if (this instanceof _this ) { // this:a (new出来的实例对象) , _this:Person // 为实例对象a添加Person方法 this.fn = _this; // 实例对象a执行Person方法 this.fn(...[...args,...innerArgs]); }else{ // 普通函数被调用 context.fn(...[...args,...innerArgs]); } } result.prototype = Object.create(this.prototype); // 为什加这一句?看原型图下面会解释 return result; } // 测试 // function Person(name, age) { // console.log(name); //'我是第一次参数传进来的name被args接收' // console.log(age); //'我是第二次参数传进来的age被innerArgs接收' // console.log(this); //构造函数this指向实例对象 // } // // 构造函数原型的方法 // Person.prototype.say = function() { // console.log(123); // } // let obj = { // objName: '我是obj传进来的name', // objAge: '我是obj传进来的age' // } // // bind 返回的函数 作为构造函数调用 // let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj // let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a // a.say() //123 // 测试 let obj = { objName: '我是obj传进来的name', objAge: '我是obj传进来的age' } // 普通函数 function normalFun(name, age) { console.log(name); //'我是第一次参数传进来的name' console.log(age); //'我是第二次参数传进来的age' console.log(this === obj); // true console.log(this.objName); //'我是obj传进来的name' console.log(this.objAge); //'我是obj传进来的age' } // bind 返回的函数 作为普通函数调用 let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj bindFun('我是第二次参数传进来的age被innerArgs接收');
到此这篇关于JavaScript中call,apply,bind的区别与实现的文章就介绍到这了,更多相关JS call,apply,bind区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!