先编译,再执行。 浏览器
栈溢出是如何产生的?
当调用一个函数时,会给他建立一个执行上下文 push 到栈中,执行完毕从栈中 pop。若函数内部又调用了其余函数,内部又调用其余函数...,不断将执行上下文往栈中 push 却没有 pop,超过必定数量就会致使栈溢出报错。闭包
没有终止条件的递归会一直建立新函数的执行上下文压入栈中,超过栈容量的最大先以后就会报错;app
能够经过把递归改形成其余形式、加入定时器拆分任务等方法来解决。函数
调用栈是 JavaScript 引擎追踪函数执行的一个机制,当一次有多个函数被调用时,经过调用栈就能追踪到哪一个函数正在被执行和各个函数间的调用关系。工具
chat* myname = "geek time";
void showName() {
printf("%s \n", myname); // 'geek time'
if(0){
chat* myname = "Hei ha";
}
}
int main(){
showName();
return 0;
}
复制代码
最终打印为 'geek time'ui
var myname = "geek time";
function showName() {
console.log(myname);
if (0) {
var myname = "Hei ha";
}
}
showName();
复制代码
最终打印为 undefinedthis
function foo() {
for (var i = 0; i < 7; i++) {}
console.log(i);
}
foo(); //7
复制代码
输出为 7,变量 i 在 foo 循环结束后并无被销毁,说明在建立执行上下文阶段,变量 i 就已经被提高了。spa
在其余语言中,for,if,while,{},函数块等内部变量执行完后就会被销毁。设计
经过 var 声明的变量,在编译阶段被放进变量环境
,而经过 let,const 声明的被放进词法环境(Lexical Environment)
;
3d
function bar() {
console.log(myName);
}
function foo() {
var myName = "极客邦";
bar();
}
var myName = "极客时间";
foo();
复制代码
极客邦
; 实际答案是
极客时间
当一段代码使用一个变量时,JS 引擎首先在“当前执行上下文(bar)”中查找该变量,若没有,则在 outer 所指向的执行上下文中查找,这个查找链条就是做用域链
。
由于在 JS 执行过程当中,做用域链
是由词法做用域
决定的。
词法做用域是由代码中函数声明的位置来决定的,因此词法做用域是静态做用域,经过它能预测代码在执行过程当中如何查找标识符。
词法做用域是代码阶段就决定好的,和函数怎么调用没有关系。 再看上面的问题,就知道打印的结果为何是“极客时间”了。 若是换成下面的:
function foo() {
var myName = "极客邦";
function bar() {
console.log(myName);
}
return bar();
}
var myName = "极客时间";
foo();
复制代码
此时打印的就是“极客邦”了。
function bar() {
var myName = "浏览器";
let test1 = 100;
if (1) {
let myName = "Chrome 浏览器";
console.log(test);
}
}
function foo() {
var myName = "极客邦";
let test = 2;
{
let test = 3;
bar();
}
}
var myName = "极客时间";
let myAge = 10;
let test = 1;
foo();
复制代码
结合上面的做用域链与词法做用域,易得最终输出结果为 1。 查找顺序以下(图中标记的 1,2,3,4,5)
function foo() {
var myName = "极客时间";
let test1 = 1;
const test2 = 2;
var innerBar = {
getName() {
console.log(test1);
return myName;
},
setName(newName) {
myName = newName;
}
};
return innerBar;
}
var bar = foo();
bar.setName("极客邦");
bar.getName();
console.log(bar.getName());
复制代码
getName
与
setName
能够访问 foo 中的 myName 和 test1。因此,当 foo 执行完后,这两个变量成为 foo 闭包的专属变量,除了 setName 和 getName 其余任何地方都没法访问 foo 闭包中的变量。调用栈的状态以下:
经过上图能够看出,当执行到 foo 时,闭包就产生了,foo 结束后,getName 与 setName 都引用了clourse(foo)
对象,因此即便 foo 函数结束了,clourse(foo)
依然被其内部的 getName 和 setName 引用,调用这两个方法时,建立的执行上下文就包含了 clourse(foo)
myName
,因而生成一个闭包环境来存放 myName 变量。当执行 bar.setName()
方法中的 myName = 'xxx' 时,JS 引擎会沿着“当前执行上下文 -> foo 函数闭包 -> 全局执行上下文”的属性来查找,以下:
Scope
便可查看做用域链的状况。
若是引用闭包的函数是全局变量,那么闭包会一直存在到页面关闭;但若是这个闭包之后再也不使用的话,就会形成内存泄漏。
若是引用闭包的函数是局部变量,等函数销毁后,下次 JS 引擎执行垃圾回收时,判断闭包若是已再也不被使用,就会回收这块内存。
综上所述,若闭包一直使用,则做为全局变量,不然为局部变量。
this 是和执行上下文绑定的,执行上下文有全局、函数、eval 执行上下文,故对应的 this 也有这三种。
window
let bar = {
myName: 'x'
}
function foo() {
this.myName = 'xxx'
}
foo.call(bar)
复制代码
var myObj = {
name: 'x',
showThis() {
console.log(this)
}
}
myObj.showThis() // 等同于 myObj.showThis.call(myObj)
var foo = myObj.shiwThis
foo() // window
复制代码
在全局环境中调用一个函数,函数内部的 this 指向的是全局变量 window。 经过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象自己。
3. 经过构造函数中设置 new 运算符
var myObj = {
name: 'jk',
showThis() {
console.log(this) // myObj
function bar() {
console.log(this)
}
bar() // window
}
}
复制代码
解决办法:1. 外层绑定 this 2. 箭头函数
2. 普通函数中的 this 默认指向全局对象 window 严格模式下,默认执行一个函数,这个函数执行上下文中的 this 是 undefined
showName();
var showName = function() {
console.log(2);
};
function showName() {
console.log(1);
}
复制代码
输出 1,第一个 showName 带 var 通过变量提高后被赋值为 undefined,变量 showName 会被下面同名函数覆盖,再次执行 showName 就为 2,具体过程以下
// 编译
var showName = undefined;
function showName() {
console.log(1);
}
// 执行
showName(); // 1
showName = function() {
console.log(2);
};
showName(); // 2
复制代码
let myname = "geek time";
{
console.log(myname);
let myname = "Hei ha";
}
复制代码
最终的打印结果不是 undefined.
而是:Cannot access 'myname' before initialization 缘由:在块级做用域内,let 变量只是建立被提高,初始化并无被提高,在初始化以前使用变量,会造成一个暂时性死区。