第一次尝试用思维导图记笔记,感觉还不错~~~不过还是改不了我读书笔记写成抄书笔记的毛病 =。=

因为开始学JS的时候,一般浏览器就已经支持ES6了,所以比较喜欢使用ES6语法,let,=>等,文中代码不是抄书的,都用了ES6。

 

《你不知道的JavaScript(上卷)》读书笔记

 

《你不知道的JavaScript(上卷)》读书笔记 

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>
View Code

详见:https://juejin.im/post/5acc17cb51882555745a03f8

 

 

相关文章: