js对象属性的getter和setter

原创 2017年01月03日 17:02:01

在看Vue的API时,里面提到修改Model层,会实时更新View视图,底层原理利用的是ES5的getter和setter方法,通过 Object.defineProperty 把实例属性全部转为 getter/setter。故温故一遍getter和setter定义属性的方法。

通过对象字面量定义get和set方法

有个注意的地方,get与set的函数体都不能再定义本身该属性,否则执行的时候会陷入死循环,抛出栈溢出。

  • 使用get语法时,不能带参数;然而set必须有一个明确的参数。

  • 在对象字面量中,同一个属性不能有两个get,也不能既有get又有属性键值(不允许使用 { get x() { }, get x() { } } 和 { x: …, get x() { } } )

  • 在同一个对象中,不能为一个已有真实值的变量使用 set ,也不能为一个属性设置多个 set。
    ( { set x(v) { }, set x(v) { } } 和 { x: …, set x(v) { } } 是不允许的 )

  • get和set都能用delete方法删除


var o = {
  set current (str) {
    return this.log[this.log.length] = str;
  },
  log: []
}

此处current的值为undefined

var o = {
  set current (str) {
    this.current = str 
    return this.log[this.log.length] = str;
  },
  log: []
};
o.current = 'a' //抛出错误,进入死循环
//Uncaught RangeError: Maximum call stack size exceeded

使用 Object.defineProperty 方法

  • 与对象字面量不同,使用 Object.defineProperty 方法可以为任何已存在的属性重新定义get与set方法。

  • get的返回值直接为该属性的值。

  • 可以定义configurable、enumerable,默认都为false。但是如果定义了set或者get方法中的任何一个,就不能再设置writable,即便false也不可以

var o = { a:0 }

Object.defineProperty(o, "b", { get: function () { return this.a + 1; } });

console.log(o.b) // Runs the getter, which yields a + 1 (which is 1)

下面这个代码会报错TypeError

    var o = {a:0}
    Object.defineProperty(o, "b", {
        set: function (val) {
          this.a = val + 2
        },
        get: function () {
            return this.a + 1;
        },
        writable: true,
        configurable: true,
        enumerable: true
    });
//执行报错:Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
//删除writable属性就可以了

如何实现数据双向绑定

双向数据绑定底层的思想非常的基本,它可以被压缩成为三个步骤:

1.我们需要一个方法来识别哪个UI元素被绑定了相应的属性

2.我们需要监视属性和UI元素的变化

3.我们需要将所有变化传播到绑定的对象和元素

方法一:利用发布订阅模式,订阅数据变更
html代码

<div class="app">
    <input type="text" data-bind-id="demo">
    <p data-id="demo"></p>
</div>

javascript代码

    function DataBinder(object_id) {
        //创建一个简单地PubSub对象
        var pubSub = {
                    callbacks: {},
                    on: function (msg, callback) {
                        this.callbacks[msg] = this.callbacks[msg] || [];
                        this.callbacks[msg].push(callback);
                    },

                    publish: function (msg) {
                        this.callbacks[msg] = this.callbacks[msg] || [];
                        for (var i = 0, len = this.callbacks[msg].length; i < len; i++) {
                            this.callbacks[msg][i].apply(this, Array.prototype.slice.call(arguments, 1));
                        }
                    }
                };

        var data_attr = "data-bind-" + object_id,
                view_attr = 'data-' + object_id,
                message = object_id + ":change";

        var changeHandler = function (e) {
            var target = e.target || e.srcElemnt, //IE8兼容
                prop_name = target.getAttribute(data_attr);

            if (prop_name && prop_name !== "") {
                pubSub.publish(message, target, prop_name, target.value);
            }
        };

        //监听变化事件并代理到PubSub
        if (document.addEventListener) {
            document.addEventListener("keyup", changeHandler, false);
        } else {
            //IE8使用attachEvent而不是addEventListener
            document.attachEvent("keyup", changeHandler);
        }

        //PubSub将变化传播到所有绑定元素
        pubSub.on(message, function (event, prop_name, new_val) {
            var elements = document.querySelectorAll("[" + view_attr + "=" + prop_name + "]"), tag_name;

            for (var i = 0, len = elements.length; i < len; i++) {
                tag_name = elements[i].tagName.toLowerCase();

                if (tag_name === "input" || tag_name === "textarea" || tag_name === "select") {
                    elements[i].value = new_val;
                } else {
                    elements[i].innerHTML = new_val;
                }
            }
        });

        return pubSub;
    }
    DataBinder('id');

方法二:数据劫持
html代码

<div class="app">
    <input type="demo" h-model="demo" spellcheck="true">

    <p h-text="demo"></p>
</div>

javascript代码

    (function(window, undefined) {

        var addEvent = (function () {
            if(window.addEventListener) {
                return function (el, type, fn) {
                    if(el && el.nodeName || el === window) {
                        el.addEventListener(type, fn, false)
                    }else if(el.length) {
                        for(var item of el) {
                            addEvent(item, type, fn);
                        }
                    }
                }
            }else {
                return function (el, type, fn) {
                    if(el && el.nodeName || el === window) {
                        el.attachEvent('on' + type, function () {
                            return fn.call(el, event);
                        })
                    }else if(el.length) {
                        for(var item of el) {
                            addEvent(item, type, fn);
                        }
                    }
                }
            }
        })();

        var Hue = function(opt) {
            var el = document.querySelector(opt.el);
            var data = opt.data || {};

            this.el = el;
            this.data = data;

            this.bindText();
            this.bindModel();

            return this;
        };

        Hue.prototype = {
            constructor: Hue,
            // **前端数据劫持**
            defineObj: function(obj, prop, value) {
                var _value = value || '', _this = this;

                try {
                    Object.defineProperty(obj, prop, {
                        get: function() {
                            return _value;
                        },
                        set: function(newVal) {
                            _value = newVal;
                            _this.bindText();
                        },
                        enumerable: true,
                        configurable: true
                    });
                } catch (error) {

                    // IE8+ 才开始支持defineProperty,这也是Vue.js不支持IE8的原因
                    console.log("Browser must be IE8+ !");
                }
            },
            bindModel: function() {
                var modelDOMs = this.el.querySelectorAll('[h-model]'), lenModel = modelDOMs.length;

                var _this = this, i, propModel;

                for (i = 0; i < lenModel; i++) {
                    propModel = modelDOMs[i].getAttribute('h-model');

                    //init value
                    modelDOMs[i].value = this.data[propModel] || '';

                    // 前端数据劫持
                    this.defineObj(this.data, propModel);
                }

                addEvent(modelDOMs, 'keyup', function (e) {
                    _this.data[propModel] = e.target.value;
                })

            },
            bindText: function() {
                var textDOMs = this.el.querySelectorAll('[h-text]'),
                        lenText = textDOMs.length,
                        prppText,
                        j;

                for (j = 0; j < lenText; j++) {
                    propText = textDOMs[j].getAttribute('h-text');

                    textDOMs[j].innerHTML = this.data[propText] || '';
                }
            }
        };

        window.Hue = Hue;

    })(window);

    // test...
    new Hue({
        el: '.app',
        data: {
            demo: 'Kenny'
        }
    });
版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 举报

相关文章推荐

JavaScript中的setter和getter方法!!

javascript中的setter、getter是平时接触比较少的方法,其本身也并不是标准方法,只在非ie浏览器里支持(ie内核也许有其他方法可以做到呢?暂时不知其解),但是加以利用可以做许多事情,...

JavaScript对象中属性的getter和setter方法

JavaScript对象的属性是由名字、值和一组特性(可写、可枚举、可配置等)构成的。在ECMAScript 5中,属性值可以用一个或两个方法代替,这两个方法就是getter和setter。var m...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

JavaScript对象属性的getter和setter

JavaScript对象属性的getter和setter

JS的getter和setter

javascript getter和setter的两种写法
  • cceevv
  • cceevv
  • 2016-03-15 11:05
  • 1241

easyUI组件datagrid的二次封装

项目中经常用到easyUI的组件datagird,每次重复的属性写很多(copy-paste),架构师把这活安排给我了,苦逼。。 项目是后台系统,表格行的增删改查几乎都有,有些需求还包括排序,所以写了...
  • wkyseo
  • wkyseo
  • 2016-04-23 09:48
  • 5197

深入解析js中基本数据类型与引用类型,函数参数传递的区别

ECMAScript的数据有两种类型:基本类型值和引用类型值,基本类型指的是简单的数据段,引用类型指的是可能由多个值构成的对象。 Undefined、Null、Boolean、Number和Strin...
  • wkyseo
  • wkyseo
  • 2016-05-23 20:30
  • 2509

项目中遇到的bug(web前端-持续更新)

项目中遇到的常见bug,及时整理。 input放在a标签里面单机不能获取input的光标(IE环境下)双击才可以获得焦点,目前有的解决方案: 不要给a标签添加href属性; 不要在外面套上a标签。 返...
  • wkyseo
  • wkyseo
  • 2016-04-15 10:11
  • 2010

NodeJS学习二CommonJS规范

概述Node程序由许多个模块组成,每个模块就是一个文件。Node模块采用了CommonJS规范。根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文...
  • wkyseo
  • wkyseo
  • 2016-04-26 12:25
  • 1826

面向对象的程序设计——(一)理解对象的属性以及属性的特性

一、什么是对象?        对象是无序属性的集合,属性包括:基本数据类型、对象或者函数。 二、创建对象的方式?        1、创建一个实例,如:          ...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)