当前位置: 首页 > Web前端 > HTML

《面试题》JS高频面试题汇总02《每周更新》

时间:2023-04-02 22:18:27 HTML

本周面试题列表:JS数据类型和存储方式四种常用JS数据类型判断方法的区别作用域、作用域链、ExecutionContext(EC)、ExecutionContextStack(ECS)原型和原型链说说你对闭包的理解6、JS数据类型和存储方式的区别在ECMAScript规范中,一共定义了8种数据类型。分为两大类:基本数据类型和引用数据类型(引用数据类型也叫复杂数据类型):基本数据类型:String、Number、Boolean、Null、Undefined、Symbol(ES6新增)、BigInt(新增)inES10)引用数据类型:Object(Function、Array、Date、RegExp、Math...都是Object类型的实例对象)存储方式区别:基本数据类型:变量名和值存储在栈内存中;引用数据类型:变量名称存放在栈内存中,值存放在堆内存中。堆内存中提供了一个引用地址指向堆内存中的值,这个引用地址存放在栈内存中。7、JS数据类型常用的四种判断方法Typeof的缺点:不能判断Object类型,也不能判断Null类型。typenull返回对象,因为JavaScript使用32位存储值,类型由值的低1或3位标识。object的前三位代表000,null是机器码空指针,32位表示全0,前三位相同,所以typeofnull也会打印出object。console.log(typeof"")//stringconsole.log(typeof1)//numberconsole.log(typeoftrue)//booleanconsole.log(typeofSymbol())//symbolconsole.log(typeofundefined)//undefinedconsole。log(typeofnull)//objectconsole.log(typeof[])//objectconsole.log(typeof{})//objectconstructor构造函数可以找出是谁构造了这个变量。缺点:无法判断null和undefined,因为null和undefined是无效对象,所以不会有构造函数。这两类数据需要通过其他方法来判断。函数的构造函数不稳定,主要体现在自定义对象上。当开发者重写原型时,原来的构造函数引用会丢失,构造函数会默认为Objectconsole.log(("").constructor==String)//trueconsole.log((1).constructor==Number)//trueconsole.log((true).constructor==Boolean);//trueconsole.log([].constructor==Array)//trueconsole。log(({}).constructor==Object)//trueinstanceofinstanceof用于判断A是否是B的实例,这里要特别注意:instanceof检测原型。缺点:instanceof只能用来判断两个对象是否属于一个实例关系,而不能判断一个对象实例属于哪种类型。console.log([]instanceofArray)//trueconsole.log({}instanceofObject)//trueconsole.log([]instanceofObject)//trueconsole.log(newDate()instanceofDate)//trueObject.prototype。toString.call()使用.call将传入的对象作为Object原型上的this,然后通过toString方法可以看到具体的类型。优缺点:不能细分谁是谁的实例,但是判断这个变量的类型非常方便可靠。console.log(Object.prototype.toString.call())//[对象未定义]console.log(Object.prototype.toString.call(""))//[对象字符串]console.log(Object.prototype.toString.call(1))//[objectNumber]console.log(Object.prototype.toString.call(true))//[objectBoolean]console.log(Object.prototype.toString.call(Symbol()))//[objectSymbol]console.log(Object.prototype.toString.call(null))//[objectNull]console.log(Object.prototype.toString.call(newFunction()))//[objectFunction]console.log(Object.prototype.toString.call(newDate()))//[objectDate]console.log(Object.prototype.toString.call([]))//[objectArray]console.log(Object.prototype.toString.call(newRegExp()))//[对象RegExp]console.log(Object.prototype.toString.call(newError()))//[对象错误]console.log(Object.prototype.toString.call(document))//[objectHTMLDocument]console.log(Object.prototype.toString.call(window))//[objectWindow]8.作用域、作用域链、执行上下文(EC)、以执行上下游(ECS)使用域函数数在定定义时有两种作用域:全局作用域和局部作用域,所以JS的作用域是静态作用域执行上下文(EC)。当JS执行一段代码时,会生成执行上下文(EC);“执行上下文”分为三种类型:全局执行上下文、函数执行上下文和eval执行上下文。“执行上下文”由三部分组成:变量对象(VO)、作用域链(词法作用域)、thispoint。活动对象(AO):当变量对象的上下文为活动EC时,称为活动对象。执行上下文栈(ECS)在形成“执行上下文”的过程中,会使用“执行上下文栈”来管理执行上下文。代码执行的过程:创建一个全局上下文(globalEC),从上到下逐行执行一个全局执行上下文(caller)。当遇到一个函数时,函数执行上下文(被调用者)被推到执行栈的顶部。函数执行上下文被激活,成为activeEC,函数中的代码开始执行。调用者挂起后,被调用者通过popStack退出执行,将控制权交还给全局上下文(调用者),继续执行示例:functiona(){b();}functionb(){c();}functionc(){console.log("welcome");}/**ECS=[globalContext]ECS.push(functionAContext);ECS.push(functionBContext);ECS.push(functionCContext);ECS.pop();ECS.pop();ECS.pop();**/作用域链函数内部会保留一个[[scope]]属性,会保存所有父变量对象。并且函数在执行的时候,会添加自己的AO对象,所以在执行的时候会先找自己的AO属性,如果找不到就往上找。这就是作用域链。“作用域链”由两部分组成:[[scope]]属性:指向父变量对象和作用域链,即包括父的[[scope]]和AO。AO:活动对象本身[[scope]]包含[[scope]],从上到下形成一个“作用域链”。示例:vara=1;functionsum(){varb=2;返回a+b;}sum();/*sum.[[scope]]={globalContext.VO}//编译阶段:sumContext={A0:{arguments:{length:0},b:undefined},Scope:[A0,sum.[[scope]]]}//执行阶段:ESC=[globalContext,sumContext]A0:{arguments:{length:0},b:2},//执行后:ECS.pop()*/9。原型和原型链原型:prototype,每个函数都有一个prototype属性;原型链:__proto__,每个对象都有一个__proto__属性;构造函数:可以通过new创建一个新对象的函数。实例:构造函数和new创建的对象是一个实例。实例通过__proto__指向原型,通过构造函数指向构造函数。示例:functionAnimal(){this.type="Mammal"}Animal.prototype.type="Lactation"//instanceletanimal=newAnimal();console.log(animal.__proto__.__proto__===Object.prototype);//trueconsole.log(Animal.prototype.constructor==Animal);//trueconsole.log(Object.prototype.__proto__);//null这个可能比较抽象,我画了一张图帮助大家彻底理解它们之间的关系:特殊情况:Function可以作为一个对象或者函数console.log(Function.__proto__===Function.prototype);console.log(Object.__proto__===Function.prototype);console.log(Object.__proto__===Function.__proto__);10.谈谈你对闭包的理解闭包属于一个特殊的范围。它的定义可以理解为:当父函数被销毁时,返回的子函数的[[scope]]仍然保留着父函数的单个变量对象和作用域链,因此可以继续访问父函数的变量对象,这样的函数称为闭包。一句话解释就是:函数定义的作用域和函数执行的作用域不在同一个作用域。闭包会造成一个很经典的问题:多个子函数的[[scope]]同时指向parent,完全共享。所以当父变量对象被修改时,所有的子函数都会受到影响。解决方案:可以以函数参数的形式引入变量,避免使用默认的[[scope]]查找和使用setTimeout包,传入第三个参数并使用块级作用域,让变量成为其属性自己的上下文,避免共享手动编写一个简单的闭包以下示例中的闭包是一个闭包:functionfunc(){vara=1,b=2;函数闭包(){返回a+b;}returnclosure;}关于作者奇小神,前端程序元。有点文艺,喜欢摄影。虽然我现在朝九晚五,努力学习,但我的梦想是当女侠,扶贫济困,剑走天下。希望有一天我能修复BUG,实现我的梦想。公众号:前端大空间,不定时更新,欢迎来玩~