关于值的比较
字符串的比较
字符串是根据位数一位一位比较的,从第一位按位比较,直到必出true 或者 false。
不同类型的比较
对不同类型的值进行比较的时候,会将其先转化为数字再进行比较。
true会转化为1,false会转化为0。
null == undefined 这是js的一个规则。null 与 undefined 不会在进行值比较==时进行任何格式上的转化,所以会出现以下情况:
alert(null > 0) // false
alert(null == 0) // false
alert(null >= 0) // true
一些练习
5 > 4 // true
"apple" > "pineapple" // false
"2" > "12" // false
undefined == null // true
undefined === null // false
null == "\n0\n" // false
null === +"\n0\n" // false
关于逻辑运算符和返回值
或运算的返回值
处理每一个操作的时候,都将其返回一个布尔值,然后进行或运算,找到第一个true的时候则停止运算,返回第一个真值。如果没有真值,就返回最后一个值。
作用:
- 获取变量和表达式的第一个真值
- 短路求值。当
||左侧是false的时候执行右侧命令。
与运算和返回值
与运算返回第一个false的值,如果没有则返回最后一个值。
同理,也可以用来短路求值,当&&左边是真时直行右侧命令。
与运算比或运算优先级高
非运算
1.两个非运算有时可以用来把表达式转成Boolean属性。
空值合并运算符??
第一个参数不是null或者undefined则返回第一个,否则返回第二个。
换句话说就找到不是null或undefined的第一个数。
关于函数
函数可以通过表达式或者声明来实现,同时也可以通过 = 赋值过去。赋值的时候不能带括号,如果带了括号则是将返回值赋值
回调函数
在一个函数里,函数的传参是函数,这种情况就是回调函数。
箭头函数
let func = (arg1,arg2) => expression //剪头函数版本
let func = function(arg1,arg2){
return expression;
} // 普通函数版本
上述两个函数本质其实相同。
做一些练习:
function ask(question, yes, no) {
if (confirm(question)) yes();
else no();
}
let ask = (question,yes,no) => {
if(confirm(question)) yes()
else no()
}
};
ask(
"Do you agree?",
() => alert("You agreed"),
()=> alert("You canceled the excution."));
ask(
"Do you agree?",
function() { alert("You agreed."); },
function() { alert("You canceled the execution."); }
);
对象
读取对象中的属性,不仅可以通过.符号进行读取,也可以通过[]进行读取
尤其是属性需要通过变量获取时。
使用for...in遍历对象
光看in来说,in本身是一个判断符号,判断属性是否在目标对象里。
遍历变量的顺序是根据属性数字的大小排列,如果没有数字大小可以比较则比较创建时间
Array,Date,Error是不同类型的对象。
对象引用和复制
对象存储的东西不是本身,是对象的地址(类似索引)。
直接使用=
复制,直接将对象存储的地址复制给目标对象的值。
看到一个非常合适的例子,复制生成的新对象是另一把可以打开对象这个柜子的钥匙,原先的对象也是一把钥匙,等于的过程就是配钥匙的过程。所以打开柜子后放入东西或者拿出东西,另一把钥匙打开这个柜子也会看到更改后的内容。
同时,比较两个对象也是通过比较他们的地址,下面有个很有意思的例子。
let obj1 = {}
let obj2 = {}
let obj3 = obj1
alert(obj1 == obj2) // false
alert(obj1 == obj3) // true
alert(obj1 === obj3) // true
浅拷贝
使用Object.assign
Object.assign(dest, [src1, src2, src3...])
dest是目标对象,src是源对象,目标是把所有的源对象拷贝到dest里
浅拷贝拷贝的一层属性可以修改,但是属性里的对象在一层靠背的时候一样是将地址拷贝过去。
换句话说如果对象里套对象,修改浅拷贝过后对象里的对象时,原对象的对象属性也会发生改变。
深克隆
用的最多的就是JSON.parse(JSON.Stringfy(obj))
手写一个深克隆
function deepClone (obj){
let dcRes = {}
for(let key in obj){
if( typeof obj[key] == 'object'){
deepClone(obj[key])
} else {
dcRes[key] = obj[key]
}
}
return dcRes
}
垃圾回收
垃圾回收的方式:删除不可达的对象,释放内存。
垃圾分为独立垃圾 --> 无任何对象引用。
报团垃圾 --> 几个垃圾互相引用彼此,但是不可达。
垃圾回收的方式
筛选出主流程中不可达的元素。如果对象中包含对外引用,但是不可达,依旧视为垃圾。
垃圾回收的基本算法
根的概念
- 当前执行的函数里的局部变量和参数。
- 当前嵌套调用链上的其他函数,局部变量和参数。
- 全局变量。
mark-and-sweep
垃圾回收标记所有找得到的根,然后标记他们。
遍历所有根并标记根的引用。
再继续遍历根的引用的引用....
一直到所有可达的变量被标记为止。
没有标记到的变量将会被删除。
关于垃圾回收的优化建议
1.将标记分为旧标记和新标记,如果是旧标记则降低检查的频率,如果说新标记则增加检查的频率。
2.将全部对象拆分为一个一个小的部分,分开进行标记和垃圾回收。
3.在CPU空闲的时候在进行垃圾回收,CPU任务重的时候不进行垃圾回收。
对象里的this
可以通过对象里的this访问这个对象的其他属性。
如果没有上下文的对象可以引用的话,this的值为undefined
存储在对象里的函数是方法
箭头函数没有this指向
想要实现链式调用,可以在方法里返回this
构造函数和new
构造函数外观上看是常规函数,不过有两个约定
- 命名只能通过大写字母开头。
- 只能通过new操作符来执行。
当一个构造函数被使用new操作符进行唤醒时,他会按照以下步骤执行:
- 新建一个空对象,并将构造函数的指针指向这个对象。
- 函数体执行,修改指针指向的对象,修改他的属性。
- 返回this的值。
new.target
用来判断是否是通过new来使用的,如果是通过new返回true,不是通过new 返回false
return
构造函数的return通常不写,但是也有自己的规则
如果返回的是一个对象,则返回对象;如果返回的是原始类型,则返回原始类型。
可选链?.
对于一个对象,他的属性不一定拥有,需要使用?.来访问,如果没有则undefined
不过他是ES2020新增的特性
可选链的作用是对于一个对象嵌套对象的现象,保证第二级的对象里的属性可以访问。
短路效应
如果?.的左边不存在,则会立即停止运算
?.()与?.[]
用来调用一个可能不存在的函数 或者访问一个不存在的属性。