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将数据库中的TEXT类型数据动态赋值到TEXTAREA中
Apr 20 Javascript
IE DOM实现存在的部分问题及解决方法
Jul 25 Javascript
js实现运动logo图片效果及运动元素对象sportBox使用方法
Dec 25 Javascript
vue实现添加标签demo示例代码
Jan 21 Javascript
node.js实现复制文本到剪切板的功能
Jan 23 Javascript
vue.js 左侧二级菜单显示与隐藏切换的实例代码
May 23 Javascript
vue使用stompjs实现mqtt消息推送通知
Jun 22 Javascript
老生常谈JavaScript面向对象基础与this指向问题
Oct 16 Javascript
深入浅析javascript继承体系
Oct 23 Javascript
使用layui+ajax实现简单的菜单权限管理及排序的方法
Sep 10 Javascript
原生JavaScript实现简单五子棋游戏
Jun 28 Javascript
动态规划之使用备忘录来改进Javascript函数
Apr 07 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
德生S2000收音机更换“钕铁硼”全频扬声器
2021/03/02 无线电
PHP与MySQL交互使用详解
2006/10/09 PHP
实用函数3
2007/11/08 PHP
php mssql 数据库分页SQL语句
2008/12/16 PHP
PHP通用检测函数集合
2011/02/08 PHP
基于php socket(fsockopen)的应用实例分析
2013/06/02 PHP
PHP中$_SERVER使用说明
2015/07/05 PHP
深入浅析用PHP实现MVC
2016/03/02 PHP
PHP如何实现阿里云短信sdk灵活应用在项目中的方法
2019/06/14 PHP
Google Suggest ;-) 基于js的动态下拉菜单
2006/10/11 Javascript
js 表格隔行颜色
2009/12/02 Javascript
jquery获取下拉列表的值为null的解决方法
2011/03/18 Javascript
用javascript判断IE版本号简单实用且向后兼容
2013/09/11 Javascript
node.js中的fs.readSync方法使用说明
2014/12/17 Javascript
AngularJS ng-blur 指令详解及简单实例
2016/07/30 Javascript
javascript 中null和undefined区分和比较
2017/04/19 Javascript
vue.js之vue-cli脚手架的搭建详解
2017/05/05 Javascript
微信小程序时间标签和时间范围的联动效果
2019/02/15 Javascript
JS绘图Flot如何实现动态可刷新曲线图
2020/10/16 Javascript
[02:39]DOTA2英雄基础教程 极限穿梭编织者
2013/12/05 DOTA
pycharm 使用心得(八)如何调用另一文件中的函数
2014/06/06 Python
PYTHON 中使用 GLOBAL引发的一系列问题
2016/10/12 Python
Python语言描述最大连续子序列和
2017/12/05 Python
python根据文章标题内容自动生成摘要的实例
2019/02/21 Python
python GUI库图形界面开发之PyQt5切换按钮控件QPushButton详细使用方法与实例
2020/02/28 Python
Python日志logging模块功能与用法详解
2020/04/09 Python
Django web自定义通用权限控制实现方法
2020/11/24 Python
微软瑞士官方网站:Microsoft瑞士
2018/04/20 全球购物
Aurora London官网:奢华、负担得起的皮革手袋
2020/08/01 全球购物
历史专业毕业生的自我鉴定
2013/11/15 职场文书
会计实习自我鉴定
2013/12/04 职场文书
史学专业毕业生求职信
2014/05/09 职场文书
中央空调节能方案
2014/06/15 职场文书
幼儿园体操比赛口号
2015/12/25 职场文书
nginx反向代理配置去除前缀案例教程
2021/07/26 Servers
Nginx location 和 proxy_pass路径配置问题小结
2021/09/04 Servers