理解defineProperty以及getter、setter
我们常听说vue是用getter与setter实现数据监控的,那么getter与setter到底是什么东西,它与defineProperty是什么关系,平时有哪些用处呢?本文将为大家一一道来。
对象的属性
按照一贯的“由浅到深”行文原则,我们先温习一下对象的属性。我们知道对象有自身的属性以及原型上的属性,它们都可以通过obj.key这样的方式访问到。
要设置/修改对象的属性也是很简单的,只需obj.key='value'即可。要注意的是,如果key位于原型上,那么此时会在对象自身设置该值,而不是修改原型上的。
另外需要注意的是,原型上的属性有时候会被for in给“不小心”遍历出来,例如下面的代码:
var arr = [1,2,3];
arr.__proto__.test = 4;
for(i in arr){
console.log(arr[i]);
}
//输出:1234
所以我们一般在用for in的时候都要加上hasOwnProperty判断,或者是抛弃for in,用forEach.
认识defineProperty
defineProperty是挂载在Object上的一个方法,作用是:为对象定义一个属性,或是修改已有属性的值,并设置该属性的描述符。该方法返回修改后的对象。
如果没有后半句作用的话,那它与obj.key = 'value'这种赋值语句没什么两样。他的完整语法是这样:Object.defineProperty(obj, prop, descriptor)
obj: 目标对象
prop: 属性名称
descriptor: 属性描述符
前两个就不必讲了,需要重点理解的是第三参数。属性描述符用于定义该属性的一些特性。具体来讲分了两类:数据描述符(data descriptor)、访问描述符(accessor descriptor).
这两类描述符有两个必选项:
configurable
从字面意思看它表示“可配置”,含义是:当它为true时,该属性的描述符可被修改,并且该属性可被delete删除。同理,当它为false时,我们无法再次调用defineProperty去修改描述符,也不可通过delete删除。enumerable
从字面意思看它表示“可枚举”,含义是:当它为true时,该属性可被迭代器枚举出来。比如使用for in或者是Object.keys。
接下来就是数据描述符(data descriptor)了,有两个:
value
这个就是该属性的值啦,即通过obj.key访问时返回。任何js数据类型都可以使用(number,string,object,function等)。writable
这个也很好理解,表示该属性是否可写。当它为false时,属性不可被任何赋值语句重写。然而,此时还可以调用defineProperty来修改value,当然前提是configurable为true啦。
剩下的就是访问描述符啦,先卖个关子讲两个注意事项。
描述符的原型与默认值
一般情况,我们会创建一个descriptor对象,然后传给defineProperty方法。如下:
var descriptor = {
writable: false
}
Object.defineProperty(obj, 'key', descriptor);
这种情况是有风险的,如果descriptor的原型上面有相关特性,也会通过原型链被访问到,算入在对key的定义中。比如:
descriptor.__proto__.enumerable = true;
Object.defineProperty(obj, 'key', descriptor);
Object.getOwnPropertyDescriptor(obj,'key'); //返回的enumerable为true
为了避免发生这样的意外情况,官方建议使用Object.freeze冻结对象,或者是使用Object.create(null)创建一个纯净的对象(不含原型)来使用。
接下来的注意点是默认值,首先我们会想普通的赋值语句会生成怎样的描述符,如obj.key="value"
。
可以使用Object.getOwnPropertyDescriptor来返回一个属性的描述符:
obj = {};
obj.key = "value";
Object.getOwnPropertyDescriptor(obj, 'key');
/*输出
{
configurable:true,
enumerable:true,
value:"value",
writable:true,
}
*/
这也是复合我们预期的,通过赋值语句添加的属性,相关描述符都为true,可写可配置可枚举。但是使用defineProperty定义的属性,默认值就不是这样了,其规则是这样的:
configurable: false
enumerable: false
writable: false
value: undefined
所以这里还是要注意下的,使用的时候把描述符写全,免得默认都成false了。
getter与setter
所谓getter与setter其实是两个概念,并没有这样的属性。与之对应的是两个访问描述符(access descriptor):
- get
它是一个函数,访问该属性时会自动调用,函数的返回值即为该属性的value。默认为undefined。
你可能会想,既有value又有get函数,那么属性的值是什么呢?那你就想多了,这种情况在定义的时候就直接报错了,本身逻辑就矛盾嘛。
- set
它是一个函数,为该属性赋值时会自动调用,并且新值会被当做参数传入。
看到这里你可能就眼前一亮了,为属性赋值的时候会自动执行一个函数,那岂不是就能监控到数据的变化,从而实现mvvm的双向绑定?其实vue的数据监控用到的核心原理也就是这个啦。如果你用过knockout可能感受会更深,knockout能做到在IE6都支持双向绑定,就是强制让属性值为函数类型,必须手动执行函数才能拿到值。
还好现在有了浏览器的默认支持,ES5开始就支持gettter、setter了,现在移动端基本完全可用,pc端需要IE9+。
实际应用
这么好用的方法,我们平时好像也不怎么用呀?写业务代码可能用到的确实少,但是当你要写一个公共模块乃至写一个框架时,就可能用到啦。
比如你写一个公共模块,会往window上挂一些全局属性,并且你不希望别人在其他地方不小心覆盖这个属性,那就可以用defineProperty让该属性不可写、不可配置。贴一个我们项目中的代码:
//向全局挂载通用方法
for(let key in methods){
if(methods.hasOwnProperty(key)){
Object.defineProperty(WIN, key, {
value : methods[key]
});
}
}
另外一个用途呢,就是你自己想干坏事。覆盖别人写的代码,比如写chrome插件刷页面。或者说是想篡改浏览器的一些信息。
比如你想把浏览器的userAgent给改了,直接写navigator.userAgent = 'iPhoneX'
.你再输出一下userAgent,发现并没有修改。这是为什么呢?我们用这行代码看一下:
Object.getOwnPropertyDescriptor(window, 'navigator');
//输出
{
configurable:true,
enumerable:true,
get:ƒ (),
set:undefined
}
原因就找到了,navigator是有setter的,每次取值总会执行这个set函数来做返回。但是好消息是什么呢?configurable为true,那就意味这我们可以通过defineProperty来修改这个属性,代码就相当简单了:
Object.defineProperty(navigator, 'userAgent', {get: function(){return 'iphoneX'}})
console.log(navigator.userAgent); //输出iphoneX
喏,篡改浏览器userAgent的方法我教给你了。
理解defineProperty以及getter、setter的更多相关文章
- 懒加载(getter\setter理解)
为什么要用懒加载 1.首先看一下程序启动过程:(如图) 会有一个mian的设置,程序一启动会加载main.storyboard main.storyboard又会加载箭头所指的控制器 控制器一旦加载, ...
- Java程序猿JavaScript学习笔记(4——关闭/getter/setter)
计划和完成这个例子中,音符的顺序如下: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScr ...
- es6 getter setter
https://stackoverflow.com/questions/34517538/setting-an-es6-class-getter-to-enumerable 1. 我要 getter ...
- iOS getter setter
getter setter 给成员变量起名字用的 setter方法 设置成员变量值 1. setter 方法一定是对象方法 不可能是类方法 2.一定没有返回值 3. 以set开头,并且set后面跟上需 ...
- Lombok(1.14.8) - @Getter, @Setter, @ToString, @EqualsAndHashCode & @Data
@Getter / @Setter @Getter 和 @Setter,分别实现了 Gette r和 Setter 方法. package com.huey.hello.bean; import ja ...
- lombok @Getter @Setter 使用注意事项
lombok是一个帮助简化代码的工具,通过注解的形式例如@Setter @Getter,可以替代代码中的getter和setter方法,虽然eclipse自带的setter.getter代码生成也不需 ...
- lombok(@Getter&@Setter)
Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法. 官方地址:https://project ...
- 自动生成getter setter
如何使用java黑魔法给一个entity生成getter,setter方法? 由于java是一门静态语言,要给一个类动态添加方法,看似是不可能的.但牛B的程序员会让任何事情发生.我只知道有两种方式可以 ...
- 为什么要使用getter/setter
变量私有化的好处 1. 在setter中可以加入合法性检查,比如设置颜色的函数中,对于RGB颜色要判断其值在0~255之间. 2. 更新与被设置变量相关的其它变量的值,比如在一个潜水艇模拟系统中,改变 ...
随机推荐
- 【转】嵌入式C语言调试开关
在调试程序时,经常会用到assert和printf之类的函数,我最近做的这个工程里就有几百个assert,在你自认为程序已经没有bug的时候,就要除去这些调试代码,应为系统在正常运行时这些用于调试的信 ...
- <Mastering KVM Virtualization>:第四章 使用libvirt创建你的第一台虚拟机
在第3章<搭建独立的KVM虚拟化>中,你安装并启动了libvirtd服务.你还引入了帮助你管理虚拟机的libvirt管理工具virt-manager和virsh. 相较于命令行,新用户总是 ...
- BigDecimal与Long之间的转换
新建了一个class类 取名叫Firut import java.math.BigDecimal; public class Firut { private String id; private Bi ...
- 《Spark Python API 官方文档中文版》 之 pyspark.sql (一)
摘要:在Spark开发中,由于需要用Python实现,发现API与Scala的略有不同,而Python API的中文资料相对很少.每次去查英文版API的说明相对比较慢,还是中文版比较容易get到所需, ...
- 2456: mode
2456: mode Time Limit: 1 Sec Memory Limit: 1 MBSubmit: 4798 Solved: 2009[Submit][Status][Discuss] ...
- two.js之实现动画效果
一.什么是two.js? Two.js 是面向现代 Web 浏览器的一个二维绘图 API.Two.js 可以用于多个场合:SVG,Canvas 和 WebGL,旨在使平面形状和动画的创建更方便,更简洁 ...
- Log4j 2翻译 Garbage-free Steady State Logging(稳定的以不会生成垃圾的状态来记录日志)
本人菜鸟,在学习Log4j 2 的时候做的一些笔记---对"官方网站"的翻译,部分内容自己也不懂,希望大家指点 Garbage collection pauses are a co ...
- JS是按值传递还是按引用传递?【转载】
最近遇到个有趣的问题:“JS中的值是按值传递,还是按引用传递呢?” 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference). ...
- Python爬虫入门:URLError异常处理
大家好,本节在这里主要说的是URLError还有HTTPError,以及对它们的一些处理. 1.URLError 首先解释下URLError可能产生的原因: 网络无连接,即本机无法上网 连接不到特定的 ...
- Task及Mvc的异步控制器 使用探索
微软的Task已经出来很久了,一直没有去研究,以为就是和Thread差不多的东西.直到最近看到了Task的使用介绍,发现比Thread的语法要精炼多了,于是便在项目中用上了. 结果就出问题了,数据库连 ...