• JavaScript 面试题总结


    JavaScript 面试题总结

    复习路线:https://ot7ehe3n3i.feishu.cn/mindnotes/bmncnCJmBALBBsRoMWcZ0KvXeme#mindmap

    var、let、const

    三者的区别

    区别letconstvar
    重复声明不能重复声明,会报SyntaxError错const 定义常量,值不能修改的变量叫做常量,一定要赋初始值,因为不能修改。可以重复声明
    变量提升不存在不存在存在
    块级作用域拥有拥有不拥有
    会不会污染全局变量(挂载在window上)不会不会
    补充如果常量是个数组或对象,对其内部元素修改,不算对常量的修改,不会报错。因为常量的值报错的是地址,地址并没有改变

    变量提升和函数提升

    1. 变量声明升级
      通过var定义(声明)的变量,在定义语句之前的就可以访问到
      但是值是undefined

    2. 函数声明提升
      通过function声明的函数,在之前就可以直接调用。
      值是函数体

    //变量提升先于函数提升,提升后被函数声明function覆盖,所以就算换了顺序也是function
    function a(){
    }
    var a ;
    console.log(typeof a); //function
    
    
    var f1 = function () {
        console.log(1);
    }
    function f1 () {
        console.log(2);
    }
    f1() ; //1
    //变量提升后
    var f1;//变量提升
    function f1(){};//函数提升
    f1 = function () {
        console.log(1);
    }
    f1() ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    变量提升练习题

    作用域和作用域链

    理解:一个代码段所在的区域,是静态的,在编写代码时就确定了
    作用:变量绑定在这个作用域内有效,隔离变量,不同作用域下同名变量不会有冲突。
    作用域分类

    1. 全局作用域
    2. 函数作用域
    3. 块级作用域

    作用域链:多个作用域嵌套,就近选择,先在自己作用域找,然后去就近的作用域找。

    函数的作用域在声明的时候就已经决定了,与调用位置无关
    所以执行aaa()的时候先在aaa的作用域里面找,没有找到a,再去父级作用域window里面找,找到a=10

    var a = 10;  
    function aaa() {
        alert(a);
    }
    function bbb() {
        var a = 20;
        aaa();
    }
    bbb();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行上下文

    对当前JavaScript的执行环境的抽象,每当JavaScript开始执行的时候,它都在执行上下文中运行。

    • 全局执行上下文:在执行全局代码前将window确定为全局执行上下文

    对全局数据进行预处理

    • var定义的全局变量 --> undefined,添加为window的属性

    • function声明的全局函数 --> 赋值(函数体),添加为window的方法

    • this --> 赋值window

    • 开始执行全局代码

    • 函数执行上下文:在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象

    对局部数据进行预处理

    • 形参变量 --> 赋值(实参)–> 添加到函数执行上下文的属性
    • arguments(形参列表封装成的伪数组)–>赋值(实参列表),添加到函数执行上下文的属性
    • var定义的局部变量–>undefined,添加为函数执行上下文的属性
    • function声明的函数–>赋值(函数体),添加为函数执行上下文的方法
    • this–>赋值(调用函数的对象)
    • 开始执行函数体代码

    执行上下文栈
    1.在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
    2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
    3.在函数执行上下文创建后,将其添加到栈中(压栈)
    4.在当前函数执行完成后,将栈顶的对象移除(出栈)
    5.当所有的代码执行完后,栈中只剩下window

    作用域执行上下文
    定义了几个函数 + 1 = 几个作用域执行了几个函数 + 1 = 几个执行上下文
    函数定义时就确定了,一直存在,不会再变化,是静态的全局执行上下文环境实在全局作用域确定之后,js代码执行之前创建的
    调用函数时创建,函数调用结束被释放,是动态的

    在这里插入图片描述

    var foo = 1;
    function bar () {
        console.log(foo);
        var foo = 10;
        console.log(foo);
    }
    
    bar();
    
    //变量提升后
    var foo = 1;
    function bar () {
    	var foo = undefined;
        console.log(foo); //undefined
        foo = 10;
        console.log(foo);//10
    }
    
    bar();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如何用ES5实现let和const

    let :使用立即执行函数创造出一个块级作用域

    (function(){
      var a = 1;
      console.log('内部a:', a);
    })();
    
    • 1
    • 2
    • 3
    • 4

    const
    1.使用立即执行函数创造出一个块级作用域。
    2.对于不可变性,可以利用Object.defineProperty 将变量挂载在对象上

    var __const = function __const(data, value) {
    	this.data = value // 把要定义的data挂载到某个对象,并赋值value
    	Object.defineProperty(this,data, { // 利用Object.defineProperty的能力劫持当前对象,并修改其属性描述符
    		enumerable: false,
    		configurable: false,
    		get: function () {
    			return value
    		},
    		set: function (data) {
    		if (data !== value) { // 当要对当前属性进行赋值时,则抛出错误!
    			throw new TypeError('Assignment to constant variable.')	
    		} else {
    			return value
    		}
    		}
    		})
    	}
    	//然后和立即执行函数结合
    	(function(){
    	 	var obj = {}
    		_const.call(obj,'a',10)
    	})()
    	//当执行完毕后,全局上就不会有obj,也不会有obj.a这个变量,进而实现了块级作用域的功能
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    代码输出题

    function a(){
      a.name ='aaa';
      return this.name;
    }
    var b = {
      a,
      name:'bbb',
      getName:function(){
        return this.name;
      }
    }
    var c =b.getName;
    console.log(a()); //this指向window,window上没有name,所以输出undefined
    console.log(b.a()); //b.a 是function,b调用a函数,所以this指向b,所以输出'bbb'
    console.log(b.getName);//通过b调用getName,所以getName指向b,所以输出'bbb'
    console.log(c());//c是function,this指向window,window上没有name,所以输出undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    数据类型

    笔记链接

    • JS数据类型有哪些
    • 介绍一下Symbol和Bigint
    • 如何判断一个数据类型
    • Object.prototype.toString.call() 的缺点?
    • 各个方法的原理是什么
    • typeof(NaN) typeof(Null)
    • 手写 instanceof 方法
    • null==undefined 和 null===undefined
    • 隐式转换规则 === 和 == 的区别
    • map和weakmap区别
    • map和object区别
    • Generator生成器函数
    • iterator迭代器
    • for in、for of 区别,分别对对象和数组使用问结果
    • 讲一下数组的遍历方法,filter与map的使用场景,some,every的区别
    • map的操作原理
    • map和forEach的区别
    • 使用迭代器实现for-of
    • 手写数组去重
    • 手写数组扁平化
    • map和filter的区别
    • 数组的常用方法
    • 用reduce实现map

    this的指向

    • 一般函数中this的指向会在调用时向函数传递执行上下文对象中设置。
      • 以函数形式调用,指向window
      • 以方法形式调用,this指向调用的方法
      • 以构造函数的形式调用,this是新创建的对象
    • 箭头函数:本身没有this,它的this可以沿作用域链(定义时就确定了的)查找

    bind、call、apply的区别与实现

    apply、call、bind 函数可以改变 this 的指向。

    区别callapplybind
    调用函数×
    参数从第二个参数开始依次传递封装成数组传递从第二个参数开始依次传递

    bind函数的特殊点
    多次绑定,只指向第一次绑定的obj对象。
    多次绑定,一次生效。
    原因:返回函数,后续bind修改的是返回函数的this

    call函数的实现

    //从第二个参数开始依次传入,所以接收时使用rest参数
    Function.prototype.call=function(obj,...args){
    	obj = obj || window;
    	args = args ? args : [];
    	//给obj新增一个独一无二的属性以免覆盖原有属性
        const key = Symbol()
    	obj[key] = this;
    	const res = obj[key](...args);
    	delete obj[key];
    	return res;	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    apply函数的实现

    Function.prototype.apply=function(obj,args){
    	obj = obj || window;
    	args = args ? args : [];
    	//给obj新增一个独一无二的属性以免覆盖原有属性
        const key = Symbol()
    	obj[key] = this;
    	const res = obj[key](...args);
    	delete obj[key];
    	return res;	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    bind函数的实现

    Function.prototype.bind(obj,...args)=function(){
    	obj = obj || window;
    	args = args ? args : [];
    	return (...args2) => {
    		return this.apply(obj,[...args,...args2])
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    一般函数和箭头函数

    箭头函数是普通函数的语法糖,书写要更加简洁

    区别一般函数箭头函数
    this指向调用时确定定义时确定,没有自己的this,沿着作用域链找父级的this
    改变this指向call,apply,bind不能改变,静态
    arguments没有,可以用rest参数代替
    作为构造函数× 没有prototype属性
    匿名函数可以匿名可以不匿名匿名函数

    闭包

    是什么
    闭包就是在函数中能够读取其他函数内部变量

    本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。
    正常情况下,代码执行完成之后,函数的执行上下文出栈被回收。但是如果当前函数执行上下文执行完成之后中的某个东西被执行上下文以外的东西占用,则当前函数执行上下文就不会出栈释放,也就是形成了不被销毁的上下文,闭包。

    有什么用
    1.可以读取函数内部的变量
    2.使函数的内部变量执行完后,仍然存活在栈内存中(延长了局部变量的生命周期)。

    JavaScript闭包就是在另一个作用域中保存了一份它从上一级函数或者作用域得到的变量,而这些变量是不会随上一级函数的执行完成而销毁

    常用场景:节流防抖
    缺点是什么
    1.函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
    2.容易造成内存泄露
    怎么解决
    及时释放:让内部函数成为垃圾对象(将闭包手动设置为null)–> 回收闭包

    原型和原型链

    笔记链接

    • 原型和原型链
    • 继承
  • 相关阅读:
    PVA接枝键合聚苯乙烯二乙烯基苯交联微球/聚苯乙烯微球表面接枝聚合丙烯酸酯的改性方法
    Qt应用开发(基础篇)——组合框容器 QGroupBox
    JavaSE - 封装、static成员和内部类
    springmvc入门
    python机器人编程——差速机器人小车的控制,控制模型、轨迹跟踪,轨迹规划、自动泊车(上)
    Scrum的三个内置子模式 | 图解敏捷系列
    java计算机毕业设计基于ssm的医院门诊互联电子病历管理信息系统(源代码+数据库+Lw文档)
    YUV图像基础知识
    asyncio.sleep
    vue基础知识十二:双向数据绑定是什么
  • 原文地址:https://blog.csdn.net/qq_41370833/article/details/126028743