《你不知道的JavaScript(上卷)》读书笔记
第一次尝试用思维导图记笔记,感觉还不错~~~不过还是改不了我读书笔记写成抄书笔记的毛病 =。=
因为开始学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 设置一个 getterget: function() { return this.a * 2},// 确保 b 会出现在对象的属性列表中enumerable: true} )console.log(myObject.a) // 2 console.log(myObject.b) // 4 a = 3 b = 5console.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.datathis.$methods = options.methods// 根据 dom 获取数据都绑定了哪些 dom 元素 并记录 以便数据更新的时候 同步更新domthis._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 = valthis._binding[item].forEach((meth) => {meth()})}}})})}/*** @param {HTMLElement} root: vue 绑定的 dom 元素节点**/MyVue.prototype._complie = function(root) {// 如果有子节点 const nodes = root.childrenfor (let i = 0; i < nodes.length; i++) {const node = nodes[i]if (node.children.length) {this._complie(node)}// 如果是bind 证明绑定了某个数据 那么改数据更改时 更改该处 domif (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 节点更改时也更新 dataif (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
3. call、apply、bind 的联系和区别
他们都是定义在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
4. import和export
有点多,不想写了。。。。参考 Module 的语法
5. 类与对象
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.prototypeObject.prototype.__proto__ = null // Object.prototype 是对象 Function.prototype.prototype = undefined // Function.prototype 是函数
转载于:https://www.cnblogs.com/wenruo/p/9329352.html
《你不知道的JavaScript(上卷)》读书笔记相关推荐
- 读书笔记 | 墨菲定律
1. 有些事,你现在不做,永远也不会去做. 2. 能轻易实现的梦想都不叫梦想. 3.所有的事都会比你预计的时间长.(做事要有耐心,要经得起前期的枯燥.) 4. 当我们的才华还撑不起梦想时,更要耐下心来 ...
- 读书笔记 | 墨菲定律(一)
1. 有些事,你现在不做,永远也不会去做. 2. 能轻易实现的梦想都不叫梦想. 3.所有的事都会比你预计的时间长.(做事要有耐心,要经得起前期的枯燥.) 4. 当我们的才华还撑不起梦想时,更要耐下心来 ...
- 洛克菲勒的38封信pdf下载_《洛克菲勒写给孩子的38封信》读书笔记
<洛克菲勒写给孩子的38封信>读书笔记 洛克菲勒写给孩子的38封信 第1封信:起点不决定终点 人人生而平等,但这种平等是权利与法律意义上的平等,与经济和文化优势无关 第2封信:运气靠策划 ...
- 股神大家了解多少?深度剖析股神巴菲特
股神巴菲特是金融界里的传奇,大家是否都对股神巴菲特感兴趣呢?大家对股神了解多少?小编最近在QR社区发现了<阿尔法狗与巴菲特>,里面记载了许多股神巴菲特的人生经历,今天小编简单说一说关于股神 ...
- 2014巴菲特股东大会及巴菲特创业分享
沃伦·巴菲特,这位传奇人物.在美国,巴菲特被称为"先知".在中国,他更多的被喻为"股神",巴菲特在11岁时第一次购买股票以来,白手起家缔造了一个千亿规模的 ...
- 《成为沃伦·巴菲特》笔记与感想
本文首发于微信公众帐号: 一界码农(The_hard_the_luckier) 无需授权即可转载: 甚至无需保留以上版权声明-- 沃伦·巴菲特传记的纪录片 http://www.bilibili.co ...
- 读书笔记002:托尼.巴赞之快速阅读
读书笔记002:托尼.巴赞之快速阅读 托尼.巴赞是放射性思维与思维导图的提倡者.读完他的<快速阅读>之后,我们就可以可以快速提高阅读速度,保持并改善理解嗯嗯管理,通过增进了解眼睛和大脑功能 ...
- 读书笔记001:托尼.巴赞之开动大脑
读书笔记001:托尼.巴赞之开动大脑 托尼.巴赞是放射性思维与思维导图的提倡者.读完他的<开动大脑>之后,我们就可以对我们的大脑有更多的了解:大脑可以进行比我们预期多得多的工作:我们可以最 ...
- 读书笔记003:托尼.巴赞之思维导图
读书笔记003:托尼.巴赞之思维导图 托尼.巴赞的<思维导图>一书,详细的介绍了思维发展的新概念--放射性思维:如何利用思维导图实施你的放射性思维,实现你的创造性思维,从而给出一种深刻的智 ...
- 产品读书《滚雪球:巴菲特和他的财富人生》
作者简介 艾丽斯.施罗德,曾经担任世界知名投行摩根士丹利的董事总经理,因为撰写研究报告与巴菲特相识.业务上的往来使得施罗德有更多的机会与巴菲特亲密接触,她不仅是巴菲特别的忘年交,她也是第一个向巴菲特建 ...
最新文章
- 《Linux From Scratch》第二部分:准备构建 第五章:构建临时文件系统- 5.11. Tcl-8.6.3...
- 初识Service Worker
- C++ primer学习方法
- 数据量很大,分页查询很慢,有什么优化方案?
- android 日期时间类,Android 日期时间等转换工具类
- python安装方法及运行_Python下载及其安装步骤
- 冯鑫涉嫌犯罪被带走 暴风集团开盘即跌停
- java微调器_java-更改微调器标题栏样式
- windows本地script脚本恶意代码分析(带注释)
- Python3.7+RF3.1实现接口自动化
- matlab中可调节负载,请教MOC3022控制可控硅的电路调节负载功率的问题
- WebView复制粘贴文本
- EXCEL之绝对引用、相对引用和混合引用
- 软件测试教学实训平台
- 进程和线程的主仆问题
- poi excel下载
- windows通过iscsi挂载linux硬盘
- Linux服务器如何开启某个端口?
- 好文章也是要色香味俱全的!——CVPR2010最有趣文章
- Python爬取爱徒网素材下载链接,点击链接即可下载
热门文章
- Adopting Modern Objective-C
- asp.net 应用数据缓存 -- Cache对象使用
- 数据挖掘之数据预处理
- 纽约时代广场广告费才7千元每天,花钱装逼值不值?
- 哪些钱借了可以不还?
- 说不尽的蒙古人:推荐多兰的诗歌
- stepinto stepout stepover的区别
- 图像处理一些常用的网站
- ue4 无限地图_UE4大地图(流关卡、无缝地图)
- xx是一个类型 这在给定的上下文_BERT, ELMo, amp; GPT-2: 这些上下文相关的表示到底有多上下文化?...