第一次尝试用思维导图记笔记,感觉还不错~~~不过还是改不了我读书笔记写成抄书笔记的毛病 =。=
因为开始学JS的时候,一般浏览器就已经支持ES6了,所以比较喜欢使用ES6语法,let,=>等,文中代码不是抄书的,都用了ES6。
1. 属性描述符(ES5开始)
获取属性描述符:
var myObject = { a:2 }; Object.getOwnPropertyDescriptor( myObject, "a" ); // { // value: 2, // writable: true, // enumerable: true, // configurable: true // }
设置属性描述符,被设置的属性可以定义过,也可以未定义过,
var myObject = {}; Object.defineProperty( myObject, "a", { value: 2, writable: false, // 不可写! configurable: true, enumerable: true });
其中:
writable 决定是否可以修改属性的值,如果设置为 false。修改属性值会静默失败(silently failed),严格模式会报错,TypeError。
configurable 决定属性是否可以配置。很显然,把configurable设置为false是单项的。并且无法撤销。
即便属性是 configurable:false,我们还是可以 把 writable 的状态由 true 改为 false,但是无法由 false 改为 true。
configurable:false 还会禁止删除这个属性,导致删除静默失败。
enumerable 控制属性是否会出现在对象的属性枚举中,默认为true。for..in 遍历的是可枚举属性。
2. 访问描述符
当给一个属性定义 getter、setter 或者两者都有时,这个属性会被定义为“访问描述 符”(和“数据描述符”相对)。对于访问描述符来说,JavaScript 会忽略它们的 value 和 writable 特性,取而代之的是关心 set 和 get(还有 configurable 和 enumerable)特性。
var myObject = { // 给 a 定义一个setter get a() { return 2 } } Object.defineProperty( myObject, // 目标对象 'b', // 属性名 { // 描述符 // 给 b 设置一个 getter get: function() { return this.a * 2 }, // 确保 b 会出现在对象的属性列表中 enumerable: true } ) console.log(myObject.a) // 2 console.log(myObject.b) // 4 a = 3 b = 5 console.log(myObject.a) // 2 console.log(myObject.b) // 4
getter 和 setter 一般是成对出现,如果只出现一个,会导致 set 不生效 / get 到 undefined。
有个getter和setter,就可以在设置数据的同时,做一些其他的事情了,vue的双向绑定,就是在数据set()里更新dom元素,同时在dom的input事件更新数据,实现双向绑定。代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <!-- HTML --> <div id="app"> <input type="text" v-model="number"> <button v-click="incre">+</button> <button v-click="minus">-</button> <button v-click="incre4">+4</button> <span v-bind="number"></span> </div> <!-- JavaScript --> <script> function MyVue(options) { // 先绑定基本数据 this.$el = document.querySelector(options.el) // vue 绑定的 dom 元素 this.$data = options.data this.$methods = options.methods // 根据 dom 获取数据都绑定了哪些 dom 元素 并记录 以便数据更新的时候 同步更新dom this._binding = {} // 初始化为空数组 Object.keys(this.$data).forEach((item) => { this._binding[item] = [] }) this._complie(this.$el) console.log(this._binding) Object.keys(this.$data).forEach((item) => { // 这里对value的使用是一个闭包?... let value = this.$data[item] Object.defineProperty(this.$data, item, { get: () => { console.log(`获取${item}: ${value}`) return value }, set: (val) => { // 更新 data 的时候要把相关 dom 节点全部更新 console.log(`更新${item}: ${val}`) if (val !== value) { value = val this._binding[item].forEach((meth) => { meth() }) } } }) }) } /** * @param {HTMLElement} root: vue 绑定的 dom 元素节点 **/ MyVue.prototype._complie = function(root) { // 如果有子节点 const nodes = root.children for (let i = 0; i < nodes.length; i++) { const node = nodes[i] if (node.children.length) { this._complie(node) } // 如果是bind 证明绑定了某个数据 那么改数据更改时 更改该处 dom if (node.hasAttribute('v-bind')) { const dataName = node.getAttribute('v-bind') const attr = (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA') ? 'value' : 'innerHTML' node[attr] = this.$data[dataName] // 初始化页面 this._binding[dataName].push(() => { console.log('v-bind: ', node, attr, dataName) node[attr] = this.$data[dataName] }) } // 如果有 v-click 就在点击事件中执行methods中对应的那个函数 if (node.hasAttribute('v-click')) { const methName = node.getAttribute('v-click') const method = this.$methods[methName] node.onclick = method.bind(this.$data) // method是对data中的数据进行操作,这里记得要把this绑到data上 } // 数据更改时更新 dom 节点 dom 节点更改时也更新 data if (node.hasAttribute('v-model')) { const dataName = node.getAttribute('v-model') node.value = this.$data[dataName] // 初始化页面 this._binding[dataName].push(() => { node.value = this.$data[dataName] }) node.addEventListener('input', () => { console.log('v-model', node) this.$data[dataName] = node.value }) } } } window.onload = function() { const app = new MyVue({ el: '#app', data: { number: 0, c: 1 }, methods: { incre: function() { console.log('incre...', this) this.number++ }, minus: function() { console.log('minus...', this) this.number-- }, incre4: function() { console.log('incre4...', this) this.number = Number(this.number) + 4 } } }) } </script> </body> </html>
详见:https://juejin.im/post/5acc17cb51882555745a03f8