查缺补漏 - JS基础知识补充。 -《重学前端》
类型
七种数据类型:String、Number、Null、Undefined、Boolean、Object、Symbol。
-
Number
浮点数的运算精度无法用===
来比较。
使用Number
提供的最小精度来比较Number.EPSILON
。// 示例 0.1 + 0.4 === 0.5 // true 0.1 + 0.7 === 0.8 // false 0.1 + 0.7 = 0.799999999999 // 使用最小精度比较 Math.abs(0.1 + 0.7 - 0.8) <= Number.EPSILON // true
-
类型转换,数据类型的隐式转换,推荐显示转换后
===
进行比较字符串转数值
parseInt
- 参数为要解析的值、要转换的进位数(2-36),无法转换返回NaN。
parseFloat
- 参数为需要解析转换的值,不能转换则返回NaN。
Number
- 构造函数处理转换。// parseInt parseFloat parseInt("10"); // 10 parseInt("10",2); // 2 // Number("10"); // 10
装箱转换
概念:将基本类型转换为对应的对象。Object.prototype.toString
可以识别对象对应的基本类型。针对于经过装箱的需要配合
typeof
区分基本类型还是对象类型。// typeof let fn = new Function(); typeof fn; // "function" fn.toString(); // "function anonymous(){}" // fn = Object.prototype.toString.call(fn); typeof fn; // "string" fn.toString(); // "[object Function]"
拆箱转换
概念:现将对象变成基本类型,再从基本类型转换为对应的其他类型。在标准中存在
toPrimitive
方法进行拆箱转换。拆箱转换会调用
valueOf
和toString
来获得基本数据类型。string
会先调用toString
方法,都不存在时会抛出错误TypeError
// 1. 定义类型转换 转换为 String let obj = { valueOf:()=>{console.log("valueof"); return Function}, toString:()=>{console.log("toString"); return Function} } String(obj); // toString // valueof // TypeError // 2. 转换 Number Number(obj); // valueof // toString // typeError // 3. 找到对应的类型后结束调用 let obj = { valueOf:()=>{console.log("valueof"); return Function}, toString:()=>{console.log("toString"); return "private"} } String(obj); // toString // "private" // 4. 自定义行为 ES6 obj[Symbol.toPrimitive] = ()=>{console.log("primitive"); return "toPrimitive"} String(obj); // primitive // "toPrimitive"
对象
JS对象:核心是理解原型链prototype
.
使用new
运算符:
- 以构造器的
prototype
属性为原型,创建新对象; - 将
this
和传递的参数传给构造器; - 如果构造器返回的是对象,则返回;否则返回创建的新对象。
function User(){
this.name = "admin";
this.getAge = function(){
return 25;
}
}
User.prototype = {
getName:function(){
return this.name;
}
}
// 实例化调用
let user = new User;
user.getName(); // "admin"
new User()
和new User
返回的是一样的。
ES6的Class
语法,之前一些过文章ES6 - 类(class)、修饰器:
JS 对象分为宿主对象(即有运行时环境决定)、内置对象(包含固有对象、原生对象、普通对象);
JS 执行
由宿主环境发起的任务为宏观任务;由JS 引擎发起的任务为微观任务。一个宏观任务包含多个微观任务,待微观任务执行完,这整个宏观任务执行结束。
Promise与setTimeout
Promise
内部的执行代码比setTiemout
先执行。
setTimeout
属于宿主对象,属于宏观任务;Promise
是js标准内置对象,属于微观任务。
代码执行顺序:
代码分析:
// 1. 加入一个宏观任务,待执行
setTimeout(()=>console.log("d"), 0)
// 2. promise 执行,内部代码会同步执行 输出 "a"
var r = new Promise(function(resolve, reject){
console.log("a");
resolve()
});
// 3. then 调用 执行输出 c1
r.then(() => {
console.log("c1") ;
// 3-1 promise 微任务执行,直接调用输出 c2
new Promise(function(resolve, reject){
resolve()
}).then(() => console.log("c2"))
});
// 微任务执行完成之后,执行宏任务, 输出 d
结果输出:
JS 执行
void
运算会忽略表达式的值,返回的undefined。
JS 立即执行函数:
(function(){
console.log("a");
})()
// 使用void
void function(){
console.log("a");
}()
语法
JS 执行有两个阶段:预处理(处理声明的变量)、执行阶段
最为难理解的就是var
声明的变量的取值。
示例分析:
- 预处理阶段 - 全局作用域内声明
a
;声明了foo
函数,且在内声明覆盖了a
; - 执行阶段 - 全局作用域赋值
a=1
打印输出1
- 执行
foo()
- 预处理阶段已重新声明了a
但未赋值。打印输出undefined
- 执行赋值
a=2
- 打印输出2
. - 最后输出全局变量
a
打印1
var a = 1;
function foo() {
console.log(a);
var a = 2;
console.log(a);
}
console.log(a);
foo();
console.log(a);
function
在普通声明方法时,在预处理阶段会赋值;非正常声明时,只有声明;
示例分析:
- 函数的正常声明,打印结果为
fn
; - 带有条件语句的声明是,打印结果为
undefined
;
// 1. 输出 fn
console.log(fn);
function fn(){
//...
}
// 2. 在其他语句中执行声明
console.log(fn);
if(true){
function fn(){
// ...
}
}
break、continue语句
break
用于跳出循环语句或switch语句;continue
用于结束本次循环继续下次循环。
这里需要示例的时带标签的语法
四个示例:
break
跳出本次循环。上层循环继续执行;
break tag_1
跳出指定的循环;
continue
跳过本次循环,继续下次循环;continue tag_1
跳出循环到指定的循环体中,继续下次循环。
// 1. 跳出指定循环体
tag_1:for(let i=0;i<4;i++){
tag_2:for(let j=0;j<4;j++){
let num = i*j;
if(num === 4){
break tag_1;
}
console.log(i,j);
}
}
// 2. 跳出本次循环
tag_1:for(let i=0;i<4;i++){
tag_2:for(let j=0;j<4;j++){
let num = i*j;
if(num === 4){
break;
}
console.log(i,j);
}
}
表达式
说明:
**
乘方表达式;- 移位表达式:
>>
左移、<<
右移、>>>
无符号右移。- 左移n位相当于乘以2的n次方。
- 右移n位相当于除以2,取整n次。
- 无符号右移会把减号视为符号位,参与移位。
- 位运算:
&
与运算、|
或运算、^
异或运算符。- 异或运算表达:相同时为 0 ,不同时为1
// 1. 乘方表达式
2**10 // 1024
(-2) ** 10 // 1024
// 2. 移位表达式
2>>3 // 10 右移三位 为 0
2<<3 // 10 左移三位 10000 为 16
// 3. 位运算
3&5 // 011 & 101 结果 001 1
3|5 // 结果 111 7
3^5 // 结果 110 6