JavaScript语言精粹 笔记
一、简单数据类型
一共5个:Number、String、Boolean、null 和 undefind
Number、String、Boolean 拥有方法
简单/基本数据类型 均为 不可变类型(immutable)
不可修改 指 其内存中的值是不可变的,对这个变量重新赋值实际上是在内存中新建了一个值,将这个变量名指向了新建的值的地址
比如:
var s = "abcdefg";
var s2 = s;
s = "hijklmn";
console.log(s2);
//此时s2仍为 "abcdefg"`
二、对象
除去简单数据类型,其他数据类型均为对象。对象均为可修改类型,在原地址上进行修改。
比如:
var arr = [1, 2, 3, s];
var arr2 = arr;
arr[0] = 5;
console.log(arr2);
//arr2为[5, 2, 3, "abcdefg"]
所以,对象是通过引用来传递的,不会被复制,若a=b=c={} 这样为引用同一个,若改变则三个一起变。
对象可以看作是可变的键值对、字典。
数组、函数、正则表达式 都是对象。对象是无类型的 class-free
key值带连接符“ - ”是不合法的,需要用双引号括起来,(一般的key值不用引号),而下划线“ _ ”是合法的。
三、原型 prototype
原型链只有在获取某属性值时才会用到。
一直往上追溯,直到终点Object.prototype的过程,叫委托。
原型关系是动态关系:添加一个属性到原型中,会立即对所有基于该原型创建的对象生效。
person1.proto = Person.prototype
proto_是访问器,相当于索引,去找构造函数里的prototype
prototype是构造器,包含了构造函数里的各种方法。
所以,实例中是没有prototype属性的。
person2.constructor = Person
Person.prototype.constructor = Person
任意一个函数对象的_proto 是 f( ) {[ native code ]}
四、函数 Function
动态函数需要new一个实例,而静态函数可以直接调用。这里跟类class的逻辑类似。
比如:
function fnxx(){
this.xx = function(){} //这个是动态方法
}
fnxx.abc = function(){} //这个是静态方法
调用:
fnxx.abc()
var _f = new fnxx();
_f.xx();
另:构造函数 如没有返回值的,会返回this。有return值时,仅返回return的内容。所以构造函数尽量不写return。
五、调用
javascript一共有四种调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式
调用运算符是跟在任何表达式后面的()
apply调用模式可以允许我们选择this值,第一个参数为新传入的obj,第二个参数为传递的参数数组。apply会把参数list自动打开,变成多个参数。注:传入和接收是不同的。
例子:
var arr = [3,4];
function add(...arr){ //这里用(a,b) 两个参数也可以
return arr[0]+arr[1];
}
var sum = add.apply(null,arr);
调用 默认传参 arguments伪数组
六、异常 Exceptions
throw {name:" ", message:" "}
throw中自定义参数,可以加别的属性,所有属性都会传递到catch中。
throw会中断函数执行,并抛出exception对象,进入catch(e){ e.name e.message}
,e就为throw中写的参数。
七、扩充类型方法 Augmenting Types
自定义新方法,扩充原型中的方法,方便调用。
//具体实现,给Function.prototype扩充
Function.prototype.method = function(name,func){
this.prototype[name] = func;
return this;
}
//定义
Number.method('integer',function(){
return Math[ this<0 ? 'ceil' : 'floor'](this);
})
//使用
(-10/3).integer();
此处增加一个解释项: Fuction中为何包含Number
Number 是一个构造函数,因此它被链接到Function.prototype
原文:https://www.coder.work/article/1057512
此外,还可以直接进行定义,比较易读。
Number.prototype.integer = function(){
return Math[ this<0 ? 'ceil' : 'floor'](this);
}
小tips:
console.dir()
显示该对象所有的属性和方法
八、递归 Recursion
先举一个简单例子:
function fn(n){
if(n>0){
console.log(n); //3,2,1
fn(n-1);
console.log(n); //1,2,3
}
}
调用 fn(3)
解释:js是单线程,进入fn会打断现有执行,进入新线程,fn完成后,会回到这个线程执行下一行输出,所以是从最里层开始输出。
递归跟for循环最大的区别是:递归是先递到底,在归回来的时候,一块返回结果。
尾递归:函数最后return自身,运行时逻辑与‘循环’一致。按原理来说可以提升递归运行效率,因为不会在调用域里 创建新的作用域,从始至终只有一个作用域。但javascript并没有做尾递归优化,所以还是会与普通递归一致。此外,因为退出条件设置不当,很可能会变成深度递归,因堆栈溢出而运行失败。
九、自启动函数
正常函数会在定义之后 调用的时候运行,但也有些函数,需要在页面初始化的时候自动运行,也就是这里说到的 『.自启动函数』。
在讨论自启动函数之前,先来看一下两种函数定义方式。
- 第一种,关键字+函数名,这种可以变量提升,即在定义之前,就可以调用。
fnName();
function fnName(){
...
}
- 第二种,变量赋值,这种不能在定义之前使用,因为还未赋值,会报错。
fnName();
var fnName=function(){
...
}
这种定义方式,可以直接在后面接()调用,实现自启动。
var fnName=function(){
...
}();
待填坑:
汉诺塔