当前位置: 首页 > 科技观察

Javascript的New、Apply、Bind、Call你知道多少

时间:2023-03-21 00:43:40 科技观察

1上面用Javascript写的apply、call、bind方法是前端代码开发中非常重要的概念,与方向密切相关这。在本文中,我们将深入探讨这个关键字的作用,并尝试手动复现。在看文章之前,我们带着几个问题进行了调研:新关键词可以用什么样的思维?apply、bind、call这三种方法有什么区别?如何手动实现一个apply、bind、call?2new关键字new关键字的作用是执行一个构造函数,返回一个实例对象,根据构造函数的情况判断是否可以接受参数的传递。2.1new的原理使用new来实例化一个对象。步骤为:新建一个空对象,即{}将对象构造函数的作用域赋值给新对象,this指向新对象(即新对象作为this的上下文)执行构造函数中的代码并将属性添加到这个新对象。如果对象构造器没有返回对象,则返回thisfunctionPerson(){this.name="yichuan"}constp=newPerson();console.log(p.name);//"yichuan"我们可以看到在使用new的时候为了实例化,构造函数的this可以指向新对象p。不使用new时,此时构造函数的this指向window。functionPerson(){this.name="yichuan"}constp=Person();console.log(p);//undefinedconsole.log(name);//"yichuan"window.nameconsole.log(p.name);//"yichuan"是undefined当我们在构造函数中直接返回一个与this无关的对象时,使用new关键字实例化该对象,新生成的对象是构造函数返回的对象,而不是this对象的构造函数。functionPerson(){this.name="yichuan";return{age:18};}constp=newPerson();console.log(p);//{age:18}console.log(p.name);//"undefined"console.log(p.age);/18另外,当构造函数返回的不是对象而是基本数据类型的值时,使用new创建新对象会将构造函数返回的值转换为对象形式到新对象。functionPerson(){this.name="yichuan";return"onechuan";}constp=newPerson();console.log(p);//{name:"yichuan"}console.log(p.name);//"yichuan"new关键字执行后,总会返回一个对象,要么是实例,要么是return语句指定的对象。2.2handwrittennew调用new后的实现大致做了什么?允许实例访问私有属性允许实例访问构造函数原型(constructor.prototype)所在原型链上的属性构造函数最终返回的结果是引用数据类型functionnew_object(ctor,...args){//首先判断ctor是否为函数if(typeofctor!=="function"){throw"ctormustbeafunction";}//创建一个空对象constobj=newObject();//实例obj可以访问的属性ctor原型所在的原型链obj.__proto__=Object.create(ctor.prototype);//将构造函数的this指向实例对象objconstres=ctor.apply(obj,...args);//确保最后一个new返回的是对象constisObject=typeofres==="object"&&typeofres!==null;constisFunction=typeofres==="function";returnisObject||isFunction?res:obj;}当然我们还可以优化如下:functionnew_object(){//1,获取构造函数,并删除第一个参数constctor=[].shift.call(arguments);//其实这里就是借用了数组的shift方法//2、创建一个空对象并链接原型,obj可以访问构造函数原型中的属性constobj=Object.create(ctor.prototype);//3、绑定this实现继承,obj可以访问构造函数原型中的属性constructorconstret=ctor.apply(obj,arguments);//4.优先返回构造函数返回的对象returnretinstanceofObject?ret:obj;};3apply、bind和callApply、bind和call是挂载Function对象的三个方法,调用这三个方法的必须是一个函数。3.1applyapply()方法使用给定的this值调用函数,并将参数作为数组(或类数组对象)提供。apply()方法可以改变函数this的指针并立即执行函数。注意:Chrome14和InternetExplorer9仍然不接受类数组对象。如果传入类似数组的对象,它们会抛出异常。func.apply(thisArg,[param1,param2,...]);使用apply时,会将func的this指向指向thisArg,然后将[param1,param2,...]参数数组作为参数输入。func(["red","green","blue"]);func.apply(newFun,["red","green","blue"]);我们可以看到,在执行func时,func函数的第一个this指向window全局对象,第二个func函数的this指向newFun。Function.prototype.apply=function(context,arr){context=context?Object(context):window;context.fn=this;letresult;//判断是否有参数数组输入if(!arr){result=语境。fn();}else{result=context.fn(...arr);}//这里也可以使用eval进行处理//constresult=eval("context.fn(...arr)");deletecontext.fnreturnresult;}3.2bindbind()方法创建一个新函数。调用bind()时,这个新函数的this被指定为bind()的第一个参数,其余参数将作为新函数的参数。调用时使用。绑定(thisArg,参数1,参数2,...);其实bind的实现思路和apply基本一样,只是在最后返回结果的时候,bind不需要直接执行,而是以返回函数的形式返回结果,然后通过执行这个结果。首先分析bind的特点:首先指定new对象的this点,然后传入参数返回一个定义好的函数,最后使用currying调用。同样,我们也可以根据这些特点,手动封装一个bind函数:typeofthis!=="function"){thrownewError("thisbindfunctionmustbeuserdtofunction");}//存放this的指针constself=this;//context为新对象this指向的目标对象,参数为参数constargs=第一个参数后的数组。prototype.slice.call(arguments,1);//创建一个空对象constfun=function(){}//返回一个函数constfunBind=function(){//返回bind函数的所有参数constbindArg=Array.prototype.slice.call(arguments);//将传入的参数合并到一个新的参数数组中作为self.apply()的第二个参数returnsself.apply(thisinstanceoffun?this:context,args.concat(bindArgs));/**********************阐明************************************//空对象的原型指向绑定函数的原型fun.prototype=this.prototype;//空对象的实例赋值给funBind.prototypefunBind.prototype=newfun();returnfunBinf;}补充说明:当这个instanceoffun返回true时,说明fun是一个构造函数,它的this指向实例,直接传入上下文作为参数。当这个instanceoffun返回false时,说明fun是一个普通的函数,它的this指向顶层对象窗口,而绑定函数的this指向上下文对象当然我们也可以写成这样的形式:Function。prototype.bind=函数(上下文,...args){//先判断bind函数的调用是否为函数,需要抛出异常if(typeofthis!=="function"){thrownewError("thisbindfunctionmustbeuserdtofunction");}//将this指向constself=this;constfBind=function(){self.apply(thisinstanceofself?this:context,args.concat(Array.prototype.slice.call(arguments)));}if(this.prototype){fBind.prototype=对象。create(this.prototype);}returnfBind;}注:Object.create()是es2015语法引入的新特性,IE<9浏览器不支持。3.3call()方法使用一个指定的this值和一个或多个单独给定的参数来调用一个函数。使用调用者提供的this值和参数调用此函数的返回值。如果方法没有返回值,则返回undefined。function.call(thisArg,param1,param2,...)注意:该方法的语法和功能与apply()方法类似,唯一的区别是call()方法接受一个参数列表,而apply()方法接受包含多个参数的数组。调用函数的实现:Function.prototype.call=function(context,...args){//将函数设置为对象的属性context=context||window;context.fn=this;//执行thefunctionconstresult=eval("context.fn(...args)");//删除对象的这个属性deletecontext.fn;returnresult;}4篇参考文章《解析 bind 原理,并手写 bind 实现》《解析 call/apply 原理,并手写 call/apply 实现》5写在文末,我们知道apply、bind、call的区别是:apply和call改变this点后,会立即调用函数,并返回执行结果。bind改变this点后返回一个函数,需要再次调用bind和call传递的第一个参数是this将指向的对象,后面的参数以一个参数的形式传入。apply传递的第一个参数也是this要指向的对象,后面传递的第二个参数是一个参数数组