JavaScript面经

目录

延迟加载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的区别

队列

链表

链表和数组的区别

字典

二叉树

结构

二叉树前序遍历

前序遍历 栈版本  非递归版本

二叉树中序遍历

中序遍历 栈版本  非递归版本

后序遍历

后序遍历 栈版本  非递归版本


延迟加载js有哪些方式?

     延迟加载 : async、defer
    defer : 等html全部解析完成,才会执行js代码,顺次执行js脚本.
    async : async是和html解析同步的(一起的),不是顺次执行js脚本(谁先加载完谁先执行)

数据类型有哪些?

基本类型:string、number、boolean、undefined、null、symbol、bigint
引用类型:object
NaN是一个数值类型,但是不是一个具体的数字。

null和undefined的区别

    1.作者在设计js的都是先设计null(为什么设计了null:最初设计js的时候借鉴了java的语言)

    2.null会被演示转化成0,很不容易发现错误.

    3.先有null后有undefined是为了填补之前的坑.

    具体区别:JavaScript的最初版本是这样区分的:

      null是一个表示"无"的对象(空对象指针),转为数值是0;

      undefined,转为数值时为NaN

 null本质是一个指针,指向一个空的对象,作用是js引擎用来垃圾回收,undefined是未初始化变量的默认值,作用是区分空变量和未初始化的变量. 简单来说,undefined 是一个空的变量,null是一个空的对象.

数据类型检测的方式有哪些?

(1)typeof

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,其他判断都正确。

(2)instanceof

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属性。

(3)constructor

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

(4)Object.prototype.toString.call()

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()做判断

    Object.prototype.toString.call(obj).slice(8,-1) === 'Array';
  • 通过原型链做判断

    obj.__proto__ === Array.prototype
  • 通过ES6的Array.isArray()做判断

    Array.isArray(obj);
  • 通过instanceof判断

    obj.instanceof Array
  • 通过Array.prototype.isPrototypeOf()判断

    Array.prototype.isPrototypeOf(obj)

typeof null的结果是什么,为什么?

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。

为什么0.1+0.2!==0.3,如何让其相等

在开发过程中遇到类似这样的问题:

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为四舍五入

JS数组去重

方式一:new set

var arr1 = [1,2,3,2,4,1];
function unique(arr){
    return [...new Set(arr)]
}
console.log(  unique(arr1) );

方式二:indexOf

var arr2 = [1,2,3,2,4,1];
function unique( arr ){
    var brr = [];
    for( var i=0;i<arr.length;i++){
        if(  brr.indexOf(arr[i]) == -1 ){
            brr.push( arr[i] );
        }
    }
    return brr;
}
console.log( unique(arr2) );

方式三:sort

var arr3 = [1,2,3,2,4,1];
function unique( arr ){
    arr = arr.sort();
    var brr = [];
    for(var i=0;i<arr.length;i++){
        if( arr[i] !== arr[i-1]){
            brr.push( arr[i] );
        }
    }
    return brr;
}
console.log( unique(arr3) );

let、const、var的区别

(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;
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值