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循环可以在对象上添加该方法,也可以在原来具备接口的对象上重写方法,从而改变其行为
最后
暂时就想到这么多,欢迎大家补充,同时感谢你对毛票票的支持。