javascript中的隐式调用


Posted in Javascript onFebruary 10, 2018

前言

不知道用隐式调用来形容是否确切,其行为总是隐藏在背后,时不时出来露脸一下,作用貌似不大,但是了解一下还是有用处的,保不准在你的使用下大有作为。

所谓的隐式调用简单来说就是自动调用一些方法,而这些方法像钩子一样可以在外部修改,从而改变既定行为。
下面我会列举一些最近看到的隐式调用,例子都是点到即止,欢迎补充

数据类型转换 toSting 和 valueOf

var obj = {
   a: 1,
   toString: function () {
    console.log('toString')
    return '2'
   },
   valueOf: function () {
    console.log('valueOf')
    return 3
   }
  }
  console.log(obj == '2'); //依次输出 'valueOf' false
  console.log(String(obj));//依次输出 'toString' '2'
var obj = {
   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

var eventObj = {
   a: 1,
   handleEvent: function (e) {
    console.log(this, e);//返回 eventObj 和 事件对象
    alert(this.a)
   }
  }
  document.addEventListener('click', eventObj)

你没有看错,addEventListener 第二个参数除了函数外还可以是一个对象, 事件触发后会执行对象的handleEvent方法,方法执行时的this指向eventObj, 你可以把想传入的数据绑定在eventObj对象上

JSON对象 toJSON

var Obj = {
   a: 10,
   toJSON: function () {
    return {
     a: 1,
     b: function () {
     },
     c: NaN,
     d: -Infinity,
     e: Infinity,
     f: /\d/,
     g: new Error(),
     h: new Date(),
     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执行后返回的对象,有一点要注意的是,如下面代码

var Obj1 = {
   a: 10,
   toJSON: function () {
    console.log(this === Obj1);//true
    return this
   }
  }
  console.log(JSON.stringify(Obj1));//{"a":10}
var Obj2 = {
   a: 10,
   toJSON: function () {
    console.log(this === Obj2);//true
    return {
     a: this
    }
   }
  }
  console.log(JSON.stringify(Obj2));//报错 Maximum call stack size exceeded

如果按上面的说法很明显报错是我们所预期的,但是当直接 return this 根本没有报错,不妨可以大胆猜测一下其内部对toJSON返回的对象和原对像进行比较,如果相等就直接使用原对象

promise对象的 then

var obj = {
   then: function (resolve, reject) {
    setTimeout(function () {
     resolve(1000);
    }, 1000)
   }
  }
  Promise.resolve(obj).then(function (data) {
   console.log(data);// 延迟1秒左右输出 1000
  })

当Promise.resolve方法传入对象时,如果存在 then 方法会立即执行then方法,相当于把方法放入new Promise中,除了Promise.resolve有这个行为外,Promise.all也有这个行为

var timePromise = function (time) {
    return new Promise(function (resolve) {
     setTimeout(function () {
      resolve(time);
     }, time)
    })
   }
   var timePromise1 = timePromise(1000);
   var timePromise2 = timePromise(2000);
   var timePromise3 = 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

var obj = {
    _age: 100,
    get age() {
     return this._age < 18 ? this._age : 18;
    },
    set age(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岁或以下的,当执行属性存取时实际上是调用对象属性相应的 get set函数,除了以上写法还有下面的写法

var input = document.createElement('input');
  var span = document.createElement('span');
  document.body.appendChild(input);
  document.body.appendChild(span);
  var obj = {
   _age:''
  }
  var obj = Object.defineProperty(obj, 'age', {
   get: function () {
    return this._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

var arr = [ 1, 2 , 3];
  arr[Symbol.iterator] = function () {
   const self = this;
   let index = 0;
   return {
    next () {
     if(index < self.length) {
      return {
       value: self[index] ** self[index++],
       done: false
      }
     } else {
      return {
       value: undefined,
       done: true
      }
     }
    }
   }
  }
  console.log([...arr, 4]);//返回 [1, 4, 27, 4]
  for(let value of arr) {
   console.log(value); //依次返回 1 4 27
  }

可以看到凡是调用扩展运算符,或者使用 for...of 循环遍历对象都会调用对象的遍历器接口,像Array,String,Map,Set,TypedArray还有一些类数组对象如arguments,NodeList原生具备了遍历器接口,而普通对象没有部署这个接口,如果要让对象能够使用扩展运算符或者for...of循环可以在对象上添加该方法,也可以在原来具备接口的对象上重写方法,从而改变其行为

最后

暂时就想到这么多,欢迎大家补充,同时感谢你对三水点靠木的支持。

Javascript 相关文章推荐
Javascript 入门基础学习
Mar 10 Javascript
jquery sortable的拖动方法示例详解
Jan 16 Javascript
js和css写一个可以自动隐藏的悬浮框
Mar 05 Javascript
BootStrap初学者对弹出框和进度条的使用感觉
Jun 27 Javascript
jQuery Easyui Tabs扩展根据自定义属性打开页签
Aug 15 Javascript
js简单获取表单中单选按钮值的方法
Aug 23 Javascript
Angular.js中下拉框实现渲染html的方法
Jun 18 Javascript
解决Vue2.0中使用less给元素添加背景图片出现的问题
Sep 03 Javascript
Vue $mount实战之实现消息弹窗组件
Apr 22 Javascript
jQuery提示框插件SweetAlert用法分析
Aug 05 jQuery
js数组相减简单示例【删除a数组所有与b数组相同元素】
Mar 04 Javascript
在Webpack中用url-loader处理图片和字体的问题
Apr 28 Javascript
VUEJS 2.0 子组件访问/调用父组件的实例
Feb 10 #Javascript
webpack之devtool详解
Feb 10 #Javascript
React组件refs的使用详解
Feb 09 #Javascript
详解vue-cli项目中的proxyTable跨域问题小结
Feb 09 #Javascript
使用express搭建一个简单的查询服务器的方法
Feb 09 #Javascript
js自定义trim函数实现删除两端空格功能
Feb 09 #Javascript
JavaScript运行原理分析
Feb 09 #Javascript
You might like
php开发文档 会员收费1期
2012/08/14 PHP
PHP开发工具ZendStudio下Xdebug工具使用说明详解
2013/11/11 PHP
php查询ip所在地的方法
2014/12/05 PHP
PHP编程一定要改掉的5个不良习惯
2020/09/18 PHP
JS Timing
2007/04/21 Javascript
jquery解析JSON数据示例代码
2014/03/17 Javascript
jquery重复提交请求的原因浅析
2014/05/23 Javascript
DOM基础教程之使用DOM设置文本框
2015/01/20 Javascript
全面解析Bootstrap表单使用方法(表单控件状态)
2015/11/24 Javascript
详解vue.js下引入百度地图jsApi的两种方法
2018/07/27 Javascript
解决vue-cli webpack打包后加载资源的路径问题
2018/09/25 Javascript
vue实现双向绑定和依赖收集遇到的坑
2018/11/29 Javascript
jQuery创建折叠式菜单
2019/06/15 jQuery
vue + typescript + video.js实现 流媒体播放 视频监控功能
2019/07/07 Javascript
基于JavaScript实现大文件上传后端代码实例
2020/08/18 Javascript
vue a标签点击实现赋值方式
2020/09/07 Javascript
Vue用mixin合并重复代码的实现
2020/11/27 Vue.js
[05:46]DOTA2英雄梦之声_第18期_陈
2014/06/20 DOTA
[04:59]2018DOTA2亚洲邀请赛 4.7 Mineski夺冠时刻
2018/04/09 DOTA
Python图算法实例分析
2016/08/13 Python
Python使用回溯法子集树模板获取最长公共子序列(LCS)的方法
2017/09/08 Python
Python 类的特殊成员解析
2018/06/20 Python
Python实现读取txt文件中的数据并绘制出图形操作示例
2019/02/26 Python
python TK库简单应用(实时显示子进程输出)
2019/10/29 Python
Python unittest基本使用方法代码实例
2020/06/29 Python
咖啡为什么会有酸味?你喝到的咖啡為什麼是酸的?
2021/03/17 冲泡冲煮
CSS3选择器新增问题的实现
2021/01/21 HTML / CSS
New Balance天猫官方旗舰店:始于1906年,百年慢跑品牌
2017/11/15 全球购物
英国羊皮鞋类领先品牌:Just Sheepskin
2019/12/12 全球购物
Java TransactionAPI (JTA) 主要包含几部分
2012/12/07 面试题
jQuery treeview树形结构应用
2021/03/24 jQuery
新年爱情寄语
2014/04/08 职场文书
美德少年事迹材料500字
2014/08/19 职场文书
家长会欢迎词
2015/01/23 职场文书
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
2022/02/12 Redis
SQL Server中的逻辑函数介绍
2022/05/25 SQL Server