js技巧之十几行的代码实现vue.watch代码


Posted in Javascript onJune 09, 2018

getter和setter

getter 是一种获得属性值的方法,setter是一种设置属性值的方法。
属性被赋值 a = 1的时候, a 的原型内的setter就会被触发;
而 console.log(a) 的时候,a 的原型内的getter就会被触发。

实现getter和setter

我们不能直接给变量的setter和getter 绑定事件函数,为了实现绑定我们要借助Object对象来构造带有setter和getter的属性。

这里有前辈总结的 几种实现getter和setter的方法,而且他还总结了一些Object.prototype内控制属性枚举的特性的隐式属性。

我这里选用了比较好构造的一种 Object.defineProperty

概要
Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。
语法
Object.defineProperty(obj, prop, descriptor)
参数
obj
需要定义属性的对象。
prop
需被定义或修改的属性名。
descriptor
需被定义或修改的属性的描述符。

  1. 第一个参数,被构造的属性的this指向的对象
  2. 第二个参数,被构造的属性名
  3. 第三个参数,构造的规则(上面的文字链接最后面有介绍)
(function () {
  var o = { a : 1}//声明一个对象,包含一个 a 属性,值为1
  Object.defineProperty(o,"b",{
    get: function () {
      return this.a;
    },
    set : function (val) {
      this.a = val;
    },
    configurable : true
  });

  console.log(o.b);//==> 1
  o.b = 2;
  console.log(o.b);//==> 2
})();

configurable是指 "b" 是否可以被再配置,默认是false。false的话
Object.defineProperty(o,"a",{set : function(val){}} );

再修改时会不起作用或者报错,一般默认false。

构造我们的vue.watch

目标实现,以下是我们想要的达到的效果

import watcher from './watcher.js';
let wm = new watcher({
  data:{
    a: 0 
  },
  watch:{
    a(newVal,oldVal){
      console.log('newVal:'+newVal);
      console.log('oldVal:'+oldVal);
    }
  }
})
vm.a = 1 
// newVal:1
// oldVal:0

创建构造对象

class watcher{
  constructor(opts){
    this.$data = opts.data;
    for(let key in opts.data){
      this.setData(key,opts.data[key])
    }
  }

  setData(_key,_val){
    Object.defineProperty(this,_key,{
      get: function () {
        return this.$data[_key];
      },
      set : function (val) {
        const oldVal = this.$data[_key];
        if(oldVal === val)return val;
        this.$data[_key] = val;
        return val;
      },
    });
  }
}

export default watcher;

添加 watch事件触发

/**
 * @desc 属性改变监听,属性被set时出发watch的方法,类似vue的watch
 * @author Jason
 * @date 2018-04-27
 * @constructor 
 * @param {object} opts - 构造参数. @default {data:{},watch:{}};
 * @argument {object} data - 要绑定的属性
 * @argument {object} watch - 要监听的属性的回调 
 * watch @callback (newVal,oldVal) - 新值与旧值 
 */
class watcher{
  constructor(opts){
    this.$data = this.getBaseType(opts.data) === 'Object' ? opts.data : {};
    this.$watch = this.getBaseType(opts.watch) === 'Object' ? opts.watch : {};
    for(let key in opts.data){
      this.setData(key)
    }
  }

  getBaseType(target) {
    const typeStr = Object.prototype.toString.apply(target);
  
    return typeStr.slice(8, -1);
  }

  setData(_key){
    Object.defineProperty(this,_key,{
      get: function () {
        return this.$data[_key];
      },
      set : function (val) {
        const oldVal = this.$data[_key];
        if(oldVal === val)return val;
        this.$data[_key] = val;
        this.$watch[_key] && typeof this.$watch[_key] === 'function' && (
          this.$watch[_key].call(this,val,oldVal)
        );
        return val;
      },
    });
  }
}

export default watcher;
  • 为了函数内部的健壮性,getBaseType是用来做类型校验的。
  • Object.defineProperty(this),this把上下文指向当前对象。
  • this.$watch[_key].call(this,val,oldVal),把监听事件的上下文页绑定到当前对象,方便在watch内通过this获取对象内的值,如下
let wm = new watcher({
  data:{
    a: 0,
    b: 'hello'
  },
  watch:{
    a(newVal,oldVal){
      console.log(this.b);
    }
  }
})

总结

有人可能会问为什么不直接用vue呢。你也知道vue是一个工程级别的框架,做比较大的项目当然是用vue,react;但是单单做一个展示性的官网或者做个移动端的H5宣传页也用上vue吗?那当然是没有必要的。
用上这一个watcher类,可以让你页面的状态控制有条理、有迹可循。
比如几个按钮联动一个或几个视图的改变和动效的时候,你就不用在每个按钮的click时都触发一下修改

btn1.onclick=function(){
  var a = 'haha';
  document.getElementById('id').innerHTML = a;
 }
 btn2.onclick=function(){
  var a = 'xixi';
  document.getElementById('id').innerHTML = a;
 }
let wm = new watcher({
  data:{
    a: "",
  },
  watch:{
    a(newVal,oldVal){
      document.getElementById('id').innerHTML = newVal;
    }
  }
})

btn1.onclick=function(){
  wm.a = 'haha';
 }
 btn2.onclick=function(){
  wm.a = 'xixi';
 }

但是如果你的视图不被2个以上动作联动的话,也未必会用上。

Javascript 相关文章推荐
JavaScript 学习点滴记录
Apr 24 Javascript
jquery tools之tabs 选项卡/页签
Jul 25 Javascript
Jquery 选中表格一列并对表格排序实现原理
Dec 15 Javascript
jquery插件实现鼠标经过图片右侧显示大图的效果(类似淘宝)
Feb 04 Javascript
select多选 multiple的使用示例
Jun 16 Javascript
JavaScript判断DIV内容是否为空的方法
Jan 29 Javascript
JavaScript函数节流概念与用法实例详解
Jun 20 Javascript
js图片放大镜实例讲解(必看篇)
Jul 17 Javascript
Vue仿今日头条实例详解
Feb 06 Javascript
使用ngrok+express解决本地环境中微信接口调试问题
Feb 26 Javascript
node.js域名解析实现方法详解
Nov 05 Javascript
ElementUI实现el-form表单重置功能按钮
Jul 21 Javascript
浅谈JS对象添加getter与setter的5种方法
Jun 09 #Javascript
让你5分钟掌握9个JavaScript小技巧
Jun 09 #Javascript
jQuery基于闭包实现的显示与隐藏div功能示例
Jun 09 #jQuery
JS实现区分中英文并统计字符个数的方法示例
Jun 09 #Javascript
详解angular脏检查原理及伪代码实现
Jun 08 #Javascript
解析vue路由异步组件和懒加载案例
Jun 08 #Javascript
node中modules.exports与exports导出的区别
Jun 08 #Javascript
You might like
哪吒敖丙传:新人物二哥敖乙出场 小敖丙奶气十足
2020/03/08 国漫
php基础知识:控制结构
2006/12/13 PHP
基于递归实现的php树形菜单代码
2014/11/19 PHP
Yii框架日志操作图文与实例详解
2019/09/09 PHP
DHTML Slide Show script图片轮换
2008/03/03 Javascript
javascript Firefox与IE 替换节点的方法
2010/02/24 Javascript
非常好用的JsonToString 方法 简单实例
2013/07/18 Javascript
文档对象模型DOM通俗讲解
2013/11/01 Javascript
jquery删除数据记录时的弹出提示效果
2014/05/06 Javascript
jQuery 1.9.1源码分析系列(十四)之常用jQuery工具
2015/12/02 Javascript
jQuery实现ToolTip元素定位显示功能示例
2016/11/23 Javascript
如何实现星星评价(jquery.raty.js插件)
2016/12/21 Javascript
基于jQuery实现顶部导航栏功能
2016/12/27 Javascript
vue动态生成dom并且自动绑定事件
2017/04/19 Javascript
Angular排序实例详解
2017/06/28 Javascript
使用Node.js实现简易MVC框架的方法
2017/08/07 Javascript
NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】
2017/09/06 NodeJs
详解Vue中的MVVM原理和实现方法
2020/07/15 Javascript
vue项目,代码提交至码云,iconfont的用法说明
2020/07/30 Javascript
[01:28:43]2014 DOTA2华西杯精英邀请赛5 24 DK VS CIS
2014/05/25 DOTA
python中virtualenvwrapper安装与使用
2018/05/20 Python
python把数组中的数字每行打印3个并保存在文档中的方法
2018/07/17 Python
Python socket实现多对多全双工通信的方法
2019/02/13 Python
如何在python中实现线性回归
2020/08/10 Python
教你如何用python操作摄像头以及对视频流的处理
2020/10/12 Python
材料物理专业大学毕业生求职信
2013/10/15 职场文书
毕业生求职简历的自我评价
2013/10/23 职场文书
应用心理学个人的求职信
2013/12/08 职场文书
企业法人代表任命书
2014/06/06 职场文书
建设工程授权委托书
2014/09/22 职场文书
2014年妇幼卫生工作总结
2014/12/09 职场文书
物业工程部岗位职责
2015/02/11 职场文书
幼儿园重阳节活动总结
2015/05/05 职场文书
家长会开场白和结束语
2015/05/29 职场文书
PostgreSQL通过oracle_fdw访问Oracle数据的实现步骤
2021/05/21 PostgreSQL
Java Optional<Foo>转换成List<Bar>的实例方法
2021/06/20 Java/Android