㈠ Web前端工程师这些JavaScript的问题你会吗
今天小编要跟大家分享的文章是关于Web前端工程师这些JavaScript的问题你会吗。JavaScript是一种有趣的语言,我们都喜欢它,因为它的性质。浏览器是JavaScript的主要运行的地方,两者在我们的服务中协同工作。JS有一些概念,人们往往会对它掉以轻心,有时可能会忽略不计。原型、闭包和事件循环等概念仍然是大多数JS开发人员绕道而行的晦涩领域之一。正如我们所知,无知是一件危险的事情,它可能会导致错误。
接下来,来看看几个问题,你也可以试试想想,然后作答。
问题1:浏览器控制台上会打印什么?vara=10;
functionfoo(){
console.log(a);//??
vara=20;
}
foo();
问题2:如果我们使用let或const代替var,输出是否相同vara=10;
functionfoo(){
console.log(a);//??
leta=20;
}
foo();
问题3:"newArray"中有哪些元素?vararray=[];
for(vari=0;i<3;i++){
array.push(()=>i);
}
varnewArray=array.map(el=>el());
console.log(newArray);//??
问题4:如果我们在浏览器控制台中运行'foo'函数,是否会导致堆栈溢出错误?functionfoo(){
setTimeout(foo,0);//是否存在堆栈溢出错误?
};
问题5:如果在控制台中运行以下函数,页面(选项卡)的UI是否仍然响应functionfoo(){
returnPromise.resolve().then(foo);
};
问题6:我们能否以某种方式为下面的语句使用展开运算而不导致类型错误varobj={x:1,y:2,z:3};
[...obj];//TypeError
问题7:运行以下代码片段时,控制台上会打印什么?
varobj={a:1,b:2};
Object.setPrototypeOf(obj,{c:3});
Object.defineProperty(obj,'d',{value:4,enumerable:false});
//-inloop?
for(letpropinobj){
console.log(prop);
}
问题8:xGetter()会打印什么值?
varx=10;
varfoo={
x:90,
getX:function(){
returnthis.x;
}
};
foo.getX();//prints90
varxGetter=foo.getX;
xGetter();//prints??
答案
现在,让我们从头到尾回答每个问题。我将给您一个简短的解释,同时试图揭开这些行为的神秘面纱,并提供一些参考资料。
问题1:undefined
解析:
使用var关键字声明的变量在JavaScript中会被提升,并在内存中分配值undefined。但初始化恰发生在你给变量赋值的地方。另外,var声明的变量是函数作用域的,而let和const是块作用域的。所以,这就是这个过程的样子:
vara=10;//全局使用域
functionfoo(){
//vara的声明将被提升到到函数的顶部。
//比如:vara
console.log(a);//打印undefined
//实际初始化值20只发生在这里
vara=20;//localscope
}
问题2:ReferenceError:aundefined
解析:
let和const声明可以让变量在其作用域上受限于它所使用的块、语句或表达式。与var不同的是,这些变量没有被提升,并且有一个所谓的暂时死区(TDZ)。试图访问TDZ中的这些变量将引发ReferenceError,因为只有在执行到达声明时才能访问它们。
vara=10;//全局使用域
functionfoo(){//TDZ开始
//创建了未初始化的'a'
console.log(a);//ReferenceError
//TDZ结束,'a'仅在此处初始化,值为20
leta=20;
}
下表概述了与JavaScript中使用的不同关键字声明的变量对应的提升行为和使用域:
问题3:[3,3,3]
解析:
在for循环的头部声明带有var关键字的变量会为该变量创建单个绑定(存储空间)。阅读更多关于闭包的信息。让我们再看一次for循环。
//误解作用域:认为存在块级作用域
vararray=[];
for(vari=0;i<3;i++){
//三个箭头函数体中的每个`'i'`都指向相同的绑定,
//这就是为什么它们在循环结束时返回相同的值Ɖ'。
array.push(()=>i);
}
varnewArray=array.map(el=>el());
console.log(newArray);//[3,3,3]
如果使用let声明一个具有块级作用域的变量,则为每个循环迭代创建一个新的绑定。
//使用ES6块级作用域
vararray=[];
for(leti=0;i<3;i++){
//这一次,每个'i'指的是一个新的的绑定,并保留当前的值。
//因此,每个箭头函数返回一个不同的值。
array.push(()=>i);
}
varnewArray=array.map(el=>el());
console.log(newArray);//[0,1,2]
解决这个问题的另一种方法是使用闭包。
letarray=[];
for(vari=0;i<3;i++){
array[i]=(function(x){
returnfunction(){
returnx;
};
})(i);
}
constnewArray=array.map(el=>el());
console.log(newArray);//[0,1,2]
问题4:不会溢出
解析:
JavaScript并发模型基于“事件循环”。当我们说“浏览器是JS的家”时我真正的意思是浏览器提供运行时环境来执行我们的JS代码。
浏览器的主要组件包括调用堆栈,事件循环,任务队列和Web
API。像setTimeout,setInterval和Promise这样的全局函数不是JavaScript的一部分,而是Web
API的一部分。JavaScript环境的可视化形式如下所示:
JS调用栈是后进先出(LIFO)的。引擎每次从堆栈中取出一个函数,然后从上到下依次运行代码。每当它遇到一些异步代码,如setTimeout,它就把它交给Web
API(箭头1)。因此,每当事件被触发时,callback都会被发送到任务队列(箭头2)。
事件循环(Eventloop)不断地监视任务队列(TaskQueue),并按它们排队的顺序一次处理一个回调。每当调用堆栈(call
stack)为空时,Eventloop获取回调并将其放入堆栈(stack
)(箭头3)中进行处理。请记住,如果调用堆栈不是空的,则事件循环不会将任何回调推入堆栈。
现在,有了这些知识,让我们来回答前面提到的问题:
步骤
1.调用foo()会将foo函数放入调用堆栈(callstack)。
2.在处理内部代码时,JS引擎遇到setTimeout。
3.然后将foo回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空
4.计时器被设置为0,因此foo将被发送到任务队列
5.由于调用堆栈是空的,事件循环将选择foo回调并将其推入调用堆栈进行处理。
6.进程再次重复,堆栈不会溢出。
问题5:不会响应
解析:
大多数时候,开发人员假设在事件循环
在底层来看,JavaScript中有宏任务和微任务。setTimeout回调是宏任务,而Promise回调是微任务。
主要的区别在于他们的执行方式。宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行后返回到事件循环之前清空。因此,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。只有当微任务队列为空时,事件循环才会重新渲染页面、
现在,当你在控制台中运行以下代码段
functionfoo(){
returnPromise.resolve().then(foo);
};
每次调用'foo'都会继续在微任务队列上添加另一个'foo'回调,因此事件循环无法继续处理其他事件(滚动,单击等),直到该队列完全清空为止。因此,它会阻止渲染。
问题6:会导致TypeError错误
解析:
展开语法和for-of语句遍历iterable对象定义要遍历的数据。Array或Map
是具有默认迭代行为的内置迭代器。对象不是可迭代的,但是可以通过使用iterable和iterator协议使它们可迭代。
在Mozilla文档中,如果一个对象实现了@@iterator方法,那么它就是可迭代的,这意味着这个对象(或者它原型链上的一个对象)必须有一个带有@@iterator键的属性,这个键可以通过常量Symbol.iterator获得。
上述语句可能看起来有点冗长,但是下面的示例将更有意义:varobj={x:1,y:2,z:3};
obj[Symbol.iterator]=function(){
//iterator是一个具有next方法的对象,
//它的返回至少有一个对象
//两个属性:value&done。
//返回一个iterator对象
return{
ext:function(){
if(this._countDown===3){
constlastValue=this._countDown;
return{value:this._countDown,done:true};
}
this._countDown=this._countDown+1;
return{value:this._countDown,done:false};
},
_countDown:0
};
};
[...obj];//打印[1,2,3]
还可以使用generator函数来定制对象的迭代行为:
varobj={x:1,y:2,z:3}
obj[Symbol.iterator]=function*(){
yield1;
yield2;
yield3;
}
[...obj];//打印[1,2,3]
问题7:a,b,c
解析:
for-in循环遍历对象本身的可枚举属性以及对象从其原型继承的属性。可枚举属性是可以在for-in循环期间包含和访问的属性。varobj={a:1,b:2};
vardescriptor=Object.getOwnPropertyDescriptor(obj,"a");
console.log(descriptor.enumerable);//true
console.log(descriptor);
//{value:1,writable:true,enumerable:true,configurable:true}
现在你已经掌握了这些知识,应该很容易理解为什么我们的代码要打印这些特定的属性
varobj={a:1,b:2};//a,b都是enumerables属性
//将{c:3}设置为'obj'的原型,并且我们知道
//for-in循环也迭代obj继承的属性
//从它的原型,'c'也可以被访问。
Object.setPrototypeOf(obj,{c:3});
//我们在'obj'中定义了另外一个属性'd',但是
//将'enumerable'设置为false。这意味着'd'将被忽略。
Object.defineProperty(obj,"d",{value:4,enumerable:false});
for(letpropinobj){
console.log(prop);
}
//打印
//a
//b
//c
问题8:10
解析:
在全局范围内初始化x时,它成为window对象的属性(不是严格的模式)。看看下面的代码:varx=10;//globalscope
varfoo={
x:90,
getX:function(){
returnthis.x;
}
};
foo.getX();//prints90
letxGetter=foo.getX;
xGetter();//prints10
咱们可以断言:
window.x===10;//true
this
始终指向调用方法的对象。因此,在foo.getx()的例子中,它指向foo对象,返回90的值。而在xGetter()的情况下,this指向window对象,返回
window中的x的值,即10。
要获取foo.x的值,可以通过使用Function.prototype.bind将this的值绑定到foo对象来创建新函数。
letgetFooX=foo.getX.bind(foo);
getFooX();//90
就这样!如果你的所有答案都正确,那么干得漂亮。咱们都是通过犯错来学习的。这一切都是为了了解背后的“原因”。
以上就是小编今天为大家分享的一些关于JavaScript的相关问题,想要了解更多Web相关知识记得关注北大青鸟Web培训官网。最后祝愿小伙伴们工作顺利,成为一名优秀的Web前端工程师!
英文:AmandeepSingh,译文:前端小智
来源:#/post/5d2d146bf265da1b9163c5c9
㈡ win10打开网页就提示堆栈溢出怎么解决
电脑中无法打开网页并提示堆栈溢出的原因分析及解决方法 1、函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈; 2、局部静态变量体积太大。 解决方法: 1、按win+r打开运行,输入cmd,并按回车; 2、打开命令提示符后,分别输入并执行以下命令: regsvr32 atl.dll regsvr32 shdocvw.dll regsvr32 urlmon.dll regsvr32 browseui.dll regsvr32 oleaut32.dll regsvr32 shell32.dll regsvr32 jscript.dll regsvr32 vbscript.dll regsvr32 mshtmled.dll regsvr32 CLBCATQ.DLL regsvr32 cscui.dll regsvr32 MLANG.dll regsvr32 stobject.dll regsvr32 WINHTTP.dll regsvr32 msxml3.dll regsvr32 query.dll regsvr32 jscript9.dll regsvr32 hlink.dll
㈢ 在web应用系统开发框架中,什么是全堆栈框架
这种概念全然不用去理会它,完全噱头而言,一个前端的开发能跟堆栈扯上个毛线关系,
堆栈的概念与应用都是涉及到内存的操作,特别是汇编中尤为重要,与前端没一毛钱关系,前端的框架99%用的都是JS,封装一下就成了不同版本的了,顶多顶多用了下,后进先出,先进先出这些堆栈的概念而已,就妄称而已
㈣ 执行当前 Web 请求期间生成了未处理的异常.可以使用下面的异常堆栈跟踪信息确定
找你代码中的调用
String.SubString
方法的地方
=============================
System.String.Substring(Int32
startIndex)
+17
=============================
一般来说,你在用这个方法截取子字符串时,所指定的
开始位置或结束位置已经超出或低于(startIndex
=
-1)它的长度。
㈤ c# 来自网页的提示:堆栈溢出 webbrowser
部分代码如下:
在窗口及WebBrowser创建完以后,写此代码:
WebBrowser.OnNewWindow2:=IE1NewWindow2;
给主窗口创建一个方法:
procere TBrowForm.IE1NewWindow2(Sender: TObject; var ppDisp: IDispatch;var Cancel: WordBool);
Var NewForm:TBrowForm;
begin
try
NewForm :=TPopBrowerForm.Create(application);
NewForm.WebBrowser.OnNewWindow2 :=IE1NewWindow2;//如果在窗口的Oncreate事件
//有此代码,此处就可以省略
ppDisp := NewForm.WebBrowser.Application;
NewForm.Show;
Cancel:=false;
except
end;
end;
㈥ vs2008 打开窗体时显示调用堆栈 这是什么原因啊
窗体里面某个控件在设计视图中加载的时候出错了 根据提示信息排查一下
㈦ WebDev.WebServer40.exe 停止工作.WebDev.WebServer40.exe 中发生未处理的Microsoft .NETFramework异常,
这个问题,它说明你的站点里面存有致命错误。经过断点调试,发现了如下问题:
反思:虽然这是个马虎引起的问题,但也确实说明一个该注意的问题。就是运用分层编程时,同一条线上各层之间的方法名称最好别完全相同,应该加一些前缀或后缀标识会更加安全和规范。