javascript中的隐式调用
前言
不知道用隐式调用来形容是否确切,其行为总是隐藏在背后,时不时出来露脸一下,作用貌似不大,但是了解一下还是有用处的,保不准在你的使用下大有作为。
所谓的隐式调用简单来说就是自动调用一些方法,而这些方法像钩子一样可以在外部修改,从而改变既定行为。
下面我会列举一些最近看到的隐式调用,例子都是点到即止,欢迎补充
数据类型转换toSting和valueOf
varobj={ a:1, toString:function(){ console.log('toString') return'2' }, valueOf:function(){ console.log('valueOf') return3 } } console.log(obj=='2');//依次输出'valueOf'false console.log(String(obj));//依次输出'toString''2'
varobj={ a:1, toString:function(){ console.log('toString') return'2' }, valueOf:function(){ console.log('valueOf') return{}//修改为对象 } } console.log(obj=='2');//依次输出'valueOf''toString'true console.log(Number(obj));//依次输出'valueOf''toString'2
在相等运算符的操作中,对象会先调用valueOf如果返回的值是一个对象,就会调用toSting,null除外,然后用返回的值进行比较,第一个例子相当于3=='2'输出false,第二个例子由于执行valueOf返回的是一个对象,然后执行toString,最后相当于'2'=='2'输出true在Number和String方法中Number会先调用valueOf,后调用toString,String方法中是相反的。
数据类型的转换除了上面的两个例子外,还存在在各种其他操作中,如数值运算,当涉及到引用类型时,都会调用valueOf或toString方法,只要是对象都会继承这两个方法,我们可以重新覆盖这两个方法,从而影响数据类型转换的行为
DOM2事件中的handleEvent
vareventObj={ a:1, handleEvent:function(e){ console.log(this,e);//返回eventObj和事件对象 alert(this.a) } } document.addEventListener('click',eventObj)
你没有看错,addEventListener第二个参数除了函数外还可以是一个对象,事件触发后会执行对象的handleEvent方法,方法执行时的this指向eventObj,你可以把想传入的数据绑定在eventObj对象上
JSON对象toJSON
varObj={ a:10, toJSON:function(){ return{ a:1, b:function(){ }, c:NaN, d:-Infinity, e:Infinity, f:/\d/, g:newError(), h:newDate(), i:undefined, } } } console.log(JSON.stringify(Obj)); //{"a":1,"c":null,"d":null,"e":null,"f":{},"g":{},"h":"2018-02-09T19:29:13.828Z"}
如果JSON的stringify方法传入的对象有toJSON方法,那么该方法执行的对象会转为toJSON执行后返回的对象,有一点要注意的是,如下面代码
varObj1={ a:10, toJSON:function(){ console.log(this===Obj1);//true returnthis } } console.log(JSON.stringify(Obj1));//{"a":10}
varObj2={ a:10, toJSON:function(){ console.log(this===Obj2);//true return{ a:this } } } console.log(JSON.stringify(Obj2));//报错Maximumcallstacksizeexceeded
如果按上面的说法很明显报错是我们所预期的,但是当直接returnthis根本没有报错,不妨可以大胆猜测一下其内部对toJSON返回的对象和原对像进行比较,如果相等就直接使用原对象
promise对象的then
varobj={ then:function(resolve,reject){ setTimeout(function(){ resolve(1000); },1000) } } Promise.resolve(obj).then(function(data){ console.log(data);//延迟1秒左右输出1000 })
当Promise.resolve方法传入对象时,如果存在then方法会立即执行then方法,相当于把方法放入newPromise中,除了Promise.resolve有这个行为外,Promise.all也有这个行为
vartimePromise=function(time){ returnnewPromise(function(resolve){ setTimeout(function(){ resolve(time); },time) }) } vartimePromise1=timePromise(1000); vartimePromise2=timePromise(2000); vartimePromise3=timePromise(3000); Array.prototype.then=function(resolve){ setTimeout(function(){ resolve(4000); },4000) } Promise.all([timePromise1,timePromise2,timePromise3]).then(function(time){ console.log(time);//等待4秒左右输出4000 })
对象属性存取器get和set
varobj={ _age:100, getage(){ returnthis._age<18?this._age:18; }, setage(value){ this._age=value; console.log(`年龄设置为${value}岁`); } } obj.age=1000;//年龄设置为1000岁 obj.age=200;//年龄设置为200岁 console.log(obj.age);//18 obj.age=2;////年龄设置为2岁 console.log(obj.age);//2
可以看到不管把年龄设置为多少,我的年龄都是18岁或以下的,当执行属性存取时实际上是调用对象属性相应的getset函数,除了以上写法还有下面的写法
varinput=document.createElement('input'); varspan=document.createElement('span'); document.body.appendChild(input); document.body.appendChild(span); varobj={ _age:'' } varobj=Object.defineProperty(obj,'age',{ get:function(){ returnthis._age; }, set:function(value){ this._age=value; input.value=value; span.innerHTML=value; } }); input.onkeyup=function(e){ if(e.keyCode===37||e.keyCode===39){ return; } obj.age=this.value }
现在input的value值和obj.age的属性值span的innerHTML值都绑定在一起了
遍历器接口Symbol.iterator
vararr=[1,2,3]; arr[Symbol.iterator]=function(){ constself=this; letindex=0; return{ next(){ if(index可以看到凡是调用扩展运算符,或者使用for...of循环遍历对象都会调用对象的遍历器接口,像Array,String,Map,Set,TypedArray还有一些类数组对象如arguments,NodeList原生具备了遍历器接口,而普通对象没有部署这个接口,如果要让对象能够使用扩展运算符或者for...of循环可以在对象上添加该方法,也可以在原来具备接口的对象上重写方法,从而改变其行为
最后
暂时就想到这么多,欢迎大家补充,同时感谢你对毛票票的支持。