Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >js对象属性的getter和setter

js对象属性的getter和setter

作者头像
空空云
发布于 2018-09-27 03:45:43
发布于 2018-09-27 03:45:43
3.3K00
代码可运行
举报
文章被收录于专栏:大前端_Web大前端_Web
运行总次数:0
代码可运行

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://cloud.tencent.com/developer/article/1347519

在看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方法删除
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var o = {
  set current (str) {
    return this.log[this.log.length] = str;
  },
  log: []
}

此处current的值为undefined

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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也不可以
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div class="app">
    <input type="text" data-bind-id="demo">
    <p data-id="demo"></p>
</div>

javascript代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div class="app">
    <input type="demo" h-model="demo" spellcheck="true">

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

javascript代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    (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'
        }
    });
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年01月03日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
vue 入门
vue 的发展史 2013.12.24 发布0.7.0 2014.1.27发布0.8.0 2014.2.25 发布0.9.0 2014.3.24发布0.10.0 2015.10.27发布1.0.0 2016.4.27发布2.0分prebiew的版本 vue的两大特点 1.响应的数据绑定/响应式编程 2.组件化 vue的优缺点 优点:轻量级的框架 简单易学 双向数据绑定 组件化 视图,数据,结构分离 虚拟DOM 运行速度更快 缺点:支持特性 前后端混合受限
河湾欢儿
2018/09/06
5020
Vue.js 2 深入理解
含了父作用域中不作为 prop 被识别(且获取)的特性绑定(class 和 style 除外)
Cellinlab
2023/05/17
1.2K0
Vue.js 2 深入理解
自定义工具函数库(三)
开开心心收工?有点问题,如果对象中有循环引用,即”你中有我,我中有你”的话,就会导致形成死循环,会导致无法跑出结果,直到超出最大调用堆栈大小
赤蓝紫
2023/03/11
1.1K0
第十六章 vue数据监测原理
概念: **​​Object.defineProperty()​​ **方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
张哥编程
2024/12/13
850
腾讯二面vue面试题总结
对象内部通过 defineReactive 方法,使用 Object.defineProperty 来劫持各个属性的 setter、getter(只会劫持已经存在的属性),数组则是通过重写数组7个方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)
bb_xiaxia1998
2022/11/18
7500
vue的双向绑定原理及实现_vue的数据绑定怎么实现
​ 所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据。(数据变化更新视图,视图变化更新数据)
全栈程序员站长
2022/11/02
9840
腾讯前端一面常考vue面试题汇总2
vue构建函数调用_init方法,但我们发现本文件中并没有此方法,但仔细可以看到文件下方定定义了很多初始化方法
bb_xiaxia1998
2023/01/04
6750
全面梳理JS对象的访问控制及代理反射
在 Javascript 中,读取、赋值、调用方法等等,几乎一切操作都是围绕“对象”展开的;长久以来,如何更好的了解和控制这些操作,就成了该语言发展中的重要问题。
江米小枣
2020/06/16
2.2K1
JS面向对象
表示能否通过delete删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true
4O4
2022/04/25
7.6K0
这些js手写题对我这个菜鸟来说写不出来
请使用最基本的遍历来实现判断字符串 a 是否被包含在字符串 b 中,并返回第一次出现的位置(找不到返回 -1)。
helloworld1024
2022/09/25
6410
js对象属性描述符详细介绍
属性描述符是 ECMAScript 5 新增的语法,它其实就是一个内部对象,用来描述对象的属性的特性。
can4hou6joeng4
2023/11/17
3150
JS操作对象属性(获取、添加、删除、修改对象属性)
属性也称为名值对,包括属性名和属性值。属性名可以是包含空字符串在内的任意字符串,一个对象中不能存在两个同名的属性。属性值可以是任意类型的数据。
用户7741497
2022/03/19
16.9K0
手写 Vue (二):响应式
提到 Vue 的响应式,通常指的是视图跟随数据的改变而更新。开发上带来的便利是,在需要更新视图呈现时,只需修改视图渲染所需要的数据即可,而不用手动操作DOM。从实现来说,可以分为两个部分:
我是一条小青蛇
2020/12/09
7070
2023前端必会手写面试题整理1
我们说迭代器对象全凭迭代器生成函数帮我们生成。在ES6中,实现一个迭代器生成函数并不是什么难事儿,因为ES6早帮我们考虑好了全套的解决方案,内置了贴心的 生成器 (Generator)供我们使用:
helloworld1024
2023/01/03
5060
【前端基础进阶】JS-Object 功能详解
该方法主要用于对象的合并,将源对象source的所有可枚举属性合并到目标对象target上,此方法只拷贝源对象的自身属性,不拷贝继承的属性。 Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。同名属性会替换。
super.x
2019/04/12
1.5K0
【前端基础进阶】JS-Object 功能详解
京东前端二面必会vue面试题(持续更新中)_2023-02-24
Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。
用户10377014
2023/02/24
8680
彻底明白vue双向绑定底层原理(源码分析)
vue是一个mvvm框架,双向绑定是vue的一个核心功能,所谓双向绑定就是当试图发生改变的时候传递给VM(ViewModel ),让数据得到更新,当数据发生改变的时候传给VM(ViewModel ),使得视图发生变化!概念都知道,但是vue怎么做到的呢?看下面的一张图(图是搬运别人的)
全栈程序员站长
2022/10/05
7720
彻底明白vue双向绑定底层原理(源码分析)
深入讲解 Vue 中实现原理
随着 Vue2.0 的发布,前端入门的要求也越来越低,已至于 Vue 已经成为一个前端的标配,最近也面了很多前端开发工程师,发现大部分都停留在用的阶段上,建议大家看看源码,学学 Vue 的思想。
CSDN技术头条
2018/07/30
7920
js判断一个对象Object是否为空对象
js判断空对象的方法 判断一个js对象是否是空对象isEmptyObject author: @TiffanysBear 方法一:使用for…in遍历 var isEmptyObject = function () { for (var i in this) { return false; } return true; } // 尽量不要使用object.prototype直接进行修改 // 否则会为继承时生成的对象新增不必要的可枚举属性 // 同时可被for-in枚举到 Object
Tiffany_c4df
2019/09/04
8.1K0
深入 JS 对象属性
对象的普通属性将字符串名称映射到值。例如,下面对象obj有一个数据属性,名称为 prop,对应的值为 123:
前端小智@大迁世界
2019/09/10
8.8K0
相关推荐
vue 入门
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验