第一次尝试用思惟导图记笔记,感受还不错~~~不过仍是改不了我读书笔记写成抄书笔记的毛病 =。=html
由于开始学JS的时候,通常浏览器就已经支持ES6了,因此比较喜欢使用ES6语法,let,=>等,文中代码不是抄书的,都用了ES6。vue
获取属性描述符: node
var myObject = { a:2 }; Object.getOwnPropertyDescriptor( myObject, "a" ); // { // value: 2, // writable: true, // enumerable: true, // configurable: true // }
设置属性描述符,被设置的属性能够定义过,也能够未定义过,es6
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 还会禁止删除这个属性,致使删除静默失败。app
enumerable 控制属性是否会出如今对象的属性枚举中,默认为true。for..in 遍历的是可枚举属性。dom
当给一个属性定义 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
他们都是定义在Function.prototype里面的函数,都有绑定this的功能。原型以下:
call(thisArg: any, args...: any)
apply(thisArg: any, argArray: Array)
apply能够把存放参数数组直接传递,若是参数存在一个数组里,使用apply将很方面。
能够经过call来将某个对象的函数应用在其余对象: Object.prototype.toString.call(something)
bind也能够绑定this,并返回一个函数,同时bind还有一个功能,就是绑定传入的函数。
function sayHello(arg1, arg2) { console.log(arg1, arg2) console.log('hello, i am ' + this.name) } name = 'global' let p1 = { name: 'xiaoming' } let p2 = { name: 'hanmeimei' } sayHello('arg1', 'arg2') // i am global sayHello.apply(p1, ['apply1', 'apply2']) // i am xiaoming sayHello.apply(p2, ['apply1', 'apply2']) // i am hanmeimei sayHello.call(p1, 'call1', 'call2') // i am xiaoming sayHello.call(p2, 'call1', 'call2') // i am hanmeimei let sayHelloWithBind = sayHello.bind(p1, '参数1') sayHelloWithBind('参数2') // 参数1 参数2 hello, i am xiaoming
若是使用内置的 .bind(..) 函数来生成一个硬绑定函数的话, 该函数是没有 .prototype 属性的。在这样的函数上使用 instanceof 的话, 目标函数的 .prototype 会代替硬绑定函数的 .prototype。
function Foo() {} Bar = Foo.bind({}) a = new Bar() console.log(a instanceof Foo) // true console.log(a instanceof Bar) // true
有点多,不想写了。。。。参考 Module 的语法
JavaScript没有构造函数,只有函数的构造调用,
JavaScript没有类,只有对象
了解一下随意的 constructor
function Foo() { ... } var a = new Foo(); a.constructor === Foo; // true
constructor,所谓的“构造函数”,实际上是Foo.prototype的,a自己并无这个属性 a.hasOwnProperty('constructor') // false
也就是说 Foo.prototype.constructor === Foo; // true 而和a是怎么生成的没有什么关系。
若是先设置 Foo.prototype={ ... } 那么 var a = new Foo(); 生成的a对象的constructor也就是Object。
a在new的时候,关联到了Foo.prototype,若是你修改了 Foo.prototype = ... a所关联的对象是不变的。
.constructor 并非一个不可变属性。它是不可枚举的,可是它的值是可写的。此外,你能够给任意 [[Prototype]] 链中的任意对象添加一个名 为 constructor 的属性或者对其进行修改,你能够任意对其赋值。
综上,.constructor 是一个很是不可靠而且不安全的引用。一般来讲要尽可能避免使用这些引用。
原型继承
function Foo(name) { this.name = name } Foo.prototype.myName = function() { return this.name } function Bar(name, label) { Foo.call(this, name) this.label = label } // 为 Bar.prototype 重新赋值一个 [[Prototype]] 为 Foo.prototype 的对象 // 此时 Bar.prototype 是 没有constructor 属性的 Bar.prototype = Object.create(Foo.prototype) Bar.prototype.myLabel = function() { return this.label } var a = new Bar('a', 'obj a') console.log(a.myName()) console.log(a.myLabel())
咱们来对比一下两种把 Bar.prototype 关联到 Foo.prototype 的方法:
// ES6 以前须要抛弃默认的 Bar.prototype Bar.ptototype = Object.create( Foo.prototype ) // ES6 开始能够直接修改现有的 Bar.prototype Object.setPrototypeOf( Bar.prototype, Foo.prototype )
JavaScript中没有类,只有对象,因此没有类继承,只有对象的委托,经过 b=Object.create(a) 能够将 b 的[[Prototype]] 属性设为 a,这样当在b中查找属性找不到的时候就能够找到a。a中查找不到就继续沿原型链查找。最终通常会查找到Object.prototype。默认字面量对象的[[Prototype]]是Object.prototype。这样,只要在Object.prototype上定义一些函数toString(), valueOf()等,全部对象均可以使用。
new Foo() 操做能够生成一个对象,而后将对象的[[Prototype]] 绑定到Foo.prototype,并将Foo的this绑定为这个新函数,若是Foo()没有返回值的话,该函数将做为返回值。能够看出new像是一个辅助功能,来方面在JS中模拟相似类的操做。注意,若是Foo返回了一个对象,那个new返回的也就是那个对象,新生成的对象将被抛弃。而那个对象,可能和Foo没有任何关系。
对象关联(OLOO, objects linked to other objects)
Task = { setID: function(ID) { this.id = ID; }, outputID: function() { console.log(this.id); } }; // 让XYZ委托Task XYZ = Object.create(Task); XYZ.prepareTask = function(ID, Label) { this.setID(ID); this.label = Label; }; XYZ.outputTaskDetails = function() { this.outputID(); console.log(this.label); }; // ABC = Object.create( Task ); // ABC ... = ... XYZ.prepareTask('123', 'Task-xyz') XYZ.outputTaskDetails()
在上面的代码中,id 和 label 数据成员都是直接存储在 XYZ 上;在委托行为中咱们会尽可能避免在 [[Prototype]] 链的不一样级别中使用相同的命名,不然就须要使用笨拙而且脆弱的语法来消除引用歧义。你没法在两个或两个以上互相(双向)委托的对象之间建立循环委托。
其实对象关联的风格更容易理解,而原型模式反而像是为了“模拟类”而出现的风格。直接经过new操做符来执行函数的内容,同时将对象的原型连接到函数的prototype。instanceof 专门用来检查经过这个方法建立对象后两这的关联。
理解了这本书讲的内容,其实这张图也不是很难看懂……
Function.prototype = Function.__proto__ = Object.__proto__ Function.prototype.__proto__ = Function.__proto__.__proto__ = Object.__proto__.__proto__ = Object.prototype Object.prototype.__proto__ = null // Object.prototype 是对象 Function.prototype.prototype = undefined // Function.prototype 是函数