【前端面试基础】(二)变量、作用域、原型、闭包

259 阅读4分钟

前言:大家好,我是忆白,本文根据慕课网双越老师《一天时间迅速准备前端面试 快速构建初级前端知识体系》课程整理的面试常考题目以及相应知识扩展,持续更新。

1. 值类型和引用类型

常见值类型:

  • undefined
  • String字符串
  • Number
  • Boolean
  • Symbol(ES6)新增

常见引用类型:

  • 对象Object
  • Array数组
  • null (特殊引用类型,指针指向为空地址)
  • function 函数(特殊引用类型,但不用于存储数据)

2. typeof运算符

  • 能识别所有的值类型(输出类型字符串如:"string")
  • 识别函数(输出'function')
  • 判断是否是引用类型(只输出object,不可再细分)

3. 手写深拷贝

function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null,或者不是引用类型,直接返回
        return obj;
    }
​
    // 初始化返回结果
    let result;
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }
​
    for (let key in obj) {
        // 保证 key 不是原型的属性,hasOwnProperty判断自身是否有指定的属性,因为for...in会遍历原型上的属性
        if (obj.hasOwnProperty(key)) {
            // 递归
            result[key] = deepClone(obj[key]);
        }
    }
​
    // 返回结果
    return result;
}

4. 三种发生类型转换的情况

  • 字符串拼接

  • ==运算符,==会尝试类型转换之后再判断相等

  • if判断语句,实际是执行两次非运算判断是否是turly变量

    以下是falsely变量(除此之外都是 truly 变量):

    !!0 === false
    !!NaN === false
    !!'' === false
    !!null === false
    !!undefined === false
    !!false === false
    

5. 如何判断一个变量是不是数组

  • a instanceof Array
  • a.__proto__ === Array.prototype
  • Array.isArray()

instanceof本质也是基于原型链

a instanceof B,本质是如果a沿着原型链能够找到B.prototype,那么就返回true,否则返回false。因此,以上第一二种方法如果修改了原型链,则判断出错。

手写instanceof

const instanceOf = (A, B) => {
    let p = A;
    while (p) {
        if (p === B.prototype) {
            return true;
        }
        p = p.__proto__;
    }
    return false;
}
​
console.log(instanceOf(1, Number)); // true
console.log(instanceOf([], Array)); // true
console.log(instanceOf([], Object)); // true

6. class的本质,如何理解

class是一种语法糖

  • class是ES6的新特性,用来定义一个类。
  • class有构造方法constructor、属性、方法
  • 子类可以通过extends继承父类,通过super调用父类构造方法

class本质也是通过原型来实现的

比如定义一个类Person,定义一个类Student继承自Person,通过Student创建一个实例zhangsan。

zhangsan.__proto__ === Student.prototypeStudent.prototype.__proto__ === Person.prototype

有关原型链,可以参考我的另一篇文章:JS原型与原型链

7. 作用域和自由变量

作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

自由变量

自由变量的值:现在本级作用域查找,如果未找到,向上级作用域依次查找,直到找到为止,如果到全局作用域都未找到,则报错 xxx is not defined

8. this 的不同应用场景,如何取值?

this的取值,是在函数执行的时候确定的,不是在函数定义的时候确定的。

  • 全局作用域或者普通函数中this指向全局对象window(注意定时器中的this指向window)
  • 对象调用方法时,指向调用方法的对象本身
  • 构造函数中this指向构造函数实例
  • 箭头函数的this指向它声明时作用域的this
  • 定时器中的this指向window(但如果定时器回调是使用箭头函数声明,则指向声明时作用域的this)
  • 事件绑定方法的回调,this指向绑定对象
  • 使用call、apply、bind改变this指向,传入什么,this就绑定什么

9. 手写bind函数

// 模拟 bind
Function.prototype.mybind = function () {
    // 将参数拆解为数组
    const args = Array.from(arguments);
​
    // 获取 this(第一个参数,即数组第一项)
    const t = args.shift();
​
    // 此时this是fn1.bind(...)中的 fn1 
    const self = this;
​
    // 返回一个函数
    return function() {
        return self.apply(t, args)
    }
}

10. 什么是闭包?闭包会用在哪里?

闭包

  • 作用域应用的特殊情况,有两种表现:

  • 函数作为参数被传递

    function print(fn) {
        let a = 200;
        fn();
    }
    let a = 100;
    function fn() {
        console.log(a);
    }
    print(fn); // 打印100
    
  • 函数作为返回值被返回

    function create() {
        let a = 100;
        return function() {
            console.log(a);
        }
    }
    let fn = create();
    let a = 200;
    fn(); // 打印100
    

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

实际开发中闭包的应用场景

  • 设置私有变量
  • 函数节流、防抖
  • 循环中拿到正确的值

写在最后

如果你觉得我写的还不错,可以给我点个赞哦,如果有写错的地方,也欢迎大家评论指出,谢谢。