JS面试题(1)

95 阅读5分钟

1. 值类型和引用类型

值类型会直接把值存到栈中,当将值赋给另一个变量时会开辟一个新的栈内存储新的变量的值。
引用类型的值存储在堆中,栈中存储的是堆的引用地址,当将值赋给另一个变量时会把栈中存储的引用地址复制一份赋给新变量,两者会相互影响。
常见的值类型:number、string、boolean、undefined、Symbol
常见的引用类型:Object、Array、null(指针指向空地址)、Function

2. === 和 == 的区别

=== 当等号两边的值为相同类型时才比较等号两边的值是否相等 == 当两边类型不同时会先进行类型转换,再比较两边的值是否相等

3. var、let、const的区别

let、const是ES6新增的语法,不存在变量提升,且是块级作用域。
var、let存储的为变量,const存储的为常量

4. 什么是闭包

闭包是指一个函数能够记住并访问它的词法作用域,即使这个函数在其词法作用域之外执行。其可实现数据封装和状态保持。实现方式如下:

function createCounter() {
    let count = 0; // 私有变量
    return function() {
        count++; // 访问外部函数的变量
        return count;
    };
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

但闭包会增加内存开销,因为外部函数的变量会被内部函数引用而不会被垃圾回收。

5. 如何判断js的数据类型

typeof:判断基本数据类型,即值类型。其直接在计算机底层基于数据类型的值(二进制)进行检测
instanceof:检测当前实例是否属于这个类,不能检测基本数据类型。基于原型链实现,但原型链可手动修改,所以有可能不准(例如手动把number的原型链指向数组)。
Object.prototype.toString.call():返回当前实例所属类的信息,返回字符串,格式为[object xxx]

6. 介绍下原型链

原型链是一种通过对象之间链接来实现继承的机制。每个对象都有一个内部属性Prototype,指向其原型,原型对象中的__proto__指向父级的原型,通过此关系一层一层往上,最终到null

7. new操作符具体干了什么

  1. 创建一个空对象
  2. 设置原型链,将新对象的__proto__属性设置为构造函数的prototype书香
  3. 绑定this,将构造函数内部的this绑定到新创建的对象上
  4. 执行构造函数
  5. 返回新对象
    代码逻辑如下:
function myNew(constructor, ...args) {
    const newObj = Object.create(constructor.prototype);
    const result = constructor.apply(newObj, args);
    if(result && (typeof result === 'object' || typeof result === 'function')) {
        return result;;
    }
    return newObj;
}

8. 介绍下this

  • 在全局执行环境中,指向全局对象
  • 在普通函数中,this的取值取决于函数的调用方式
  1. 简单调用指向全局对象
function foo() {
    console.log(this) // window
}
foo()
  1. 作为对象方法调用指向调用该函数的对象
const obj = {
    name: 'Alice',
    foo: function() {
        console.log(this.name) // Alice
    }
}
obj.foo()
  1. 构造函数调用,指向新创建的实例对象
function Person(name) {
    this.name = name;
}
const alice = new Person('Alice');
console.log(alice.name) // Alice
  • 箭头函数继承外层作用域的this
  • 可使用call、apply、bind显示改变this指向
  • 事件处理程序中通常指向触发事件的DOM元素

9. call、apply和bind

  • 相同点:都可以改变this指向
  • 不同点:用法不同
  1. call
    用法:
// thisArg:函数调用时使用的this值
// arg1, arg2:传递给函数的参数列表
function.call(thisArg, arg1, arg2, ...)

原理:

Function.prototype.myCall = function (context, ...args) {
    context = context || window;
    const fn = Symbol();
    const _this = this
    context[fn] = _this;
    const result = context[fn](...args);
    Reflect.deleteProperty(context, fn);
    return result;
}
  1. apply
    使用方法和call类似,但接收的参数为数组
    原理:
Function.prototype.myApply = function (context, args) {
    context = context || window;
    const fn = Symbol();
    const _this = this
    context[fn] = _this;
    const result = context[fn](...args);
    Reflect.deleteProperty(context, fn);
    return result;
}
  1. bind
    bind返回一个新函数,可以在以后调用
    用法:
const obj = { name: 'Alice' };
function greet(message) {
    console.log(`${message}, my name is ${this.name}`);
}
const boundGreet = greet.bind(obj, 'Hello');
boundGreet();

原理:

Function.prototype.myBind = function (context, ...args) {
    context = context || window;
    const fn = Symbol();
    const _this = this;
    context[fn] = this;
    return function F(...innerArgs) {
        if (this instanceof _this) {
            this[fn] = _this;
            this[fn](...[...args, ...innerArgs])
            Reflect.deleteProperty(this, fn);
        } else {
            context[fn](...[...args, ...innerArgs])
            Reflect.deleteProperty(context, fn);
        }
    }
}

10. 数组常用方法

  • 不改变原数组
  1. concat():合并两个或多个数组。返回一个新数组
  2. join():将数组的所有元素连接成一个字符串。返回一个字符串
  3. slice():从数组中提取一段子数组。返回一个新数组
  4. map():创建一个新数组,结果是调用提供的函数处理每个元素的结果。返回一个新数组
  5. filter():创建一个新数组,包含通过测试的所有元素。返回一个新数组
  6. reduce():对数组中的每个元素执行一个 reducer 函数,将其结果汇总为单个输出值。返回一个值
  7. every():检查数组中的所有元素是否都通过测试。返回一个布尔值
  8. some():检查数组中是否至少有一个元素通过测试。返回一个布尔值
  • 改变原数组
  1. push():在数组末尾添加一个或多个元素。返回新的数组长度
  2. pop():移除数组最后一个元素。返回被移除的元素
  3. shift():移除数组第一个元素。返回被移除的元素
  4. unshift():在数组开头添加一个或多个元素。返回新的数组长度
  5. splice():添加或删除数组中的元素。返回被删除的元素组成的数组
  6. reverse():反转数组中的元素顺序。返回反转后的数组
  7. sort():对数组元素进行排序,参数可接收函数。返回排序后的数组