js基础知识点及常见的面试题(一)

基本类型(Primitive)

面试题 基本类型有哪几种?null是对象吗?

js基本类型

boolean

null

undefined

number

string

symbol

基本类型存储的都是值,是没有函数可以调用的,比如undefined.toString(),你可能会有疑问,'1'.toString()是可以使用的,'1'已经不是基本类型了,而是被强制转化成了String类型,也就是对象类型,所以可以调用toString函数。

除了会在必要的情况下强转类型意外,原始类型还有一些坑。

其中js的number浮点类型的,在使用过程中遇到某些bug,比如0.1+0.2 !== 0.3 返回的是false,string类型是不可变的,无论你在string类型上调用任何方法,都不会对值有改变。

另外对于null来说,很多认为它是一个对象类型,这是不对的,虽然typeof 返回的是object,但它只是js中一直存在的bug,js最初的版本是32位系统,为了性能的考虑使用低位存储变量的类型信息,000开头的是对象,然而null表示为全零,所以将他错误的判断为对象,虽然现在内部类型的代码已经改变,但对于这个bug一直保存了下来。

对象类型

面试题 对象类型和基本类型的不同指出?函数参数是对象会发生什么问题?

在js中,除了基本类型就是对象类型了,对象类型和基本类型不同的是,基本类型存储的是值,对象类型存储的是指针,当你创建一个对象类型的时候,计算机内存会帮我们开辟一个空间来存放值,但我们需要找到这个空间,这个空间会拥有一个指针。

const a = []

对于常量a来说,假设地址为#0001,在该地址存储了[],常量地址存储了地址#0001,再看以下代码

const a = []

const b = a

console.log(b)

当我们将变量赋给另一个变量时,复制的是原本变量的指针,当我们修改存放的地址的值,也导致了两个变量的值发生了改变。

接下来我们看下函数参数是对象的情况

function test(person){

    person.age =26

    person ={

        name:'han',

        age:30

    }

    return person

}

const p1 = {

    name:'xiao',

    age:'34'

}

const p2 = test(p1)

console.log(p1)

console.log(p2)

对于以上代码你能否写出正确的结果,接下来我们来分析一下

首先函数传参是传递的指针副本

到函数内部修改参数这步,当前p1的值也被修改了

当我们重新为person分配一个对象时,就产生了分歧

所以person有了一个新的指针,也就和p1没什么关系了,导致了最后两个变量的值时不同的

typeof vs instanceof

面试题 typeof能否正确判断类型?instanceof正确对象的原理是什么? typeof对于原始类型来说,除了null都可以显示正确的类型。

typeof 1 // number

typeof '1' //string

typeof undefined // 'undefined'

typeof true // 'boolean'

typeof Symbol() // 'symbol'

typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型

typeof [] // 'object'

typeof {} // 'object'

typeof console.log // 'function'

如果我们想判断一个对象的正确类型,这时候我们要考虑使用instanceof,因为内部机制时通过原型链进行判断的

const Person = function(){


}

const p1 = new Person()

p1 instanceof Person

var str ='person'

str instanceof String

var str1 = new String('xiao')

str1 instanceof str1

对于基本类型来说,你通过instanceof来判断类型是不行的,当然我们是有办法让instanceof判断基本类型的

class PrimitiveString {

  static [Symbol.hasInstance](x) {

    return typeof x === 'string'

  }

}

console.log('hello world' instanceof PrimitiveString) // true

你可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。

类型转换

首先我们知道js中类型转换只有三种情况:

转换为布尔值

转换为数字

转换为字符串

我们先看一下下面的例子

原始值                      转化目标            结果

number                      布尔值          除了0,-0,NaN都为true

string                      布尔值          除了字符串都为true

undefined null              布尔值          false

引用类型                    布尔值          true

number                      字符串          5=>'5' 

布尔 函数 symbol            字符串          'true'

数组                        字符串          [1,2]=>1,2

对象                        字符串          "[object object]"

字符串                      数字            '1'=>1,'a'=>NaN

数组                        数字            空数组转化为0,有一个元素且为数字

                                            转化为数字,其他情况则为NaN

null                        数字            0

除了数组的引用类型          数字            NaN

symbol                      数字            报错

转boolean

在条件判断时,除了undefined,null,false,NaN,'',0,-0,其他所有的值都为true,包括所有对象

对象转换原始类型 对象在转换类型的时候,会调用内置的[[ToPrimitive]]函数,对于该函数来说,算法逻辑一般如下:

如果已经时原始类型了,那就不需要转换了

如果需要转字符串类型就调用x.toString(),转换为基础类型的话就返回转换的值。不转换字符串类型的话就先调用valueof,结果不是基础类型的话再调用tostring

调用x.valueof(),如果转换为基础类型,就返回转换的值

如果没有返回原始类型,就会报错

当然你也可以重写Symbol.toPrimitive,该方法在转原始类型时调用优先级最高

let a={

    valueof(){

        return 0

    },

    toString(){

        return 1

    },

    [].toPrimitive(){

        return 2

    }

}

1+a //3

四则运算符

加法运算符不同于其他几个运算符,它有以下特点

运算中其中一方为字符串,那么就会把另一方转化为字符串

如果一方不是字符串或者数字,那么会将数字转化为字符串

1+'1' //1

true+true //2

4+[1,2,3] //41,2,3

如果对于答案有疑问的话,请看解析:

对于第一行代码来说,触发特点一,所以将数字以1转化为字符串,结果得到‘11’

对于第二行代码来说,触发特点二,所以将true转为数字1

对于第三行代码来说,触发特点二,将数组通过toString转化为字符串1,2,3,得到结果41,2,3

另外对于加法还需要主义这个表达式

'a'++'b' //"aNaN"

应为+'b'等于NaN,所以结果为“NaN”,你可能也会在一些代码中看到过+‘1’的形式来快速获取number类型 对了除了加法的运算符来说,只要其中一方时数字,那么另一方就会转化为数字

4*'3' //12

4*[] //0

4*[1,2] //NaN

比较运算符

1.如果是对象,就通过toPrimitive转化对象 2.如果是字符串,就通过unicode字符索引比较

let a={

    valueof(){

        return 0

    },

    toString(){

        return '1'

    }

}

a>-1 //true

在以上代码中,因为a是对象,所以会通过valueof转换为原始类型再比较

this

面试题 如何正确判断this?箭头函数this是什么? this是很多人混淆的概念,但是其实它一点都不难,只是网上很多文章把简单的东西说的复杂了,在这一小节中,你一定会彻底明白this这个概念的

function foo(){

    console.log(this.a)

}

var a=1

foo()

const obj = {

    a:2,

    foo:foo

}

obj.foo()

const c = new foo()

接下来我们一个个分析上面几个场景

对于直接调用foo来说,不管函数被放在什么地方,this一定是window

对于obj.foo()来说,我们只需记住,谁调用了函数,谁就是this,所以在这个场景下foo函数中的this就是obj

对于new方式来说,this永远绑定在了c上面,不会被任何方式改变this

首先箭头函数其实没有this的,箭头函数中的this只取决包裹箭头函数的第一个普通函数的 this

function a(){

    return ()=>{

        return ()=>{

            return this

        }

    }

}

console.log(a()())

说完了上面的几种情况,其实很多代码中的this应该没什么问题了,下面我们看看箭头函数中的this,在这个例子中,因为包裹箭头函数的第一个普通函数是a,所以此时的this是window。另外对箭头函数使用bind,apply,call这类函数是无效的。

最后情况也就是bind这些改变上下文的api了,对于这些函数来说,this取决于第一个参数,如果第一个参数为空,那么就是window

那么说到bind,如果对一个函数进行多次bind,那么上下文会是什么呢?

let a ={}

let fn = function(){

    console.log(this)

}

fn.bind().bind(a)()

如果你认为输出的结果是a,那么就错了,其实我们可以换成另一种形式

//fn.bind().bind(a)等于

let fn2 = function(){

    return function(){

        return fn.apply()

    }

}

fn2()

可以从上述代码中发现,不管我们给函数bind几次,fn中的this永远由第一次bind决定,所以结果永远是window

let a = {

    name:'han'

}

function foo(){

    console.log(this.name)

}

foo.bind(a)()  //han

以上就是this的规则了,但是可能会发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定this最终指向哪里。

首先,new的优先级方式最高,接下来是bind这些函数了,然后obj.foo()这种调用方式,最后foo这种调用方式,同时,箭头函数的this一旦绑定,就不会再被任何方式有所改变

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。