1. 基本数据类型有哪些,复杂数据类型有哪些?为什么基本数据类型存在栈中,复杂数据类型存在堆中?
基本数据类型:String、Number、Boolean、Undefined、Null、BigInt、Symbol:
大小固定,存放在栈里面,里面直接开辟一个空间,存放的是值
复杂数据类型(引用数据类型):通过new操作符创建的对象,如Object,Array,Date,Function,RegExp等:
存放在堆里面,首先在栈里面存放地址,地址在指向堆里面的实例,真正的对象实例存放在堆空间中
原因:
堆比栈要大,但是栈比堆的运行速度要快。
基本数据类型比较稳定且占据内存少,所以放在空间小但速度快的栈中
复杂数据类型放在堆中的目的是不影响栈的效率,而是通过引用的方式去堆中查找
typeof用于确认原始类型,instanceof用于确认引用类型。
2.为什么 0.1+0.2 !== 0.3
js能表示并进行精确算术运算的整数范围是[-2 53 - 1, 2 53 - 1]
要计算0.1+0.2的结果,首先要把这两个十进制数转换成二进制相加,再转换成十进制,然而当0.1转化为二进制时,会出现无限循环,但是尾数部分最多只能表示53位,在进行对阶,移码相加等操作后最终得出一个二进制数,转为十进制数得到的结果为0.30000000000000004,是一个典型的精度丢失案例。
可以通过ES6新提出的Number.EPSILON去处理上述问题
var a = 0.1, b = 0.2, c = 0.3;
var result = (Math.abs(a + b - c) < Number.EPSILON);
console.log(result) // true
3."=="和"==="的区别
"==="表示恒等,首先比较两边的变量数据类型是否相等,其次比较两边的变量的数值是否相等;
"=="表示相等即仅仅比较两边变量的数值是否相等。如果两个操作数不是同一类型,那么相等运算符会尝试一些类型转换。
undefined值是由null值派生而来的,因此ECMA将它们定义为表面上相等
4.NaN
有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。
console.log(0/0); // NaN
console.log(-0/+0); // NaN
console.log(5/0); // Infinity
console.log(5/-0); // -Infinity
Nan不等于任何值。包括它自己。
console.log(NaN == NaN); // false
isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。把一个值传给 isNaN()后,该函数会尝试把它转换为数值。任何不能转换为数值的值都会导致这个函数返回true。
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN("10")); // false,可以转换为数值 10
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值 1
6.函数中实现私有变量
可以通过在this上添加一个用于访问私有变量的方法来实现访问函数内变量
function MyObject() {
// 私有变量和私有函数
let privateVariable = 10;
function privateFunction() {
return false;
}
// 特权方法
this.publicMethod = function () {
console.log(privateVariable)
return privateFunction();
};
}
let obj = new MyObject();
console.log(obj.publicMethod())
7.伪数组转为数组的方式
const str = 'hello';
//1 效率高于2
console.log(Array.prototype.slice.call(str));
//2
console.log([].slice.call(str));
//3
console.log(Array.from(str));
//4
console.log(Array.of(...str));
//5
console.log(new Array(...str));
8.遍历对象属性的方法
const obj = {'a' : 1,'b' : 2,'c' : 3}
//1
for(const i in obj){
console.log(i , obj[i]);
}
//2 只返回自身可枚举的属性
console.log(Object.keys(obj));
//3 自身可枚举和不可枚举的属性都返回 对于数组额外返回一个length属性
console.log(Object.getOwnPropertyNames(obj));
9.合并数组的方法
let arr1 = [1,2,3];
let arr2 = [4,5,6];
//1
arr1.push.apply(arr1 , arr2);
//2
arr1.concat(arr2);
//3
let arr3 = [...arr1 , ...arr2];
10. Object.is和===的区别
别的行为与===基本一致,只有两处不同:
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
11.isNan和Number.isNaN的区别
两者最主要的区别是:Number.isNaN()不存在类型转换的行为
isNaN()通过判断传入的参数能否转换为Number类型来判断是否NaN,并非严格地判断。
Number.isNaN()则是通过'==='严格地判断
比如下面这个例子:
isNaN不能将'测试'转换成Number类型,转换失败就返回true。
Number.isNaN严格地判断'测试'是否===NaN,返回false。
console.log(isNaN('测试')) //true
console.log(Number.isNaN('测试')) //false
12.箭头函数
(1)没有自己的this,内部的this就是外层代码块的this
因此不能用作构造函数,因为new一个对象第三步就是把构造函数里的this指向这个对象,不能使用new.target关键字。
也不能通过call,apply,bind去改变this地指向。
(2)没有原型prototype,因此也就不能作为构造函数使用
因为new一个对象第二步就是使得new出来的新对象的proto指向构造函数的prototype,而箭头函数没有
(3)不能使用arguments对象
(4)不能用作generator函数,不能使用yield关键字
13.instanceof的3个弊端
instanceof用来确认构造函数的prototype属性是否出现在某个实例对象的原型链上。
但是有3个弊端:
(1)对于基本类型的判断
//字面量创建
console.log(1 instanceof Number)//false
//实例创建
console.log(new Number(1) instanceof Number)//true
(2)检测结果未必准确
var arr = [1, 2, 3];
console.log(arr instanceof Array) // true
console.log(arr instanceof Object); // true
function fn(){}
console.log(fn instanceof Function)// true
console.log(fn instanceof Object)// true
(3)不能检测undefined和null,需要用Object.prototype.toString.call()来检测
14.Object.prototype.toString.call()
Object原型上的toString和Number,Array,Boolean,Function等等原型上的toString都不同,作用并不是用来转换成字符串的。
Object上的toString它的作用是返回当前方法执行的主体(方法中的this)所属类的详细信息即"[object Object]"。
其中第一个object代表当前实例是对象数据类型的(这个是固定死的),第二个Object代表的是this所属的类是Object。
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局对象global的引用
15.展开运算符和rest运算符
首先两者格式都是'...'
区别主要在于:
展开运算符用于打开被封闭的项目,用来展开数组,对象等
rest运算符用于包装展展开的项目,比如用来给函数传参作为最后一个参数等
展开运算符实例:
let a = [1,2,3];
let b = [4,5,6];
let c = [...a , ...b];
console.log(c) //[1,2,3,4,5,6]
rest运算符实例:
const [a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(c) //[3,4,5,6]
其实就是一个意思,不必纠结。
16. document和window
window代表浏览器中的一个打开的窗口或者框架。
window对象会在
或者