复习
for与var
for (var i = 0; i < 10; i++) {
i = 'a';
console.log(i);//a
}
等价于
var i = 0
for (; i < 10; ) {
i = 'a';
console.log(i);//a
i++;
}
for (var i = 0; i < 10; i++) {
var i = 'a';
console.log(i);//a
}
也可以变成如下代码,for表达式中的var i与for()中的var i都被提升到了全局
var i = 0
for (; i < 10; ) {
i = 'a';
console.log(i);//a
i++;
}
for与let
for (let i = 0; i < 10; i++) {
/*调用i并给i赋值为'a',调用的i就是let i=0产生的,是一个i*/
i = 'a';
console.log(i);
}
for (let i = 0; i < 10; i++) {
var i = 'a';
//SyntaxError: Identifier 'i' has already been declared
console.log(i);
}
相当于,两个块级作用域
{
let i;
{
var i;
}
}
变量提升,可以变形成
{
var i;
let i;
{
}
}
for (let i = 0; i < 10; i++) {
let i = 'a';
console.log(i);//10个a
}
相当于
{
let i = 0;
{
let i = 'a';
console.log(i);//a
}
console.log(i);//0
}
类似于上面这段代码运行10次,写10次
因为let不允许在同一个块级作用域重复声明的,会报错
想让它重新let一个i,只能为它开辟一个新的块级作用域,在里面再声明i
转义后的es5代码
{
var i = 0;
{
var _i = 'a';
console.log(_i); //a
}
console.log(i); //0
}
for与数组1
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
for (var i = 0; i < 10; i++) {
arr[i]();//0-9
/*i把之前的i覆盖了,因为都是i*/
}
相当于
var arr = [];
var i = 0
for (; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
var i = 0
for (; i < 10; i++) {
arr[i]();//0-9
/*i把之前的i覆盖了,因为都是i*/
}
for与数组2
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
for (var k = 0; k < 10; k++) {
arr[k]();//10个10
}
见立即执行函数知识点
没有运行func声明语句拿着i
for与数组3:let
var arr = [];
/*把var改成let就变成了0-9,之前是10个10,如上面代码*/
for (let i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
for (var k = 0; k < 10; k++) {
arr[k]();//0-9
}
转成es5代码
var arr = [];
/*把var改成let就变成了0-9,之前是10个10,如上面代码*/
var _loop = function _loop(i) {
arr[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
for (var k = 0; k < 10; k++) {
arr[k](); //0-9
}
存在全局的arr中,这是一个闭包
arr在全局中定义,通过_loop方法赋值,这形成了闭包,执行的_loop函数名次执行的AO中的i,被数组函数引用、调用,不会被销毁。
_loop每次运行的AO,存在arr数组方法声明产生的scope中,被全局的arr调用,不会被销毁。每次_loop运行时,AO中i的值都是不一样的,是新的AO。
方法声明里面的scope,有_loop每次运行的AO
var arr = [];
/*把var改成let就变成了0-9,之前是10个10,如上面代码*/
for (let i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
/*把k改成i也是0-9,因为arr[i]方法中调用的是闭包AO中i,不是for中的i*/
for (var i = 0; i < 10; i++) {
arr[i]();//0-9
}
for与数组4:let
现在再来看这道题
var arr = [];
/*把var改成let就变成了0-9,之前是10个10,如上面代码*/
for (let i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}
for (var k = 0; k < 10; k++) {
arr[k]();//0-9
}
不能把let提取到外面如下,结果不一样了
var arr = [];
/*把var改成let就变成了0-9,之前是10个10,如上面代码*/
let i = 0;
for (; i < 10; ) {
arr[i] = function () {
console.log(i)
}
i=i+1;
}
for (var k = 0; k < 10; k++) {
arr[k]();//10个10
}
等价于,不算循环
{
let i=0;
/**/
{
arr[i] = function() {
console.log(i)
}
}
}
类似于
var arr = [];
/*把var改成let就变成了0-9,之前是10个10,如上面代码*/
/*for (let i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i)
}
}*/
for (; 1;) {
{
let i = 0;
{
arr[i] = function () {
console.log(i)
}
}
}
{
let i = 1;
arr[i] = function () {
console.log(i)
}
}
{
let i = 2;
arr[i] = function () {
console.log(i)
}
}
break;
}
/*把k改成i也是0-9,因为arr[i]方法中调用的是闭包AO中i,不是for中的i*/
for (var i = 0; i < 3; i++) {
arr[i]();//0,1,2
}
let声明,相当于开了块级作用域再运行代码
for循环10次,执行了10次let声明语句,相当于开了10个块级作用域,每个块级作用域都有缓存记录,记录到AO中,类似于每个块级作用域都存到AO中,每个块级作用域中i的值都不一样,相当于一个闭包
块级作用域会缓存变量,类似于闭包
for (let i = 0; i < 3; i++) {
}
肯定不等价于
for (; 1;) {
let i = 0;
let i = 1;
let i = 2;
break;
}
等价于
for (; 1;) {
{let i = 0;}
{let i = 1;}
{let i = 2;}
break;
}
函数名与let
let与函数名重复也会报错
与函数名重名,也会报错
let a=1;
function a(){
}
console.log(a);//Uncaught SyntaxError: Identifier 'a' has already been declared
函数的提升后与let重名是可以的?全局的let影响了function提升到全局
全局的let影响了function提升到全局
这样可以运行,编辑器也没有错误提示
let a = 1;
{
a();//10
function a() {
console.log(10)
}
}
console.log(a);//1
这样在编辑器内会报错,但在编辑器内会有提示错误,但是是可以运行的,用nodejs可以运行
浏览器里面也可以运行成功
这个代码可以运行说明,它不能变成如下代码,function a不会提升到与let同级
let a = 1;
function a() {//SyntaxError: Identifier 'a' has already been declared
console.log(10)
}
{
a();
}
console.log(a);
函数声明提升仅限于当前的环境,当前{}
函数的提升
{
function a() {
console.log(10)
}
a();//10
}
console.log(a);
a();//10
函数的提升与let1
let a=1;
{
function a() {
console.log(10)
}
a();//10
}
console.log(a);//1
a()
当外面有块级作用域,有let声明,{}里面的function不会声明提升
函数的提升与let2
全局的let影响了function提升到全局,如果全局有let相当于全局有块级作用域
// a();//ReferenceError: Cannot access 'a' before initialization
let a=11;
{
function a() {
console.log(10)
}
a();//10
}
console.log(a);//11
// a();//TypeError: a is not a function
建议用函数表达式的方式声明函数
不主张这么做,应该用
因为函数表达式可以与let结合,并且var会如果重复会报错
let a = 1;
{
var test = function () {
console.log(10)
}
}
console.log(a);//1
let a = 1;
{
/* var a = function () {
//SyntaxError: Identifier 'a' has already been declared
console.log(10)
}*/
let a=function () {
console.log(10);
}
a()//10
}
console.log(a);//1
const
定义常量,定义时必须赋值
定义常量
1.const定义的变量,必须赋值,一旦定义必须赋值,值不能被更改。
常量恒为一个值,不变,如果不赋值,与常量冲突了。
const a;//SyntaxError: Missing initializer in const declaration
const a=12;
console.log(a);//12
常量不能更改
const a=12;
a=10;//TypeError: Assignment to constant variable.
console.log(a);//12
暂时性死区,与let一样
依然会存在暂时性死区的问题,const a只在块级作用域内有效,并且不会提升
2.有块级作用域,不能提升,有暂时性死区
{
const a = 12;
}
console.log(a);//ReferenceError: a is not defined
{
console.log(a);//ReferenceError: Cannot access 'a' before initialization
const a = 12;
}
不能重复声明
3.与let一样不能重复声明
{
const a = 12;
let a=10;//SyntaxError: Identifier 'a' has already been declared
}
const与引用值
const obj={};
obj.name='zhangsan';
console.log(obj);
如果是原始值,存在栈内存中,栈内存地址不可变,如果重新赋值,其实改变了地址,因为见js第一节课。 原始值重新赋值改变了地址,与上次赋值的地址有变化。
引用值,指针固定,没有变化,但里面的内容,里面的数据结构没有办法保证。
const只能保障地址不会变化,但地址里面的内容保障不了。
但是我想让引用值也不能被更改,这时就需要对象冻结
可以修改const定义的数组
const obj=[];
obj[2]='zhangsan';
console.log(obj);
冻结方法
对象原型上的冻结方法
console.log(Object.prototype);
方法使用
const obj=[];
Object.freeze(obj);
obj[2]='zhangsan';
console.log(obj);
const obj={};
Object.freeze(obj);
obj[2]='zhangsan';
console.log(obj);
对象同理
但如果obj对象的属性也是个对象,仅仅冻结obj对象就没有用处了
冻结函数封装
function myFreeze(obj) {
Object.freeze(obj);
for (var key in obj) {
if (typeof (obj[key]) === 'object' && obj[key] !== null) {
Object.freeze(obj[key]);
}
}
}
const person = {
son: {
lisi: '18',
zhangsan: '19'
},
car: ['benze', 'mazda', 'BMW']
}
myFreeze(person);
person.son.wangu=20;
person.son.lisi=30;
person.car[3]='toyaya';
console.log(person);
注释掉冻结函数的结果
一般不用这个方法
const http1=require('http');
请求方法,请求都是一个new出来的对象,更改new出来的对象就不会改变之前的构造函数。
例子,在上面代码更改http1变量,http包里面的函数,的内容不会改变。
就从根源上解决了这个问题,可以更改对象,但不要影响引用的包
顶层对象
var a=1;
b=1;
console.log(a);//1
console.log(window.a);//1
console.log(b);//1
console.log(window.b);//1
浏览器可以运行
nodejs中不可以运行,报错
ReferenceError: window is not defined
b、a挂载到window上面的,顶层对象的属性与全局变量是一样的,都是挂载到winodw上面
造成问题:
如果变量不写var,写个b=1,实际上我想写a=1,只不过我因为马虎写错了,但js并不会报错,会把b=1挂到window上面。a,b简单,如果变量名长了很容易写错了。
function、var允许变量声明,声明的变量是全局变量
let、const、class不允许通过声明变量方式,声明一个全局变量的,它们定义的不属于顶层对象属性,
这里面顶层对象是window
let、const、class是es6为了解决上面的问题,上面的现状提出的问题
let a=1;
console.log(window.a);//undefined
console.log(a);//1
var b=1;
console.log(window.b);//1
console.log(b);//1
let声明变量,在浏览器中,不会挂载到window上面
不同的浏览器顶层对象不一样,浏览器是window
node是global
console.log(global);