整本书看下来,稍微有些晦涩,很多理论概念被换了一种说法阐释,开阔思路,故整理笔记。
笔记按照篇章整理,没有固定逻辑。
第二章
1.语法关键字对语义逻辑的绑定结果,是对作用域的限定;
变量对位置性质的绑定结果,则是对变量生命周期的限定。
2.函数的6种声明标识符的方法(var,const,let,fucntion,class,import),他们声明的标识符在语法分析阶段就可以被识别。
3.关于值传递与引用传递
1 | var str = 'abcded'; |
func函数传入字符串时,是值传递,根据结果可知,不能直接修改其属性方法
传入obj对象时,是引用传递,传入后被修改了属性值,故打印出了hello world.
所以,到底是值传递还是引用,会根据传递的数据类型决定。
4.等值检测(==)运算规则
值类型与引用类型比较,将引用转换成值类型数据相同的数据,进行数据等值比较
两个值类型比较,将二者转换成相同数据类型,在进行数据等值比较
两个引用类型比较,仅比较引用地址
跟数字,布尔,字符串比较时
有任何一个是数字时,将另外一个转数字
有任何一个是布尔值时,转换成数字后比较
有任何一个是对象(或函数),调用valueOf方法来将其转换成值数据进行比较
按特定规则返回比较结果,如,undefined和null会返回true
等值检测一些特例1
2
3
4
5
6
7
8
9
101. NaN不等于自身
NaN == (===/ != /!==) NaN // true
2. 符号可以转为true,但不等值于true
Boolean(Symbol()) ,!Symbol(), Symbol() ==(===) true // true false false
3. 即使字面量相同的引用类型,也是不严格相等的
{} === {}, /./ === /./, function() {} === function() {} // false false false
5.序列检测规则
两个值类型比较
直接比较数据在序列中的大小
值类型与引用类型进行比较
将引用类型转换成与值类型数据相同的的数据,再进行“序列大小”比较
两个引用类型进行比较
无意义,总是返回false
6.赋值
字符串赋值原理是复制地址,所以对于字符串的操作有了以下三种限制
a.不能直接修改字符串中的字符
b.字符串连接运算必然导致写复制,这将产生新的字符串
c.不能改变字符串的长度
7.函数隐式调用的几种情况
a.es6之后的模板处理函数
b.将函数用作属性读取器时,属性存取操作将隐世调用该函数,==>getter,setter
c.使用bind方法将原函数绑定为目标函数时,调用目标函数,就是隐世调用原函数
d.当使用Proxy() 创建原函数的代理对象时,调用代理对象也是隐式调用原函数
e.new运算符运算
f.当函数用作对象的符号属性,触发相应的行为时,就会隐式调用原函数
8.模块导入
1 | import * as mynames from 'module'; // mynames.x是对象属性读取 |
new 调用普通函数
如果该普通函数返回对象,则返回该对象
如果该函数返回值类型数据,则返回值忽略,返回this 引用,也就是普通函数本身arr[1,2,3] 会返回arr[3],中括号里面的1,2,3会形成逗号运算
delete
不能删除
用var/let/const声明的变量与常量
直接继承自原型的成员
本质上用于删除对象自有属性表属性关于对象
所谓原型,就是构造器用于生成实例的模板
空白对象: 它的原型链上的所有自有属性表都为空
原型链:对象所有父类和祖先类的原型所形成的,可上溯访问的链表
函数和构造器并没有明显的界限,唯一区别只在于原型prototype是不是一个有意义的值
类是静态的声明,意味着类继承的构建过程也是静态的,是在语法分析期就决定了的
当在函数f中使用super.xxx时,无论该函数被用来作为那个实际对象的方法,
super都绑定在Object.getPrototypeOf(obj)上,f为obj的属性方法
在new 类的实例时,super()执行的目的就是回溯整个原型链,确保基类最先创建实例
没有在类中声明constructor()方法时,会默认添加并调用super()
1 | function foo() { |
继承方式的选择上,
大型系统必须采用类继承的思路,继承关系的确定性和支持静态语法检测等特性
可以帮助开发者最终简化构建大型系统的开发和业务逻辑实现,并提供足够的系统稳定性
小型系统或者体系的局部使用原型继承的思路,既可以有优美的实现和高校的性质,
也可以更深入理解js中混合不同语言特性的精髓
1 | function MyFunction() {} |
上面例子符合对象继承语义,但不能继承它的‘可执行’效果
内置对象的特殊效果不被对象系统继承
一方面这些效果被引擎绑定在特殊的构造器上,而不是他们的原型上
另一方面,系统只负责维护内部原型链,以确保instanceof运算能正确检测这种关系
而不负责这些特定效果的实现和传递
如果一个属性使用的是存取描述符,那么无论读写性质是什么,都不会新建属性描述符
子类中如果是继承来的这样的属性,那么在子类中对该属性的读写也会忠实地调用继承来的读写器
第四章
- 信息是对状态集合的解释,该集合的解释成本即是编程所应付的复杂性
编程的目的是使一个系统对外呈现可解释信息
1
2
3
4
5
6
7
8
9
10
11var x = 'outer', y = 'outer';
function foo() {
console.log(1, [x,y]) // [undefined, undefined]
if(true) {
function x() {}
} else {
function y() {}
}
console.log(2, [x,y]) // [f, undefined]
}
foo()
第五章
- 关于函数参数
默认参数都是有名字的形式参数,但是从第一个参数开始,后续所有参数不会载计入形参个数
剩余参数不计入形参个数
模板参数参与计数,但是无论一个模板参数被解构为多少标识符,都按一个计算1
2
3
4
5
6
7
8const ff = (a,b=1,c=2) => {console.log(34)}
ff.length ==>1
const ff2 = (a=3,b,c) => {console.log(34)}
ff2.length ==>0
const ff3 = (a, [b,c]) => {console.log(34)}
ff3.length ==>2
1 | function foo(filename) { |
arguments 获取的参数,不被赋予初始默认值1
2
3
4
5function foo(a=1,b,c=2,d) {
console.log(...arguments); // undefined,100,200,300
console.log(a,b,c,d); // 1,100,200,300
}
foo(undefined,100,200,300);
惰性求值1
2
3
4
5
6function foo(msg) {
console.log(msg)
}
var i = 100;
foo(i+=20, i*=2, 'value:'+i); // 120
foo(i); // 240
1 | var f = function func2() { |
16.
类可以赋予对象成员,但是不能进行函数调用,可以用new 来调用生成实例
17.
绑定函数特殊性质
- 内部原型被置为与targetFunc的原型一致
- 没有自有的prototype属性
1 | class MyFunc { |
18.
函数与对象的区别在于,前者内部结构中初始化了[[call]] 和 [[contruct]] 这两个内部方法
绑定函数通过这两个内部方法实现绑定逻辑
重写这两个方法并使其分别指向一段特有的调用或构建逻辑(以处理暂存在内部槽中的thisArg和arg1…n参数)
代理类对象Proxy则侧重重写了对象的全部13个内部方法
借助代理类也可以实现与绑定函数完全相同的功能
需要在handlers上添加自己的陷阱,以处理[[call]] 和 [[construc]]行为
19.
用属性来替代方法,并在递归中维护this引用1
2
3
4
5
6
7
8
9var obj = {
get fact() {
const fact = x=> x && x*fact(x-1) || this.power || 1;
return fact
}
}
obj.power = 100;
obj.fact(9) // 36288000
20.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29var obj = {
[Symbol.iterator] : function*() {
var obj = {
[Symbol.iterator] : function*() {
for(var i =0;i< 10; i++) yield i;
}
}
console.log(...obj) // 0 1 2 3 4 5 6 7 8 9
}
}
console.log(...obj) // 0 1 2 3 4 5 6 7 8 9
var obj = {
start: 3,
[Symbol.iterator] : function*(start = 5, end = 10) {
var {start, end} = {start, end, ...this};
for(var i=start;i<end; i++) yield i;
}
}
console.log(...obj) // 3 4 5 6 7 8 9
obj.end = 6;
console.log(...obj) // 3 4 5
delete obj.end
delete obj.start
console.log(...obj) // 5 6 7 8 9
21.
1 | var msg = (function myFunc(num) { |
第六章
- with语句传入的值如果是基础类型数据,会转换成相应类型对象再构建闭包
- null 作为对象总是转换为确定的三种值类型0,’null’和false
- 对于值类型来说,包装类上的toString()和valueOf方法其实只会对显示方法调用有效
而并不影响原始值的运算 - 类型转换分为两个阶段,其一是转换为原始值,其二是转换为尝试运算的值类型
这两个阶段,一个受上述”隐式转换逻辑”控制,另一个受具体的运算操作控制
会尽可能的转换成预期的类型1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16x = {
toString: () => '10',
valueOf: () => -1
}
parseInt(x) ===> 10 因为规定参数是字符串类型
Math.abs(x) ===> 1
1 + x ===> 0
"1" + x ===> 1
delete x.valueOf
1+x ===> '1-1'
1 | var x = new Boolean(false) |
- 一旦对象中声明过Symbol.toPrimitive属性,那么valueOf()与toString() 在值运算的隐式转换中就无效了
switch() 语句对表达式求值,并用该值与case分支中的值进行比较运算时,会采用===操作符进行运算,优先进行类型检测而不会发生类型转换过程
symbol只能转换成bool类型的true,尝试转换成其他值都会报错
补前缀并转大写
1
2
3var x = 1234567
x.toString(16).padStart(8, '0').toUpperCase() // 0012D687数组当对象去结构时,只会保留有值的key
1 | var arr = [1,2,'345',,12]; |
- 计算一个字符串中不同字符个数
1 | console.log(new Set('abcadf134oaafshjafgoi').size) ==> 14 |
是否可重写的限制主要是两个,可引用,可写
1
[100][0]++ // 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 检测成员是否是重写的
var isRewrited = function(obj,key) {
return obj.hasOwnProperty(key) && (key in Object.getPrototypeOf(obj));
}
// 检测成员是否是继承来的
var isInherited = function(obj,key) {
return (key in obj) && !obj.hasOwnProperty(key);
}
// 在对象及其原型链上查找属性的属主对象
var getPropertyOwner = function f(obj,key) {
return !obj ? null \
: obj.hasOwnProperty(key) ? obj
: f(Object.getPrototypeOf(obj),key)
}给Object.prototype添加成员,与添加全局变量名“效果相当”
1 | Object.prototype.x = 100; |
- try-catch-finally中的代码会在try最后return时,先被挂起,执行完finally再return
如果return的值是个值类型则对返回值没有影响
但如果是对象,return只保留引用,在finally中对return值进行了修改,那么将会影响返回值