在js引擎执行js代码时会首先进行一个预解析,预解析中会有声明的提升,这是一个重要的问题。
会提升var变量名和function的函数,在提升时有一些注意事项,var只提升函数名,function提升的是函数名和函数中存储的值。
1)var提升,不加var的变量是不会被提升的
2)函数提升时有一个例外,在新版的浏览器中如果函数在条件if中就只提升函数名不提升函数值
3)当拿到一段js代码时应该首先想到的是预解析,就是声明的提升
4)当有变量名和函数名都需要提升时,一般情况下不会注意到先提升哪一个
5)当函数名和变量名相同时应考虑先提升函数的声明,再提升变量的声明,后来者可能会覆盖前者的名字,可以看成不用提升
js的执行过程:当js引擎开始执行js代码的时候就产生了一个全局的执行上下文。
在全局的执行上下文中 :1)声明的提升,2)代码的执行
注意代码的执行时从上往下(因为现在的任务都是同步的,从上往下),遇到已经提升过的直接忽略,例如如果是变量名已经提升,就直接赋值,如果是函数直接忽略,因为它提升时是带着值一起提升的。
3)当代码执行过程中调用了一个函数。此时就产生了一个局部的执行上下文,1)形参的赋值,形参的赋值直接可以看做是在该函数内部声明的一个局部变量。2)声明的提升,这里注意提升var变量名,或者函数名和值,是提升在函数内部的最上面,3)代码的执行,从上到下依次执行。
var与不加var的区别:
在全局作用域下:加var与不加var都能看做是window对象的一个属性
在全局作用域下:加var会被提升,不加var不会被提升
在局部作用域下:不加var不会看作是window对象的属性
在局部作用域下:不加var只能是全局变量,不能是局部变量
加var的变量不能被删除,不加var的变量可以被删除
let声明变量的注意:
使用let声明的变量,不能被提升
不能作用于windows对象的属性
不能重新赋值,会报错
let{}会形成一个块级的作用域,块级的作用域能访问外面,外面不能访问块级作用域的数据。
const的注意:
使用const声明的变量,不能重新赋值,相当于声明的是一个常量了。
使用const不能直接 const加变量名,所以在声明的时候就要立刻赋值
ps:我们在做项目的时候,优先选择使用const,如果需要变量是动态的,再其次选择使用let,最后再是var。
作用域:
js中的作用域分为局部作用域和全局作用域,局部作用域:当声明了一个函数就产生了一个局部的作用域,在局部作用域以外的作用域是全局的作用域,作用域是静态的哦。它与局部执行上下文和全局执行上下文是不同的,上下文是执行时才有的。
作用域链:
js中的作用域是分为局部作用域和全局作用域,当声明了一个函数就产生了一个局部的作用域。
局部作用域是有上级作用域的,上级作用域看的是函数的声明。
当调用一个函数就产生了一个局部的执行上下文,局部上下文与局部的执行上下文之间是没有关系的。
一个局部上下文会做三件事,形参的赋值,声明的提升,代码的执行。当在一个局部的执行上下文中访问一个变量x,首先在自己的作用域里去找,如果找不到就去上一级去找,不只查找一级,如果找不到就一直查找到全局的执行上下文。
这一过程称为作用域链,也叫作用域的查找机制。
arguments是一个伪数组,也可以称为是一个伪类,在函数当中,它可以接收实参的数据,之所以称为伪数组,因为它具有数组得特性,可以通过arguments.length来获取数组的长度。
在该数组中,你可以理解为,它和形参一一对应,当形参改变了,它也跟着改变,反之也是如此。
假如形参有两个,它对应的是arguments的前两项。
如果要传进去的实参数量太多,可以用arguments来接收哦,示情况而定。
IIEF是立即执行函数表达式,指的是不需要手动去执行,在声明的时候它自己就会调用。之所以引入这一问题是保护函数的内部不受污染。立即执行函数表达式有三种方式。
有三种形式:
第一种形式:
;(function(){})()
第二种方式:
;(function(){}())
第三种方式:
+function(){}();
-function(){}();
!function(){}();
~function(){}();
堆内存的分配与释放:
当一个数据是引用数据类型,这个时候就会在内存中开辟一个堆空间来存放这个数据的值,这个对象需要一个变量来引用,如果没有变量来引用这个堆空间,那么浏览器就会在合适的时机去释放掉它。
全局栈内存的分配与释放:
当开始执行js代码就产生了一个全局栈内存。
当js代码执行完毕,关掉了选项卡就释放掉了这个全局的栈空间。
局部栈空间的分配与释放:
当代码执行中,调用了一个函数就产生了一个局部栈。局部栈具有保护作用。
释放:当函数执行完毕后,浏览器就会在合适的时机释放掉这个栈空间。
当函数内部调用自己,如果没有出口就会不停的开辟局部栈空间,最后爆栈,也叫死递归
闭包:当函数调用结束还有别的变量在引用这这块地址,这块栈空间就不会被释放,它里面的数据也会永久保存,这就产生了一个闭包,闭包的优点保护函数中的数据,延长变量的生命周期。缺点:泄露内存。
(1)let不能重复声明
(2)let和{}会产生一个块级的作用域
(3)let声明的变量名,不能被提升
(4)let声明的变量不属于window对象的属性
使用var和不使用var和let一起一些问题,使用let声明从还没执行到它就在内存中有了,但是不提升。所以有些问题还是要注意。
几种错误自己慢慢品:语法错误,引用错误,范围错误,类型错误。
ReferenceError引用错误有两种:第一种:Uncaught ReferenceError: a is not defined
console.log(a)
a=110; //内存中没有,输出一个不存在变量
ReferenceError引用第二种错误:Uncaught ReferenceError: Cannot access 'a' before initialization
console.log(a);
let a=110;//内存中已经有a这个变量了,但是let的声明不会被提升,输出的时候会提示的内存中已定义但是不能引用
SyntaxError语法错误示范一:Uncaught SyntaxError: Unexpected token ')'
if(a>=3)){
console.log(a)
} //普通语法错误
SyntaxError语法错误示范二:Uncaught SyntaxError: Identifier 'a' has already been declared
let a=110;
var a=123;
console.log(a) //let声明的变量不能重复声明
TypeError类型错误示范一:Uncaught TypeError: a is not a function
var a=110;
a();
TypeError类型错误示范二:Uncaught TypeError: a is not a function
var arr=[1,2,3,4]
arr()
RangeError范围错误示范一:Uncaught RangeError: Invalid array length
var arr=new Array(1000000000000000000000000000)
console.log(arr)//数组长度范围不能太大,超出了数组长度的范围
RangeError范围错误示范二:Uncaught RangeError: Invalid array length
var arr=new Array(-2);
console.log(arr)//数组长度范围不包括负,再说了长度怎么可以是负值呢,超出了数组长度的范围
(1)整个文件最开头加use strict
(2)函数内部最开头
(3)在es6中默认就处于严格模式
(1)消除js语法中,一些不合理,不严谨的地方,减少一些怪异的行为
(2)消除js代码的一些不安全之处,保证js代码运行安全
(3)严格模式可以比非严格模式下运行得更快。
(1)不允许遗漏声明,不带var
(2)不允许八进制:会报语法错误
(3)不允许把函数写在if中
(4)形参名不允许重复(声明同名的形参)
(5)不允许给对象声明相同的属性
(6)arguments和形参没有以一对应关系
(6)function中的this不再指向window
逻辑与:如果第一个的值为真返回第一个操作数,如果第一个数为假,返回第二个操作数(都是转换之前的数)
逻辑与总结:如果第一个数为假则返回第一个操作数,如果第一个数为真,则返回第二个操作数(都是转换之前的)
0 || false //false
false || 0 //0
1 || false//1
1 || true //1
"" || fals//false
1 || false//1
"" || true//true
true || ""true
true || 4 //true
"" && 123//""
123 && 0//0
123 && NaN//NaN
-0 && NaN//-0
程序调用自己的编程技巧叫做递归。
把大型的问题层层简化,得到规模小的问题,然后在解决小的问题,再层层解决大的问题
递归函数:在一个函数内部,他自己又调用了自己,需要一个出口
function f(){
f();//记得需要出口
}
利用递归思想,求100的累加
利用递归思想,求斐波那契数列中的第20项
利用递归思想,求数组中元素的和
利用递归思想,求1,3,5,7,9,... 的第N项的值。索引从0开始。
利用递归思想,求1,3,5,7,9,... 前N项的和
利用递归思想,求0,2,4,6,8,... 的第N项的值
利用递归思想,求0,2,4,6,8,... 前N项的和