<span style="font-size: 14px;">getters</span>
和 <span style="font-size: 14px;">setters</span>
。随着时间的推移,我开始喜欢上这个缺失的特性,因为相比Java大量的 <span style="font-size: 14px;">getter</span>
和 <span style="font-size: 14px;">setter</span>
,它让代码更简洁。例如,我们看看下面的Java代码:
<span style="font-size: 14px;">class Person{<br/> String firstName;<br/> String lastName; // 这个Demo中省略了一些构造器代码 :) public void setFirstName(firstName) { this.firstName = firstName;<br/> } public String getFirstName() { return firstName;<br/> } public void setLastName(lastName) { this.lastName = lastName;<br/> } public String getLastName() { return lastName;<br/> }<br/>}// Create instancePerson bradPitt = new Person();<br/>bradPitt.setFirstName("Brad");<br/>bradPitt.setLastName("Pitt");<br/></span>
JavaScript开发人员永远不会这样做,相反他们会这样:
<span style="font-size: 14px;">var Person = function () {};var bradPitt = new Person();<br/>bradPitt.firstName = 'Brad';<br/>bradPitt.lastName = 'Pitt';<br/></span>
这要简洁的多。通常简洁更好,不是吗?
的确如此,但有时我想获取一些可以被修改的属性,但我不用知道这些属性是什么。例如,我们在Java代码中扩展一个新的方法 <span style="font-size: 14px;">getFullName()</span>
:
<span style="font-size: 14px;">class Person{ private String firstName; private String lastName; // 这个Demo中省略了一些构造器代码 :) public void setFirstName(firstName) { this.firstName = firstName;<br/> } public String getFirstName() { return firstName;<br/> } public void setLastName(lastName) { this.lastName = lastName;<br/> } public String getLastName() { return lastName;<br/> } public String getFullName() { return firstName + " " + lastName;<br/> }<br/>}<br/><br/>Person bradPitt = new Person();<br/>bradPitt.setFirstName("Brad");<br/>bradPitt.setLastName("Pitt");// Prints 'Brad Pitt'System.out.println(bradPitt.getFullName());<br/></span>
在上面例子中, <span style="font-size: 14px;">fullName</span>
是一个计算过的属性,它不是私有属性,但总能返回正确的结果。
C# 和隐式的 getter/setters
我们来看看 C# 特性之一:隐式的 <span style="font-size: 14px;">getters</span>
/<span style="font-size: 14px;">setters</span>
,我真的很喜欢它。在 C# 中,如果需要,你可以定义 <span style="font-size: 14px;">getters</span>
/<span style="font-size: 14px;">setters</span>
,但是并不用这样做,但是如果你决定要这么做,调用者就不必调用函数。调用者只需要直接访问属性,<span style="font-size: 14px;">getter</span>
/<span style="font-size: 14px;">setter</span>
会自动在钩子函数中运行:
<span style="font-size: 14px;">public class Foo { public string FirstName {get; set;} public string LastName {get; set;} public string FullName {get { return firstName + " " + lastName }; private set;}<br/>}<br/></span>
我觉得这很酷...
现在,如果我想在JavaScript中实现类似的功能,我会浪费很多时间,比如:
<span style="font-size: 14px;">var person0 = {<br/> firstName: 'Bruce',<br/> lastName: 'Willis',<br/> fullName: 'Bruce Willis',<br/> setFirstName: function (firstName) { this.firstName = firstName; this.fullName = `${this.firstName} ${this.lastName}`;<br/> },<br/> setLastname: function (lastName) { this.lastName = lastName; this.fullName = `${this.firstName} ${this.lastName}`;<br/> },<br/>};<br/>console.log(person0.fullName);<br/>person0.setFirstName('Peter');<br/>console.log(person0.fullName);<br/></span>
它会打印出:
<span style="font-size: 14px;">"Bruce Willis"<br/>"Peter Willis"<br/></span>
但使用 <span style="font-size: 14px;">setXXX(value)</span>
的方式并不够'JavaScripty'(是个玩笑啦)。
下面的方式可以解决这个问题:
<span style="font-size: 14px;">var person1 = {<br/> firstName: 'Brad',<br/> lastName: 'Pitt',<br/> getFullName: function () { return `${this.firstName} ${this.lastName}`;<br/> },<br/>};<br/>console.log(person1.getFullName()); // 打印 "Brad Pitt"<br/></span>
现在我们回到被计算过的 <span style="font-size: 14px;">getter</span>
。你可以设置 <span style="font-size: 14px;">first</span>
或 <span style="font-size: 14px;">last name</span>
,并简单的合并它们的值:
<span style="font-size: 14px;">person1.firstName = 'Peter'person1.getFullName(); // 返回 "Peter Pitt"<br/></span>
这的确更方便,但我还是不喜欢它,因为我们要定义一个叫<span style="font-size: 14px;">getxxx()</span>
的方法,这也不够'JavaScripty'。许多年来,我一直在思考如何更好的使用 JavaScript。
然后 Vue 出现了
在我的Youtube频道,很多和Vue教程有关的视频都讲到,我习惯响应式开发,在更早的Angular1时代,我们叫它:数据绑定(Data Binding)。它看起来很简单。你只需要在Vue实例的 <span style="font-size: 14px;">data()</span>
块中定义一些数据,并绑定到HTML:
<span style="font-size: 14px;">var vm = new Vue({<br/> data() {<br/> return {<br/> greeting: 'Hello world!',<br/> };<br/> }<br/>})<div>{greeting}</div><br/></span>
显然它会在用户界面打印出 <span style="font-size: 14px;">“Hello world!”</span>
。
现在,如果你改变<span style="font-size: 14px;">greeting</span>
的值,Vue引擎会对此作出反应并相应地更新视图。
<span style="font-size: 14px;">methods: {<br/> onSomethingClicked() { this.greeting = "What's up";<br/> },<br/>}<br/></span>
很长一段时间我都在想,它是如何工作的?当某个对象的属性发生变化时会触发某个事件?或者Vue不停的调用 <span style="font-size: 14px;">setInterval</span>
去检查是否更新?
通过阅读Vue官方文档,我才知道,改变一个对象属性将隐式调用<span style="font-size: 14px;">getter</span>
/<span style="font-size: 14px;">setter</span>
,再次通知观察者,然后触发重新渲染,如下图,这个例子来自官方的Vue.js文档:
但我还想知道:
怎么让数据自带
<span style="font-size: 14px;">getter</span>
/<span style="font-size: 14px;">setters</span>
?这些隐式调用内部是怎样的?
第一个问题很简单:Vue为我们准备好了一切。当你添加新数据,Vue将会通过其属性为其添加 <span style="font-size: 14px;">getter</span>
/<span style="font-size: 14px;">setters</span>
。但是我让 <span style="font-size: 14px;">foo.bar = 3</span>
? 会发生什么?
这个问题的答案出现在我和SVG & Vue专家Sarah Drasner的Twitter对话中:
Timo:
<span style="font-size: 14px;">foo.bar=value;</span>
是怎么做到实时响应的?Sarah: 这个问题很难在Twitter说清楚,可以看这篇文章
Timo: 但这篇文章并没有解释上面提到的问题。
Timo: 它们就像:分配一个值->调用
<span style="font-size: 14px;">setter</span>
->通知观察者,不理解为什么在不使用<span style="font-size: 14px;">setInterval</span>
和<span style="font-size: 14px;">Event</span>
的情况下,<span style="font-size: 14px;">setter</span>
/<span style="font-size: 14px;">getter</span>
就存在了。Sarah: 我的理解是:你获取的所有数据都在Vue实例
<span style="font-size: 14px;">data{}</span>
中被代理了。
显然,她也是参考的官方文档,之前我也读过,所以我开始阅读Vue源码,以便更好的理解发生了什么。过了一会我想起在官方文档看到一个叫 <span style="font-size: 14px;">Object.defineProperty()</span>
的方法,我找到它,如下:
<span style="font-size: 14px;">/**<br/>* 给对象定义响应的属性<br/>*/export function defineReactive (<br/> obj: Object,<br/> key: string,<br/> val: any,<br/> customSetter?: ?Function,<br/> shallow?: boolean<br/>) { const dep = new Dep() const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false) { return<br/> } // 预定义getter/setters const getter = property && property.get const setter = property && property.set let childOb = !shallow && observe(val)<br/> Object.defineProperty(obj, key, {<br/> enumerable: true,<br/> configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) {<br/> dep.depend() if (childOb) {<br/> childOb.dep.depend()<br/> } if (Array.isArray(value)) {<br/> dependArray(value)<br/> }<br/> } return value<br/> }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* 禁用eslint 不进行自我比较 */ if (newVal === value || (newVal !== newVal && value !== value)) { return<br/> } /* 开启eslint 不进行自己比较 */ if (process.env.NODE_ENV !== 'production' && customSetter) {<br/> customSetter()<br/> } if (setter) {<br/> setter.call(obj, newVal)<br/> } else {<br/> val = newVal<br/> }<br/> childOb = !shallow && observe(newVal)<br/> dep.notify()<br/> }<br/> })<br/>}<br/></span>
所以答案一直存在于文档中:
把一个普通 JavaScript 对象传给 Vue 实例的
<span style="font-size: 14px;">data</span>
选项,Vue 将遍历此对象所有的属性,并使用<span style="font-size: 14px;">Object.defineProperty</span>
把这些属性全部转为<span style="font-size: 14px;">getter</span>
/<span style="font-size: 14px;">setter</span>
。<span style="font-size: 14px;">Object.defineProperty</span>
是仅 ES5 支持,且无法<span style="font-size: 14px;">shim</span>
的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
我只想简单的了解 <span style="font-size: 14px;">Object.defineProperty()</span>
做了什么,所以我用一个例子简单的给你讲解一下:
<span style="font-size: 14px;">var person2 = {<br/> firstName: 'George',<br/> lastName: 'Clooney',<br/>};<br/>Object.defineProperty(person2, 'fullName', {<br/> get: function () { return `${this.firstName} ${this.lastName}`;<br/> },<br/>});<br/>console.log(person2.fullName); // 打印 "George Clooney"<br/></span>
还记得文章开头C#的隐式 <span style="font-size: 14px;">getter</span>
吗?它们看起来很类似,但ES5才开始支持。你需要做的是使用 <span style="font-size: 14px;">Object.defineProperty()</span>
定义现有对象,以及何时获取这个属性,这个<span style="font-size: 14px;">getter</span>
被称为响应式——这实际上就是Vue在你添加新数据时背后所做的事。
<span style="font-size: 14px;">Object.defineProperty()</span>
能让Vue变的更简化吗?
学完这一切,我一直在想,<span style="font-size: 14px;">Object.defineProperty()</span>
是否能让Vue变的更简化?现今越来越多的新术语,是不是真的有必要把事情变得过于复杂,变的让初学者难以理解(Redux也是同样):
<span style="font-size: 14px;">Mutator</span>
:或许你在说(隐式)<span style="font-size: 14px;">setter</span>
<span style="font-size: 14px;">Getters</span>
:为什么不用<span style="font-size: 14px;">Object.defineProperty()</span>
替换成(隐式)<span style="font-size: 14px;">getter</span>
<span style="font-size: 14px;">store.commit()</span>
:为什么不简化成<span style="font-size: 14px;">foo = bar</span>
,而是<span style="font-size: 14px;">store.commit("setFoo", bar);</span>
?
你是怎么认为的?Vuex必须是复杂的还是可以像 <span style="font-size: 14px;">Object.defineProperty()</span>
一样简单?
相关推荐:
javascript用rem来做响应式开发实例分享
详解前端响应式布局、响应式图片,与自制栅格系统
响应式和自适应有什么区别