JS基础知识点及常考面试题

原始类型有哪几种?null 是对象嘛?

原始类型有6种

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
原始类型存储的都是值,没有函数调用
  • 比如undefined.toString();会报错
  • ‘1’.toString() 可以被调用,是因为字符串被强制转换成了String类型,也就是对象类型;

null 不是对象

  • 虽然 typeof null = object ,但是不是对象

对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?

  • 原始类型存储的是值,对象类型存储的是指针,也就是引用类型;

  • 对象类型复制,复制的是变量的指针。

  • 看下面代码

    • 最后a和b的值是一样的。也就是说引用类型复制,一个改变,另一个也改变。
      let a = [];
      let b = a;
      b.push(1)
      console.log(a) // [1]
      console.log(b) // [1]
  • 看下函数参数是对象

      function test(person){
          person.age = 1;
          person = {
              name: 'aaa',
              age:2
          }
          return person;
      }
    
      let p1 = {
          name: 'bbb',
          age:3
      }
    
      let p2 = test(p1)
    
      console.log(p1) // {name:'bbb',age:1}
      console.log(p2) // {name:'aaa',age:2}
    • 可以看出来p1的age改变了。因为p1是引用类型,被当作函数参数传递,相当于复制,在函数中改变参数person,则p1也会跟着改变。所以p1.age = 1;
    • p2是函数的返回值,也就是函数中的person

typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?

  • typeof不能正确判断类型,能判断基本类型,除了null

      typeof '1' // string
      typeof 1 // number
      typeof false // boolean
      typeof undefined // undefined
      typeof Symbol() // sybmol
    
      typeof null // object
  • typeof对于对象,除了函数,都是object

      typeof [] // object
      typeof {} // pbject
      typeof function fn(){} // function
  • 值类型存处于栈内存

    let a = 100;
    let b = a;
    b = 200;
    console.log(a) // 100
    console.log(b) // 200

    image-20200606082348815

  • 引用类型存处于堆内存
    let obj1 = { a: 20 };
    let obj2 = obj1;
    obj2 = { a : 211 }
    console.log(a) // { a: 21 }
    console.log(b) // { a: 21 }

image

  • instanceof能正确判断对象原理是通过原型链

      const Person = function() {}
      const p1 = new Person()
      p1 instanceof Person // true
    
      var str = 'hello world'
      str instanceof String // false
    
      var str1 = new String('hello world')
      str1 instanceof String // true
    


### == 和 === 有什么区别?
#### 判断流程
+ 1.首先会判断两者类型是否相同。相同的话就是比大小了
+ 2.类型不相同的话,那么就会进行类型转换
+ 3.会先判断是否在对比 null 和 undefined,是的话就会返回 true
+ 4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
+ 5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
+ 6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断

### 什么是闭包?
+ 闭包:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。可以间接访问函数内部的变量。

+ 面试题:循环中使用闭包解决 `var` 定义函数的问题
  - 下面会依次打出6个6
    ```js
        for(var i = 1;i<=5;i++){
            setTimeout(function timer(){
                console.log(i)
            },i*1000)
        }
    ```
  - 解决办法:
    - 1.闭包
    ```js
        for(var i = 1;i<=5;i++){
            (function(j){
                setTimeout(function timer(){
                    console.log(j)
                },j*1000)
            })(i)
        }
    ```
    - 2.使用setTimeout第三个参数
    ```js
        for(let i = 1;i<=5;i++){
            setTimeout(function timer(){
                console.log(i)
            },i*1000,i)
        }
    ```
    - 3.使用let
    ```js
        for(let i = 1;i<=5;i++){
            setTimeout(function timer(){
                console.log(i)
            },i*1000)
        }
    ```

### 深浅拷贝
+ 先看下面一段代码
```js
    let a = {
        age: 1
    }
    let b = a;
    b.age = 2;
    console.log(a) // { age: 2}
  • 看出把a复制给b,当b改变了,a也随着改变。因为a是引用类型;可以通过拷贝解决这个问题;

浅拷贝

  • 通过Object.assign
      let a = {
          age: 1
      }
      let b = Object.assign({},a);
      b.age = 2;
      console.log(a) // { age: 1}
  • 通过展开运算符
      let a = {
          age: 1
      }
      let b = { ... a };
      b.age = 2;
      console.log(a) // { age: 1}
  • 如果对象是多层,上面方法就不能解决了,需要用到深拷贝
      let a = {
          age: 1,
          jobs: {
              first: 'FE'
          }
      }
      let b = { ...a }
      a.jobs.first = 'native'
      console.log(b.jobs.first) // native

深拷贝

  • JSON.parse(JSON.stringify(object))
      let a = {
          age: 1,
          jobs: {
              first: 'FE'
          }
      }
      let b = JSON.parse(JSON.stringify(a))
      a.jobs.first = 'native'
      console.log(b.jobs.first) // FE
    • 但是有局限性。
      • 1.会忽略 undefined
      • 2.会忽略 symbol
      • 3.不能序列化函数
      • 4.不能解决循环引用的对象

var、let 及 const 区别?

  • 函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部
  • var 存在提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用
  • var 在全局作用域下声明变量会导致变量挂载在 window 上,其他两者不会
  • let 和 const 作用基本一致,但是后者声明的变量不能再次赋值

类型转换

  • 字符串拼接
    100 + 10 = 110
    100 + '10' = '10010'
    true + '10' = 'true10'
  • == 运算符
    100 == '100' // true
    0 == '' // true
    0 == false // true
    false == '' // true
    null == undefined // true

数组方法会改变原数组

  • 改变原数组

    • pop
    • push
    • shift
    • unshift
  • 不改变原数组

    • concat
    • map
    • forEach
    • some
    • every
    • find
    • filter
    • slice
let arr = [1,2,3,4];

// let popRes = arr.pop()
// console.log(popRes,arr) // 4 [1,2,3] 

// let pushRes = arr.push(5) // length
// console.log(pushRes,arr) // 5 [1,2,3,4,5]


// let shiftRes = arr.shift()
// console.log(shiftRes,arr) // 1 [2,3,4]

// let unshiftRes = arr.unshift(0)
// console.log(unshiftRes,arr) // 5 [0,1,2,3,4]

// 不改变原数组

// let concatRes = arr.concat([5,6,7])
// console.log(concatRes,arr) // [1,2,3,4,5,6,7] [1,2,3,4]

// let mapRes = arr.map(item => item+1)
// console.log(mapRes,arr) // [2,3,4,5] [1,2,3,4]

// let filterRes = arr.filter(item => item > 2)
// console.log(filterRes,arr) // [3,4] [1,2,3,4]

// let sliceRes = arr.slice(1)
// console.log(sliceRes,arr) // [2,3,4] [1,2,3,4]


// forEach 不返回数据, 不改变原数组
// arr.forEach(item => item+1)
// console.log(arr) 


// 返回布尔值,不改变原数组
// let someRes = arr.some(item => item > 2)
// console.log(someRes,arr) // true [1,2,3,4]

// 返回布尔值,不改变原数组
// let everyRes = arr.every(item => item>2)
// console.log(everyRes,arr)


// 返回第一个为true的元素,不改变原数组
// let findRes = arr.find(item => item > 2)
// console.log(findRes,arr) // 3 [1,2,3,4]

从输入url到渲染出页面的整个过程:

Window.onload 和 DOMContentLoaded区别

Window.onload 和 DOMContentLoaded区别

异步

  • 因为js是单线程,所以异步不会阻塞代码执行,同步会阻塞代码执行
    // 异步
    console.log(1)
    setTimeout(() => {
    console.log(2)
    })
    console.log(3)
    // 结果: 1 3 2
    

// 同步
console.log(1)
alert(2)
console.log(3)
// 结果:1 2 3

+ 异步应用场景
  - 网络请求
  - 定时任务


### 作用域和闭包
#### 作用域
+ 全局作用域
+ 函数作用域
+ 块作用域

![image-20200606152240882](source/_posts/JS基础知识点及常考面试题/image-20200606152240882.png_posts/JS基础知识点及常考面试题/image-20200606152240882.png)

#### 自由变量
+ 1.一个变量在当前作用域没有定义,但被使用了
+ 2.向上级作用域去寻找,一层层找,直到找到为止
+ 3.如果全局作用域也没有,就会报错

>  所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
>  不是在执行的地方!!!

#### 闭包
```js
// 函数作为返回值
 function create() {
     const a = 100
     return function () {
         console.log(a)
     }
 }

 const fn = create()
 const a = 200
 fn() // 100

// 函数作为参数被传递
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}
print(fn) // 100

this不同应用场景:

  • 普通函数调用
  • 使用call apply bind调用
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数

this取值:

  • 是在函数执行时确定的,不是函数定义的时候
    function f1(){
      console.log(this)
    }
    

// 普通函数
f1() // window

// call
f1.call({a:2}) // { a: 2 }

// bind
let f2 = f1.bind({b:1})
f2() // { b: 1}

const zhansan = {
name: ‘张三’,
sayHi(){
console.log(this)
},
wait(){
setTimeout(function(){
console.log(this)
},100)
}
}
zhansan.sayHi() // zhangsan
zhansan.wait() // window

const lisi = {
name: ‘李四’,
sayHi(){
console.log(this) // lisi
},
wait(){
// 箭头函数
setTimeout(() => {
console.log(this)
})
}
}
lisi.sayHi() // lisi
lisi.wait() // lisi

// class
class People{
constructor(name){
this.name = name;
}
sayHi(){
console.log(this)
}
}
let p = new People(‘aaa’)
p.sayHi() // p


### 如何识别浏览器类型
### 分析拆解url各个部分

### 原型
Class

```javascript
class Student{
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    sayHi(){
        console.log(`${this.name}:${this.age}`)
    }
}

let xiaofei = new Student('小飞',10)
console.log(xiaofei.name)
console.log(xiaofei.age)
xiaofei.sayHi()

继承:

extends super constructor

class People{
    constructor(name){
        this.name = name;
    }
    sayHi(){
        console.log(`${this.name}`)
    }
}

class Student extends People{
    constructor(name,number){
        super(name)
        this.number = number;
    }
    eat(){
        console.log(`${this.number}`)
    }
}

let s1 = new Student('小明',12)
console.log(s1.name)
console.log(s1.number)
s1.sayHi()
s1.eat()

instanceof类型判断

s1 instanceof Student // true
s1 instanceof People // true
s1 instanceof Object // true

[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true

原型

class People{
    constructor(name){
        this.name = name;
    }
    sayHi(){
        console.log(`${this.name}`)
    }
}

class Student extends People{
    constructor(name,number){
        super(name)
        this.number = number;
    }
    eat(){
        console.log(`${this.number}`)
    }
}

let s1 = new Student('小明',12)
console.log(s1.__proto__ === Student.prototype) // true

image-20200606141146472

原型关系:

1.每个class都有显示原型prototype

2.每个实例都有隐士原型proto

3.实例的 proto===class的prototype

原型属性寻找规则:

1.先在自身上找属性和方法

2.自身没有,就去实例的隐士原型去找,也就是class的实例原型prototype

原型链

image-20200606142205510

console.log(s1.__proto__ === Student.prototype) // true
console.log(People.prototype === Student.prototype.__proto__)// true
console.log(Object.prototype === People.prototype.__proto__)// true

console.log(s1.hasOwnPrototype('name')) // true 。 hasOwnProperty() 判断属性是否在这个实例上
console.log(s1.hasOwnPrototype('sayHi')) // false

image-20200606142229258

attr和property区别:

1.property:修改对象属性,不会提现到html结构中

2.attribute:修改html属性,会改变html结构

3.两者都有可能引起dom重新渲染

4.尽量使用property

let p1 = document.getElementById('p1')

// property
p1.style.width = '200'

// attribute
p1.setAttribute('width',"200")

如何捕获js的异常

1.try catch 手动捕获

2.window.onerror

​ 对跨域的js,如cdn 不会有详细的报错信息

​ 对应压缩的js,需要配合sourcemap 反查到未压缩的代码行列

new Object()和Object.create()区别

1.{}等于new Object()。原型是Object.prototype

2.Object.create(null) 没有原型

3.Object.create({…}) 指定原型

let obj1 = {
    a:1,
    b:2,
    sum:function(){
        return this.a + this.b
    }
}

let obj2 = new Object({
    a:1,
    b:2,
    sum:function(){
        return this.a + this.b
    }
})

console.log(obj1 === obj2 ) // false
let obj3 = new Object(obj1)
console.log(obj3 === obj1 ) // true

let obj4 =Object.create(null)
console.log(obj4); // 什么也没有
let obj5 = new Object() 
console.log(obj5) // 有原型

let obj6  = Object.create(obj1)
console.log(obj6) // 空对象。有原型
obj6.__proto === obj1; // true ***
console.log(obj6 === obj1) // false

数组slice 和 splice 的区别:

1.功能区别:slice切片 splice剪接

2.参数和返回值

3.是否是纯函数

let arr = [1,2,3,4]
// slice
let arr1 = arr.slice(startIndex,endIndex)

// splice
// 插入
let spliceRes = arr.splice(1,2,'a') 
console.log(spliceRes,arr) // [2,3] [1,'a',4]
// 增加
let spliceRes = arr.splice(1,0,'a') 
console.log(spliceRes,arr) // [] [1,'a',2,3,4]

//删除
let spliceRes = arr.splice(1,2) 
console.log(spliceRes,arr) // [2,3] [1,4]

[10,20,30].map(parseInt)返回结果:

[10,20,30].map((num,index) => {
  return parseInt(num,index)
})

// 相当于
parseInt(10,0) // 10
parseInt(20,1) // NaN
parseInt(30,2) // NaN

   转载规则


《JS基础知识点及常考面试题》 朝飞 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
scrollWidth,clientWidth,offsetWidth的区别 scrollWidth,clientWidth,offsetWidth的区别
scrollWidth:对象的实际内容的宽度,不包边线宽度,会随对象中内容超过可视区后而变大。 clientWidth:对象内容的可视区的宽度,不包滚动条等边线,会随对象显示大小的变化而改变。 offsetWidth:对象整体的实际宽度,
2020-05-15
下一篇 
git git
git总结本地操作 查看变更情况 git status 将当前目录及其子目录下所有变更都加入到暂存区 git add . 比较工作区和暂存区的所有差异 git diff 比较某文件工作区和暂存区的差异 git dif
2020-05-07
  目录