【Step-By-Step】面试题二(2019.7.9)

197 阅读3分钟
1. == 和 === 有什么区别?
  • 首先会判断两者类型是否相同。相同的话就是比大小了
  • 类型不相同的话,那么就会进行类型转换
  • 会先判断是否在对比 null 和 undefined,是的话就会返回 true
  • 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
1 == '1'
      ↓
1 ==  1
  • 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
'1' == true'1' ==  1
        ↓
 1  ==  1
  • 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
'1' == { name: 'yck' }
        ↓
'1' == '[object Object]'

如图详解:

=== 来说就简单多了,就是判断两者类型和值是否相同。

2.什么是闭包?
  • 闭包的定义:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。
function A() {
    var a = 1;
    function B() {
        console.log(a)
    }
    B()
}    
A();
  • 循环中使用闭包解决 var 定义函数的问题
for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

第一种是使用闭包的方式

for (var i = 1; i <= 5; i++) {
    (function(j) {
        setTimeout(function timer() {
        console.log(j)
        }, j * 1000)
    })(i)
}

第二种就是使用 setTimeout 的第三个参数,这个参数会被当成 timer 函数的参数传入。

for (var i = 1; i <= 5; i++) {
    setTimeout(
        function timer(j) {
            console.log(j)
        },
        i * 1000,
        i
    )
}

第三种就是使用 let 定义 i 了来解决问题了,这个也是最为推荐的方式

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}
3.什么是浅拷贝?如何实现浅拷贝?什么是深拷贝?如何实现深拷贝?
  • 浅拷贝(只解决了第一层的问题)

Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。

1.基本类型
let a = {
  age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

2.复杂类型(不行)
let a = { 
        age: 1,
    arr: [1,2,3]
};
let b = Object.assign({}, a);
a.age = 2;
a.arr.push(4)
console.log(a, b); // { age: 2, arr: [1,2,3,4] }, { age: 1, arr: [1,2,3,4] }

展开运算符 ... 来实现浅拷贝

1.基本类型
let a = { age: 1 }
let b = {...a}
a.age = 2;
console.log(a, b);// {age: 2} {age: 1}

2.复杂类型(不行)
let a = { age: 1, arr: [1,2,4]}
let b = {...a}
a.age = 2;
a.arr.push(4)
console.log(a, b);// {age: 2, arr:[1, 2, 4, 4]} {age: 1, arr:[1, 2, 4, 4]}
  • 深拷贝

JSON.parse(JSON.stringify(object))

let a = {
    age:1,
    jobs:{
        first: 'FE'
    }
}    
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'cl';
console.log(a,b);// {age: 1, jobs: {first: "cl"}} , {age: 1, jobs: {first: "FE"}}

缺点: 1.会忽略 undefined 2.会忽略 symbol 3.不能序列化函数

 let a = {
    age: undefined,
    sex: Symbol('male'),
    jobs: function () { },
    name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}

4.不能解决循环引用的对象

let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
  • 深拷贝函数
function deepCopy(obj) {
   let result = Array.isArray(obj) ? [] : {};
   for (let key in obj) {
       if (obj.hasOwnProperty(key)) {
           if (typeof obj[key] === "object") {
               result[key] = deepCopy(obj[key]);// 递归复制
           } else {
               result[key] = obj[key];
           }
       }
   }
   return result;
}
4.如何理解原型?如何理解原型链?

1.对于 obj 来说,可以通过 proto 找到一个原型对象,在该对象中定义了很多函数让我们来使用。

2.原型的 constructor 属性指向构造函数,构造函数又通过 prototype 属性指回原型,但是并不是所有函数都具有这个属性,Function.prototype.bind() 就没有这个属性。

3.Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它

4.Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它

5.函数的 prototype 是一个对象

6.对象的 proto 属性指向原型, proto 将对象和原型连接起来组成了原型链