复习
for与var
for (var i = 0; i < 10; i++) {i = 'a';console.log(i);//a}
等价于
var i = 0for (; i < 10; ) {i = 'a';console.log(i);//ai++;}
for (var i = 0; i < 10; i++) {var i = 'a';console.log(i);//a}
也可以变成如下代码,for表达式中的var i与for()中的var i都被提升到了全局
var i = 0for (; i < 10; ) {i = 'a';console.log(i);//ai++;}
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 declaredconsole.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 = 0for (; i < 10; i++) {arr[i] = function () {console.log(i)}}var i = 0for (; 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();//10function a() {console.log(10)}}console.log(a);//1
这样在编辑器内会报错,但在编辑器内会有提示错误,但是是可以运行的,用nodejs可以运行
浏览器里面也可以运行成功
这个代码可以运行说明,它不能变成如下代码,function a不会提升到与let同级
let a = 1;function a() {//SyntaxError: Identifier 'a' has already been declaredconsole.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);//1a()

当外面有块级作用域,有let声明,{}里面的function不会声明提升
函数的提升与let2
全局的let影响了function提升到全局,如果全局有let相当于全局有块级作用域
// a();//ReferenceError: Cannot access 'a' before initializationlet 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 declaredconsole.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 initializationconst 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);//1console.log(window.a);//1console.log(b);//1console.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);//undefinedconsole.log(a);//1var b=1;console.log(window.b);//1console.log(b);//1
let声明变量,在浏览器中,不会挂载到window上面
不同的浏览器顶层对象不一样,浏览器是window
node是global
console.log(global);


