js数据类型
基本数据类型:Number String Boolean Null Undefined Symbol 以及es2020添加的 BigInt 引用类型:Array Object Function Math Date等
js类型判断
- typeof 返回值为undefined string boolean number symbol bigint function object 。
typeof NaN // 'number'
- instanceof 可检测出引用类型,以及多层继承关系。
- constructor 不安全,因为constructor的指向可以被改变。null和undefined没有此方法。
- Object.prototype.toString.call() 返回值为 '[object 首字母大写的数据类型]'。 ps: 以上可以看出 typeof 只能准确判断出基本类型,但是typeof null会返回object,而Object.prototype.toString.call()可以准确的判断出任何数据类型。 PS: 所有对象的键,都会被存储为字符串,即使你没有给定字符串类型的键。 对象的 . 不能用来调用以数字开头的键。 对象的比较是各自引用的比较。
js类型转换
Object.prototype.valueof()方法返回指定对象的原始值,如果对象没有原始值,则valueOf会返回对象本身。该方法在类型转换时会自动调用。
对象 | 返回值 |
---|---|
Array | 返回数组对象本身 |
Boolean | 布尔值 |
Date | 时间戳 |
Function | 函数本身 |
Number | 数字值 |
Object | 默认返回对象本身 |
String | 字符串值 |
Object.prototype.toString()方法返回一个表示对象的字符串。该方法也会在类型转换时自动调用。
转换规则
一般非基础类型进行转换时会先调用valueOf,如果valueOf无法返回基本类型值,就会调用toString
字符串和数字
- +操作符,如果有一个为字符串,那么都转为字符串进行拼接
- -操作符,转为数字进行计算
[] + {} // "[object Object]"
{} + [] // 0
布尔和数字
1 + true // 2
1 + false // 1
所有的逻辑判断中都会转为布尔值
this相关
- this是当前执行代码的环境对象,在非严格模式下总是指向一个对象,严格模式下可以是任意值。
- 在全局执行环境中this指向全局对象。
- 在函数内部,this的值取决于函数被调用的方式。
- this不能在函数执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。 改变this指向的操作:
- call 立即调用,第二个参数为单个的
- apply 立即调用,第二个参数是数组
- bind 不会立即调用,返回一个新的函数,并且永久绑定this。(也就意味着一经bind绑定的this不可再次修改this指向)
- 箭头函数(箭头函数没有自己的this,会从作用域的上一层继承this)
- new 当一个函数被用作构造函数时,他的this被绑定到了正在构造的新对象上。 PS: new的绑定优先级要高于bind
原型
每个实例对象都有一个私有的__proto__属性,指向它的构造函数的原型对象(prototype)。
原型链
当对象查找一个属性的时候,如果自身没有找到,那么就会查找自身的原型对象,如果原型还没有找到,那么就会继续查找原型对象的原型,直到找到Object.prototype的原型时,此时原型为null,停止查找。这种通过原型链接的逐级向上的查找链被称为原型链。
原型链的继承
在new一个新对象时,会将新创建的对象的__proto__指向其构造函数的原型对象,这个机制就是原型链的继承。方法定义在原型上,属性定义在构造函数上。
一个对象可以使用另外一个对象的属性或者方法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样根据原型链的规则,如果查找一个对象的属性在自身不存在时,就会查找另外一个对象,相当于一个对象可以使用另外一个对象的属性和方法了。
闭包,作用域
闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包产生的本质是当前环境中存在父级作用域的引用。
js代码的整个执行过程分为两个阶段:编译阶段和执行阶段。
编译阶段由编译器完成,将代码翻译成可执行的代码,在这个阶段作用域规则就会确定。
执行阶段由引擎完成,主要是执行可执行代码,而执行上下文在这个阶段创建。
我们将作用域定义为一套规则,用来管理引擎如何在当前作用域以及嵌套子作用域中根据标识符名称进行变量查找。
当我们访问一个变量时,编译器在执行这段代码时,会先从当前的作用域中查找是否有这个标识符,如果没有找到,就会去父作用域中查找,如果父作用域中没有找到就会继续向上查找,直到全局作用域为止。而在这个查找过程中形成的一个链条,就称之为作用域链。
js循环机制
js是单线程的。 js中的任务分为 同步任务和异步任务
- 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
- 异步任务:不进入主线程,而进入 任务队列 的任务。只有 任务队列 通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
- 主线程的所有同步任务全部执行完毕之后,才会执行任务队列中的异步任务。
- 任务队列又分为:宏任务队列和微任务队列。
- 宏任务包括:setTimeout setInterval setImmediate(node独有) requestAnimationFrame(浏览器独有) UIrendering(浏览器独有) I/O
- 微任务包括:process.nextTick, MutationObserver, Promise.then catch finally PS: new Promise这个操作是同步的。 上菜:
function fn(){
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
},0);
new Promise((resolve, reject) => {
console.log(4);
resolve(5);
}).then(data => {
console.log(data);
});
setTimeout(() => {
console.log(6);
},0);
console.log(7);
}
fn();
/** 附上 详细的代码执行过程:
1. 首先自上而下执行代码,得到此时的
stack: [console.log(1), new Promise(), console.log(7)];
macrotask: [setTimeout1, setTimeout2]
microtask: [promise2.then]
此时的输出为:1, 4, 7
2. 到此全部的同步任务执行完毕,调用栈清空;
stack: [];
macrotask: [setTimeout1, setTimeout2]
microtask: [promise2.then]
3. 将微任务压入执行栈,执行
stack: [promise2.then];
macrotask: [setTimeout1, setTimeout2]
microtask: []
输出 5
4. 此时,微任务队列中为空,故此开始执行宏任务,将宏任务队列中的setTimeout1压入执行栈;
stack: [setTimeout1];
macrotask: [setTimeout2]
microtask: []
输出 2,
在执行setTimeout1时 又创建了一个微任务,放入microtask中
stack: []
macrotask: [setTimeout2]
microtask: [promise1.then]
5. 将新创建的微任务压入执行栈执行
stack: [promise1.then]
macrotask: [setTimeout2]
microtask: []
输出:3
6. 到此,微任务执行完毕,开始执行宏任务
stack: [setTimeout2]
macrotask: []
microtask: []
输出:3
7. 最后,执行栈和任务队列全部为空,代码执行完毕。
最终输出顺序为:1 4 7 5 2 3 6
完美!!!
**/
Promise
Promise是一个构造函数,用来生成Promise实例。简单说 Promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果)
- Promise对象的状态不受外界影响---只能从pedding 变为fullied活着rejected
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
- 无法取消Promise
- 如果不设置回调函数,Promise内部抛出的错误将不会反应到外部。
- 处于pedding状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)