function Test(){ console.log(this) } Test();//打印window var t=new Test()//打印Test(){}
首先我们要明白上面这个this指向问题,
普通函数调用,this指向window,
而通过new来实例化一个构造函数的对象,则this指向函数。
1.call、apply和bind 的相同点和不同点
2.call的用途。
首先,在函数调用的时候,我们都习惯直接调用,比如说function test(){};test();其实这并不是函数调用的本质,本质是test.call();test()只不过是本质的语法糖,它只能调用,而不能改变函数的执行环境。如下所示。
function test(){ console.log("hello world"); } //函数的一般调用 test(); //函数调用的本质 test.call()
那么call是如何改变函数的执行环境的?如下所示:
//这个myName变量相当于给window这个对象添加了一个myName属性 var myName="yyl" function test(){ //此时的this就是指window console.log(this.myName)//打印yyl } test() var obj={myName:"hhh"} test.call(obj)//打印hhh
这也就解释了我们之前写的那篇原型中,子函数要想继承父函数需要在子函数中写{Father.call(this)},这个this指向的是Son函数,那么就改变了Father的this指向,将其指向到Son函数中,也就实现了Son函数的实例调用Father的属性。如下所示。
function Father() { this.health = "very good" } function Son() { Father.call(this); } var son = new Son(); console.log(son.health)//打印very good
当然,这样写也可以。
function Father() { this.health = "very good" } function Son() {} var son = new Son(); Father.call(son); console.log(son.health)//打印very good
3.apply的用途。
apply除了后面的参数形式与call有区别,其他都一样。比如上面所有用到call的地方都可以用apply来代替。千万不要小看了这个这个参数形式,有些情况下就只能用apply不能用call,比如使用Math.max去获取数组(这个数组有很多元素)的最大值时,若是用call,需要将数组的所有元素陈列出来,而使用apply直接写一个数组就可以。如下:
var arr=[1,3,2,6,4]; //1.普通调用方法,是下面这个call方法的语法糖 console.log(Math.max(1,2,3,6,4))//打印6 //调用的实质 console.log(Math.max.call(null,1,2,3,6,4));//打印6 //使用apply方法 console.log(Math.max.apply(null,arr));//打印6
4.bind的用途。
直接上代码:
var myName="yyl"; function test1(){ console.log(this.myName); } test1();//打印yyl var obj={myName:"hhh"} var test2=test1.bind(obj); test2();//打印hhh
可以看出bind与call的唯一区别就是call直接改变函数test的指向,而bind是生成了一个新函数test2,该函数改变了指向。
来自转载
call、apply、bind的实现
call实现
1. 给函数的原型中添加方法
Function.prototype.mycall = function() { console.log(123); } function num() { console.log('测试创建的函数是否包含mycall的方法'); } function foo() { console.log('测试创建的函数是否包含mycall的方法'); } num.mycall() //123 foo.mycall() //123
2. mycall函数中获取当前调用的方法,(Fn.mycall)调用的方法是隐式绑定,所以直接使用this就可以获取了
Function.prototype.mycall = function() { console.log(this); } ... ... num.mycall() //[Function: num] foo.mycall() //[Function: foo]
3. 此时函数已经拿到了后面只需要修改它的this指向就可以了,既然上面已经确定了是隐式绑定那只需要修改"."前面的参数就可以实现动态绑定this了。
Function.prototype.mycall = function(thisArg) { let fn = this thisArg.fn = fn; thisArg.fn(); } function num() { console.log(this); } function foo() { console.log(this); } num.mycall({}) //{ fn: [Function: num] } foo.mycall([123]) //[ 123, fn: [Function: foo] ]
4. 现在基本的this自定义绑定已经实现了,但是还有三个大的问题。
- thisArg.fn = fn;这段代码给this增加了fn函数(调用mycall的函数)。
- 当传入非对象类型无法绑定为this。
- 无法传入参数
Function.prototype.mycall = function(thisArg = window, ...args) { //获取自定义this及参数 let fn = this thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window; //对传人的this做对象转化,为null或者undefined改为window, thisArg.fn = fn; let result = thisArg.fn(...args); delete thisArg.fn //删除fn return result //返回函数 } function num() { console.log(this); } function foo() { console.log(this); } num.mycall({}) foo.mycall(123)
apply实现
Function.prototype.mycall = function (thisArg = window, args = []) { //获取自定义this及参数 let fn = this thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window; //对传人的this做对象转化,为null或者unified改为window, thisArg.fn = fn; if (!(args instanceof Array)) throw new SyntaxError("传入的参数类型不是数组"); //确保参数为数组类型 let result = thisArg.fn(...args); delete thisArg.fn //删除fn return result //返回函数 } function num() { console.log(this); } function foo(str) { console.log(this, str); } num.mycall({}, []) foo.mycall('aaa', ['pxq'])
bind的实现
Function.prototype.mybind = function (thisArg = window, ...args) { //获取自定义this及参数 let fn = this; thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window; return function(...argsArray) { thisArg.fn = fn let result = thisArg.fn(...args, ...argsArray); delete thisArg.fn return result } } function num (num1, num2) { console.log(this, num1, num2); } function foo () { console.log(this); } var newNum = num.mybind({}, 10) newNum(20) var newFoo = foo.mybind([]); newFoo()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构