面试题JavaScript篇(一)

目录

一、JS的数据类型都有哪些

1、基本数据类型:

2、引用数据类型

二、数据类型的存储方式

三、为什么0.1+0.2>0.3

四、数据类型的判断方式

1、typeof

2、instanceof

3、Object.prototype.toString.call()

4、constructor

五、Object.prototype.toString.call()与Array.prototype.toString.call()的区别

六、为什么typeof null 是Object

七、==和===有什么区别

1、===

2、==

八、NaN === NaN返回什么

九、字面量创建对象和new创建对象有什么区别

十、new会发生的事

十一、call、apply、bind的区别

十二、什么是作用域,什么是作用域链

十三、什么是执行栈,什么是执行上下文

1、执行上下文

2、执行栈

十四、什么是闭包?闭包的作用?闭包的应用?

1、是什么

2、特点,作用

3、应用

十五、原型和原型链

1、原型

2、原型链

十六、JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点

1、原型继承

2、组合继承

3、寄生组合继承

4、ES6的extends

十七、null和undefined的区别


一、JS的数据类型都有哪些

1、基本数据类型:

  • Number
  • String
  • Boolean
  • null
  • Undefined
  • Symbol
  • BigInt

2、引用数据类型

统称为Object类型,具体有:

  • RegExp
  • Date
  • Function
  • Array
  • Object

二、数据类型的存储方式

  • 基本数据类型:存储在栈中
  • 引用数据类型:存储在堆中
  • 两种方式的联系:在栈中保存数据的引用地址,这个引用地址指向的是对应的数据,以便快速查找到堆内存中的对象。
  • 两种方式的区别:栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗

三、为什么0.1+0.2>0.3

因为在JS底层中,每个变量是以二进制表示,固定长度为64位,其中第1位是符号位,再往后11位是指数为,最后52表示的是尾数位,而0.1和0.2转为二进制的时候是无限循环小数,所以JS就会进行截取,截取以后0.1和0.2就不是他们本身了,要比原来大那么一丢丢,所以0.1+0.2就>0.3了

解决办法:先给他们放大倍数,随后在除以相应倍数

const a = 0.1;
const b = 0.2;

console.log(a + b === 0.3)   // false
console.log((a * 1000 + b * 1000) / 1000 === 0.3)  // true

四、数据类型的判断方式

1、typeof

特点:无法分辨是null还是Object

    typeof undefined // 'undefined' 
    typeof '10' // 'String' 
    typeof 10 // 'Number' 
    typeof false // 'Boolean' 
    typeof Symbol() // 'Symbol' 
    typeof Function // ‘function' 
    typeof null // ‘Object’ 
    typeof [] // 'Object' 
    typeof {} // 'Object'

2、instanceof

特点:只能判断某对象是否存在于目标对象得的原型链上,不能判断字面量的基本数据类型

function Foo() { }
    var f1 = new Foo();
    var d = new Number(1)


    console.log(f1 instanceof Foo);// true
    console.log(d instanceof Number); //true
    console.log(123 instanceof Number); //false   -->不能判断字面量的基本数据类型

3、Object.prototype.toString.call()

特点:可以分清各种数据类型,但是不能细分为谁谁的实例

 console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]" 
    console.log(Object.prototype.toString.call(null)); // "[object Null]" 
    console.log(Object.prototype.toString.call(123)); // "[object Number]" 
    console.log(Object.prototype.toString.call("abc")); // "[object String]" 
    console.log(Object.prototype.toString.call(true)); // "[object Boolean]" 


    function fn() {
      console.log("ming");
    }
    var date = new Date();
    var arr = [1, 2, 3];
    var reg = /[hbc]at/gi;
    console.log(Object.prototype.toString.call(fn));// "[object Function]" 
    console.log(Object.prototype.toString.call(date));// "[object Date]" 
    console.log(Object.prototype.toString.call(arr)); // "[object Array]"
    console.log(Object.prototype.toString.call(reg));// "[object RegExp]"

4、constructor

var d = new Number(1)
    var e = 1
    function fn() {
      console.log("ming");
    }
    var date = new Date();
    var arr = [1, 2, 3];
    var reg = /[hbc]at/gi;

    console.log(e.constructor);//ƒ Number() { [native code] }
    console.log(e.constructor.name);//Number
    console.log(fn.constructor.name) // Function 
    console.log(date.constructor.name)// Date 
    console.log(arr.constructor.name) // Array 
    console.log(reg.constructor.name) // RegExp

五、Object.prototype.toString.call()与Array.prototype.toString.call()的区别

  • Object.prototype.toString.call()返回的是统一格式
  • Array.prototype.toString.call()的部分类型无法检验,无法检验null、undefined,会报错,数组也不会输出其是Array类型
    function fn() {
      console.log("ming");
    }
    var date = new Date();
    var arr = [1, 2, 3];
    var reg = /[hbc]at/gi;

    console.log(Array.prototype.toString.call(undefined)); // 报错
    console.log(Array.prototype.toString.call(null)); // 报错
    console.log(Array.prototype.toString.call(123)); // "[object Number]" 
    console.log(Array.prototype.toString.call("abc")); // "[object String]" 
    console.log(Array.prototype.toString.call(true)); // "[object Boolean]" 
    console.log(Array.prototype.toString.call(fn)); // "[object Function]" 
    console.log(Array.prototype.toString.call(date)); // "[object Date]" 
    console.log(Array.prototype.toString.call(arr)); // "1,2,3"
    console.log(Array.prototype.toString.call(reg));// "[object RegExp]"

六、为什么typeof null 是Object

因为在JavaScript中,不同的对象都是使用二进制存储的,如果二进制前三位都是0的话,系统会判断为是Object类型,而null的二进制全是0,自然也就判断为Object

【拓展】000 =》对象;1 =》整型;010 =》双精度类型;100 =》字符串;110 =》布尔类型

七、=====有什么区别

1、===

严格意义上的相等,会比较两边的数据类型和值大小

  • 数据类型不同返回false
  • 数据类型相同,但值大小不同,返回false

2、==

非严格意义上的相等,

  • 两边类型相同,比较大小

  • 两边类型不同,根据下方表格,再进一步进行比较。

Null == Undefined ->true
String == Number ->先将String转为Number,在比较大小
Boolean == Number ->现将Boolean转为Number,在进行比较
Object == String,Number,Symbol -> Object 转化为原始类型

八、NaN === NaN返回什么

返回 falseNaN永远不等于NaN。

判断是否为NaN用一个函数isNaN来判断。isNaN传入的如果是其他数据类型,那么现将它使用Number()转为数字类型在进行判断

九、字面量创建对象和new创建对象有什么区别

字面量:

  • 字面量创建对象更简单,方便阅读
  • 不需要作用域解析,速度更快

new:

  • 创建一个新对象
  • 使新对象的__proto__指向原函数的prototype
  • 改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
  • 判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回result

十、new会发生的事

  • 创建一个新对象
  • 使新对象的__proto__指向原函数的prototype
  • 改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
  • 判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回result

手写new:见=》JavaScript手写题_Kw_Chng的博客-CSDN博客

十一、call、apply、bind的区别

1、使用

fn.call(thisArg, arg1, arg2, arg3, ...)
调用fn.call时会将fn中的this指向修改为传入的第一个参数thisArg;
将后面的参数传入给fn,并立即执行函数fn

apply(thisArg, [argsArr])
fn.apply的作用和call相同:修改this指向,并立即执行fn。
apply接受两个参数,第一个参数是要指向的this对象,
第二个参数是一个数组,数组里面的元素会被展开传入fn,作为fn的参数

bind(thisArg, arg1, arg2, arg3, ...)
fn.bind的作用是只修改this指向,但不会立即执行fn;会返回一个修改了this指向后的fn。
需要调用才会执行:bind(thisArg, arg1, arg2, arg3, ...)()。bind的传参和call相同

2、相同点

  • 三个都是用于改变this指向;
  • 接收的第一个参数都是this要指向的对象;
  • 都可以利用后续参数传参。

3、不同点

  • call和bind传参相同,多个参数依次传入的;
  • apply只有两个参数,第二个参数为数组;
  • call和apply都是对函数进行直接调用,而bind方法不会立即调用函数,而是返回一个修改this后的函数。

手写call、apply、bind:见=》JavaScript手写题_Kw_Chng的博客-CSDN博客

十二、什么是作用域,什么是作用域链

  • 规定变量和函数的可使用范围称作作用域
  • 每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链

十三、什么是执行栈,什么是执行上下文

1、执行上下文

1.1、全局执行上下文
创建一个全局的window对象,并规定this指向window,执行js的时候就压入栈底,关闭浏览器的时候才弹出

1.2、函数执行上下文
每次函数调用时,都会新创建一个函数执行上下文
执行上下文分为创建阶段和执行阶段

  • 创建阶段:函数环境会创建变量对象:arguments对象(并赋值)、函数声明(并赋值)、变量声明(不赋值),函数表达式声明(不赋值);会确定this指向;会确定作用域
  • 执行阶段:变量赋值、函数表达式赋值,使变量对象编程活跃对象

1.3、eval执行上下文

2、执行栈

  1. 首先栈特点:先进后出
  2. 当进入一个执行环境,就会创建出它的执行上下文,然后进行压栈,当程序执行完成时,它的执行上下文就会被销毁,进行弹栈。
  3. 栈底永远是全局环境的执行上下文,栈顶永远是正在执行函数的执行上下文
  4. 只有浏览器关闭的时候全局执行上下文才会弹出

十四、什么是闭包?闭包的作用?闭包的应用?

1、是什么

一个函数和对其周围状态(词法环境)的引用捆绑在一起, 这样的组合就是闭包。闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用

2、特点,作用

  • ​ 让外部访问函数内部变量成为可能;
  • ​ 可以避免使用全局变量,防止全局变量污染;
  • ​ 可以让局部变量常驻在内存中;
  • ​ 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

3、应用

  • 设计模式中的单例模式
  • for循环中的保留i的操作
  • 防抖和节流
  • 函数柯里化

十五、原型和原型链

1、原型

2、原型链

读取对象属性时,会优先对象自身属性,如果对象中有,则使用,没有则去对象的原型中寻找如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null))。如果依然没有找到,则返回undefined。

十六、JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点

1、原型继承

  • 把父类的实例作为子类的原型
  • 子类的实例共享了父类构造函数的引用属性
  • 不能传参
    var person = {
      friends: ["a", "b", "c", "d"]
    }

    var p1 = Object.create(person)

    p1.friends.push("aaa")

    console.log(p1);
    console.log(person);

2、组合继承

  • 在子函数中运行父函数,但是要利用call把this改变一下,再在子函数的prototype里面new Father() ,使Father的原型中的方法也得到继承,最后改变Son的原型中的constructor
  • 优点可传参,不共享父类引用属性
  • 调用了两次父类的构造函数,造成了不必要的消耗,父类方法可以复用

3、寄生组合继承

    function Father(name) {
      this.name = name
      this.hobby = ["篮球", "足球", "乒乓球"]
    }

    Father.prototype.getName = function () {
      console.log(this.name);
    }

    function Son(name, age) {
      Father.call(this, name)
      this.age = age
    }

    Son.prototype = Object.create(Father.prototype)
    Son.prototype.constructor = Son

    var s2 = new Son("ming", 18)
    console.log(s2);

4、ES6的extends

当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)。继承发生时,被继承的类称为 父类(超类),继承的类称为 子类

    class Son3 extends Father { // Son.prototype.__proto__ = Father.prototype
      constructor(y) {
        super(200)  // super(200) => Father.call(this,200)
        this.y = y
      }
    }

十七、null和undefined的区别

        1、undefined不是关键字,而null是关键字;

         2、undefined和null被转换为布尔值的时候,两者都为false;

         3、undefined在和null进行==比较时两者相等,===比较时两者不等

         4、使用Number()对undefined和null进行类型转换,Number(null)===0,Number(undefined) === NaN

        5、undefined本质上是window的一个属性,而null是一个对象;

        6、undefined和null的用途 :null表示没有对象,即不应该有值,经常用作函数的参数,或作为原型链的终点。undefined表示缺少值,即应该有值,但是还没有赋予(变量提升时默认会赋值为undefined,函数参数未提供默认为undefined,函数的返回值默认为undefined)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值