目录
延迟加载js有哪些方式?
数据类型有哪些?
null和undefined的区别
数据类型检测的方式有哪些?
(1)typeof
(2)instanceof
(3)constructor
(4)Object.prototype.toString.call()
判断数组的方式有哪些
通过Object.prototype.toString.call()做判断
通过原型链做判断
通过ES6的Array.isArray()做判断
通过instanceof判断
通过Array.prototype.isPrototypeOf()判断
typeof null的结果是什么,为什么?
为什么0.1+0.2!==0.3,如何让其相等
JS数组去重
方式一:new set
方式二:indexOf
方式三:sort
let、const、var的区别
栈
队列
链表
链表和数组的区别
字典
二叉树
结构
二叉树前序遍历
前序遍历 栈版本 非递归版本
二叉树中序遍历
中序遍历 栈版本 非递归版本
后序遍历
后序遍历 栈版本 非递归版本
1.作者在设计js的都是先设计null(为什么设计了null:最初设计js的时候借鉴了java的语言)
2.null会被演示转化成0,很不容易发现错误.
3.先有null后有undefined是为了填补之前的坑.
具体区别:JavaScript的最初版本是这样区分的:
null是一个表示"无"的对象(空对象指针),转为数值是0;
undefined,转为数值时为NaN
null本质是一个指针,指向一个空的对象,作用是js引擎用来垃圾回收,undefined是未初始化变量的默认值,作用是区分空变量和未初始化的变量. 简单来说,undefined 是一个空的变量,null是一个空的对象.
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof []); // object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof undefined) ; // undefined
console.log(typeof null ); // object
其中数组、对象、null都会被判断为object,其他判断都正确。
instanceof可以正确的判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。
console.log(2 instanceof Number) ; // false
console.log(true instanceof Boolean) ; // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof 0bject); // true
可以看到,instanceof只能正确判断引用数据类型,而不能判断基本数据类型。instanceof运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的prototype属性。
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function(){}).constructor === Function); // true
console.log(({}).constructor === Object); // true
constructor有两个作用,一是判断数据的类型,二是对象实例通过constructor对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了:
function Fn(){};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor === Fn); //false
console.log(f.constructor === Array); //true
Object.prototype.toString.call()使用Object对象的原型方法toString来判断数据类型:
var a = object.prototype.toString;
console.log(a.ca1l(2));
console.log(a.call(true));
console.log(a.call('str'));
console.log(a.call([]));
console.log(a.call(function(){}));
console.log(a.call({}));
console.log(a.call(undefined));
console.log(a.call(null));
同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,者是为什么?
这是因为toString是Object的原型方法,而Array、function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。
Object.prototype.toString.call(obj).slice(8,-1) === 'Array';
obj.__proto__ === Array.prototype
Array.isArray(obj);
obj.instanceof Array
Array.prototype.isPrototypeOf(obj)
typeof null的结果是Object。
在JavaScript第一个版本中,所有值都存储在32位的单元中,每个单元包含一个小的类型标签(1~3bits)以及当前要存储的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型。
000:object - 当前存储的数据指向一个对象 1:int - 当前存储的数据是一个31位的有符号整数 010:double - 当前存储的数据指向一个双精度的浮点数 100:string - 当前存储的数据指向一个字符串 110:boolen - 当前存储的数据是布尔值
如果最低位是1,则类型标签标志位的长度只有一位;如果最低位是0,则类型标签标志位的长度占三位,为存储其他四种数据类型提供了额外两个bit的长度。
有两种特殊数据类型:
undefined的值是(-2)^30(一个超出整数范围的数字);
null的值是机器码NULL指针(null指针的值全是0)
也就是说null的类型标签也是000,和object的类型标签是一样的,所以会被判定为object。
在开发过程中遇到类似这样的问题:
let n1 = 0.1, n2 = 0.2
console.log(n1+n2)//0.3000000000004
计算机是通过二进制的方式存储数据的,所以计算机计算0.1+0.2的时候,实际上是计算两个数的二进制的和。0.1的二进制是0.0001100110011001100......(1100循环),0.2的二进制是0.0011001100110011......(1100循环)这两个数的二进制都是无限循环的数,JavaScript中只有Number这一种数字类型,它的实现遵循IEEE 754标准,用64位固定长度来表示,且遵从“0舍1入”的原则。根据这个原则,0.1和0.2的二进制数相加,再转换为十进制数就是0.30000000000000004。
因此当我们想要得到想要的结果时,可以使用toFixed(num)方法,将Number四舍五入为指定小数位数的数字
(n1+n1).toFixed(2) //注意,toFixed为四舍五入
var arr1 = [1,2,3,2,4,1];
function unique(arr){
return [...new Set(arr)]
}
console.log( unique(arr1) );
var arr2 = [1,2,3,2,4,1];
function unique( arr ){
var brr = [];
for( var i=0;i
var arr3 = [1,2,3,2,4,1];
function unique( arr ){
arr = arr.sort();
var brr = [];
for(var i=0;i
(1)块级作用域:块级作用域由{ }包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了ES5中内层变量可能覆盖外层变量、用来计数的循环变量泄露为全局变量两个问题。
(2)变量提升:var存在变量提升,let和const不存在变量提升,即变量只能在声明之后使用,否则会报错。
(3)给全局添加属性:浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。
(4)重复声明:var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的变量。const和let不允许重复声明变量。
(5)暂时性死区:在使用let、const声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。
(6)初始值设置:在变量声明时,var和let可以不用设置初始值。而const声明变量必须设置初始值。
后入先出
在js中,我们可以通过push()方法和pop()方法对一个数组进行操作,实现入栈和出栈。
push()方法,是将某一元素添加到数组的末尾位置,而pop()方法,是将数组的最后一个元素删除并将被删除的元素返回。
先进先出
同上,我们可以通过push()方法和shift()方法对数组进行操作,实现入队和出队
shift()方法,将数组的第一个元素删除
什么是链表
(1)多个元素存储的列表
(2)链表中的元素在内存中不是顺序存储的,而是通过" next'指针联系在起的。
js中的原型链原理就是链表结构
(1)数组:有序存储的,在中间某个位置删除或者添加某个元素,其他元素要跟着动。
(2)链表中的元素在内存中不是顺序存储的,而是通过" next"指针联系在一起的。
(3)数组如果在中间插入新的元素,其他元素会重新计算,链表不会重新计算。
(4)查找时,数组通过下标进行查找,链表每次查找都需要从头开始找。
创建map
let map = new Map();
map.set('a','1');
map.set('b','2');
console.log(map.get('b'));获取并打印b
map.delete('a')
map.size 读取map的个数,相当于数组的length
map.clear() 将map中的数全删了
const tree = {
val:'1',
left:{
val:'2',
left:{val:'4',left:null,right:null},
right:{val:'5',left:null,right:null}
}
right:{
val:'3',
left:{val:'6',left:null,right:null},
right:{val:'7',left:null,right:null}
}
}
var preorderTraversal = function(root) {
let arr = [];
var fun = ( node ) => {
if( node ){
arr.push(node.val);
fun(node.left);
fun(node.right);
}
}
fun(root);
return arr;
};
var preorderTraversal = function(root) {
if(!root) return [];
let arr = [];
//根节点入栈
let stack = [root];
while(stack.length){
//出栈
let o = stack.pop();
arr.push(o.val);
o.right && stack.push(o.right);
o.left && stack.push(o.left);
}
return arr;
};
var inorderTraversal = function(root) {
let arr = [];
var fun = ( root ) => {
if( !root ) return;
fun(root.left);
arr.push(root.val);
fun(root.right);
}
fun(root);
return arr;
};
var inorderTraversal = function(root) {
const arr = [];
const stack = [] ;
let o = root ;
while( stack.length || o ){
while(o){
stack.push(o);
o=o.left;
}
const n = stack.pop() ;
arr.push( n.val ) ;
o = n.right ;
}
return arr;
};
var postorderTraversal = function(root) {
const arr = [];
const fun = (node)=>{
if(node){
fun(node.left);
fun(node.right);
arr.push(node.val)
}
}
fun(root);
return arr;
};
var postorderTraversal = function(root) {
let arr = [];
let stack = [root];
while(stack.length){
const o = stack.pop();
arr.unshift(o.val);
o.left && stack.push(o.left);
o.right && stack.push(o.right);
}
return arr;
};