1.基本数据类型
- 数字
- 字符串
- 布尔值
- undefined
- null
- symbol
基本数据类型是按值访问的,对象是按引用访问的,对象存储在堆中,栈中有引用指向这块堆内存
2.数据类型的转换
js中的表达式或者运算符,左右两边的东西,运行的时候会做类型转换,转换成它期望的类型
- 对象(数组)转数字:先调valueof,如果返回的不是原始数据类型,则调tostring(eg: [] == 0)
- 对象(数组)转字符串:先调tostring,如果返回的不是原始数据类型,则调valueof(eg: {} == 'xxx')
- undefined转数字是NaN
- null转数字是0
特殊:
- null == undefined -> false
- NaN == NaN -> false
- NaN(包括自己)、null(除了自己)、undefined(除了自己)跟任何东西都不相等
- 有(+)一边是字符串,就认为是字符串拼接
3.typeof和instanceOf的区别
// 原始数据类型的判断,函数跟对象有特殊:typeof 如果是函数就返回函数,是对象就返回对象。不能判断具体点的
console.log(typeof [])//object
console.log(typeof new RegExp('/A/'));//object
console.log(typeof function () { });//function
console.log(typeof null);// object特殊的
console.log(typeof 1);// number
console.log(typeof 'ss');// string
// 能判断具体的但是不能判断自定义类型的
console.log(Object.prototype.toString.call(new RegExp('/A/')))//[object RegExp]
class A { }
const a = new A();
console.log(Object.prototype.toString.call(a))//[object Object]
// instanceof 就是通过__proto__能找到的, 缺点不能判断原始数据类型
console.log([] instanceof Object); //true [].__proto__.__proto__ === Object.prototype 因为这个为true
console.log([] instanceof Array); //true [].__proto__ === Array.prototype 因为这个为true
function instanceofMy(A, B) {
B = B.prototype;
A = A.__proto__;
while (true) {
if (A === null) {
return false;
}
if (A === B) {
return true;
}
A = A.__proto__;
}
}
console.log('str' instanceof String); // false 缺点不能判断原始数据类型
// instance默认调用‘str’类上的 Symbol.hasInstance方法。所以我们可以复写String的这个方法,来自己实现
// constructor 也有相应的应用场景
4.call跟apply原理 apply只是第二个参数是数组别的没有差别
Function.prototype.call = function (context, ...args) {
// 万一context传的基本数据类型,就转一下Object(context),因为我们要给它加方法嘛
context = context ? Object(context) : window;
// 为了构造'ctx'.this(), contexts上有this的方法(此处this指的的fn1)
context.fn = this;
// es6 直接这样就行了
const r = context.fn(...args);
delete context.fn;
return r;
}
let a = { name: 9 };
function fn1(p1, p2) {
console.log(this, p1, p2);
}
fn1.call(a, 'p1', 'p2') // 正常执行
fn1.call.call.call(a, 'p1', 'p2') //报错
// ---> a['fn1.call.call']('p1', 'p2') -> 'p1'[a]('p2') -> a是个对象,你当方法执行,自然就报错了,说fn不是一个方法
const a1 = function () { console.log('a1执行了,this是:', this, arguments); }
const a2 = function () { console.log('a2执行了,this是:', this, arguments) }
a1.call(a2, 'p1', 'p2');
a1.call.call.call(a2, 'p1', 'p2');
// ---> a2['a1.call.call']('p1', 'p2') -> obj('p1').a2('p2')
a1.call.call.call(a2);
// ---> a2['a1.call.call']() -> 无参数就是window.a2()
5.await基础知识
async function demo() {
let rs = await new Promise((resolve, reject) => {
setTimeout(() => resolve('abc'), 3000)
})
console.log(rs)
}
demo().then((abc) => {
console.log(abc)
})
- demo执行完后,返回的是一个promise,如果async function demo里没写return,会自动给包装成return Promise.resolve()
- async function demo里写了return 普通对象{name:123},那么会自动包装成Promise.resolve({name:123})
- 当然如果本身返回的就是一个promise就不用包装了,直接then就能取到上个promise成功的时候往resolve里放的参数
- rs = await new Promise(xxx), rs拿到的是resolve里传的东西即:abc
6.函数有坑
- 主要区别函数的定义形式
声明式:
function fn1(){}
fn1在声明它的作用域内可见,且fn1能被修改,在变量提升的时候,声明跟定义都提升
表达式:
- (function a(){})()
- var fn = function b() {}
- +function c(){}
在变量提升的时候,只提升声明
函数名 a b c只在那个函数内可见,且是常量不能被重新赋值
题目:
var foo = 1;
(function foo(){
foo = 10;
console.log(foo)
})(); 答案:打印foo函数(非严格模式)严格模式直接报错了
var fn = function test2() {};
test2()
答案:打印test2没有定义
+function name(){
console.log(123);
}();
name();
答案:打印name没有定义