JS是动态类型语言。
关于“变”与“不变”
JS中的数据类型分为原始值类型和引用值类型。数据的可变性由值类型决定,原始值类型的值不变,引用值类型的值可变。
变量不是值,只是找到值的快捷方式。所以变量总是可变的,但是值根据其类型,可能不可变,也可能可变。
如let name="tom",name[0]="T",输出name仍是tom。
因为name变量指向的值“tom”是string类型,是基本值类型,其值是不可变的。
关于函数
JS中的函数是原始值类型,是一等公民,不仅可以传给变量,而且可以作为函数的返回值。使得函数式编程更加容易。
关于闭包
闭包就是“关闭”起来的作用域.
在这个demo,inner函数中使用到了outer函数上下文中的b,就是闭包。
具体来说:inner函数除了访问自己的作用域和全局作用域,还访问了outer的作用域,inner函数执行,访问outer作用域中的b变量时,outer函数已经执行完毕了,但是outer函数把其内部的变量b关闭起来让它信任的函数inner可以访问,这就是闭包。
这其实是因为inner和b都是outer的内容,inner是outer返回的,这样在函数体内返回的结构,可以在函数结束后访问其中的变量。就是闭包。
这也是js实现私有变量的常见方式。
关于变量提升
js中代码运行分为两个阶段,创建阶段和执行阶段,创建阶段准备内存,执行阶段实际运行。
这段代码如果在非严格模式下运行,a会输出undefined,到输出b这里就会报错。但如果能正常执行到test()这一步,也能正常输出。
但如果在严格模式下,在输出a这一步就报错了。
这就是js中的变量提升的原因,即代码被提升到了开始部分。
关于原型链
原型链是js用于处理继承关系的数据结构。最初为了解决“复用”,降低内存占用。
原型链和继承关系是一致的。
ES6中的class是原型结构的语法糖。
关于异步
如果在代码中同步地写一个非常耗时的任务(如一亿次循环),肯定会阻塞页面渲染,即造成页面卡顿。
所以我们采用异步的方式,先执行后面的任务,稍后再执行这个耗时的任务。
但是如果我们在一个点击事件的回调函数中用定时器来执行一个很大的循环,那么点击之后会发生什么呢?卡顿,也会卡顿。
因为异步只是提供了延后处理的能力,不是不处理,也不是交给其他程序的处理。
异步是工作时间的精准分配,但不能提升整体工作能力。比如异步可以让厨师更好地利用一个灶台,并不会给他两个灶台。
异步不等于并发,毕竟js是单线程的。
关于事件循环
事件循环是一种任务调度机制,提供“按优先级”的事件执行策略,最重要的事情先执行,必须耗时等待的任务延后执行,表现为“异步流程”,减少空等的时间,更加高效。
当任务量较少的时候,异步可能并不会有太大帮助,因为只是延后做,不是不做。
当任务量很大时,异步一样会性能不好,因为异步并没有提升能力。
关于Number类型的存储
Number类型的存储方式是IEEE754标准。
(待完善)
关于使用连等赋值
exp1=exp2=exp3=exp4. 等价于. exp1=(exp2=(exp3=exp4))
执行该语句时,按照“先从左到右解析各个引用,然后计算最右侧的表达式的值,最后把值从右向左赋值给各个引用”的原则执行。
先从左到右:
解析exp1,得到ref1,
解析exp2,得到ref2,
解析exp3,得到ref3,
计算exp4表达式,得到value。
再从右到左:
value赋值给ref3,
value赋值给ref2,
value赋值给ref1。
这个demo中,前两个语句执行完,foo和bar都指向对象{n:1}
foo.x=foo={n:2}执行时:
先从左到右:
foo.x解析,找到{n:1}对象,并在其身上添加一个属性x,得到{n:1,x:undefined},等待为x赋值
foo解析,得到的是一个引用,{n:1}对象的引用
{n:2}通过对象字面量的方式创建一个新的对象。
从右向左:
将新对象赋值给foo这个引用,即foo指向了新对象{n:2}。
将新对象赋值给上面的x,得到{n:1,x:{n:2}},bar始终指向这个对象。
关于模板字符串
`${xxx}`,取变量xxx的值并转为字符串,对于对象和数组等会调用对应的toString方法。
所以若xxx是一个对象,这样得到的结果就是'[Object Object]'。