这里写目录标题
变量提升机制
1.数据值操作机制
2.变量提升机制
+什么是变量提升
+带var和不带的区别
+等号左边变量提升
+条件判断下的变量提升
+重名问题的处理
3.ES6中let不存在变量提升
+不允许重复定义
+暂时性死区
+不存在变量提升
6.条件判断下的变量提升
在当前作用域下,不管条件是否成立都要进行变量提升
=>带var的还是只声明
=>带function的在老版本浏览器渲染机制下,声明+定义都处理。
但是为了迎合ES6中的块级作用域,新版浏览器对于函数(在条件判断中的函数),不管条件是否成立,都只是先声明,没有定义,类似于var。
console.log(a);//undefined
if (1 === 2) { 不成立,则不执行
var a = 12;
}
console.log(a);//undefined
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-条件判断下的变量提升到底有多坑(???)
函数本身:当条件成立,进入到判断体中(在ES6中它是一个块级作用域)
console.log(fn); //undefined
if (1 === 1) {
console.log(fn); ?//ƒ fn() {console.log('hello');}?
函数本身:当条件成立,进入到判断体中(在ES6中它是一个块级作用域)
第一件事并不是代码执行,而是类似于变量提升一样,先把fn声明和定义
了,也就是判断体中代码执行之前,fn就已经赋值了
function fn() {
console.log('hello');
}
}
console.log(fn);//ƒ fn() {console.log('hello');}
7.重名问题的处理
//1.带var和function关键字声明相同的名字,这种也算是重名了(其实是一个fn,只是存储值的类型不一样)
//2.关于重名的处理:如果名字重复了,不会重新的声明,但是会重新的定义(重新赋值)
var fn = 11;
function fn() {
console.log(22);
}
console.log(fn);//11
fn();//Uncaught TypeError: fn is not a function
????我也很懵????
fn(); //undefined
function fn() { console.log(1); }
fn(); //4
function fn() { console.log(2); }
fn(); //4
var fn = 100;
fn(); //fn is not a function
function fn() { console.log(3); }
fn(); //fn is not a function
function fn() { console.log(4); }
fn(); //fn is not a function
3.ES6中let不存在变量提升
3.1不允许重复定义
3.2不存在变量提升
1.在ES6中基于let/const等方式创建变量或者函数,不存
在变量提升机制,(存在作用域链)
2.切断了全局变量和w工NDOW属性的映射机制
=>在相同的作用域中,基于LET不能声明相同名字的变量(不管用什么方式在当前作用域下声明了变量,再次使用LET创建都会报错)
虽然没有变量提升机制,但是在当前作用域代码自上而下执行之前,浏览器会做一个重复性检测(语法检测):自上而下查找当前作用域下所有变量,一旦发现有重复的,直接抛出异常,代码也不会在执行了(虽然没有把变量提前声明定义,但是浏览器已经记住了,当前作用域下有哪些变量)
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 12;
console.log(window.a);//undefined
let a = 12, b = 10;
let fn = function () {
//console.log(a, b);//Uncaught ReferenceError: Cannot access 'a' before initialization
let a = b = 20; //let a=20; 把全局中的b=20
console.log(a, b);//20 20
}
fn();
console.log(a, b);//10 20
3.3暂时性死区(??)
// var a = 12;
// if (true) {
// console.log(a);//=>Uncaught ReferenceError: a is not defined
// let a = 13;//=>基于LET创建变量,会把大部分{}当做一个私有的块级作用域(类似于函数的私有作用域),在这里也是重新检测语法规范,看一下是否是基于新语法创建的变量,如果是按照新语法规范来解析
// }
// console.log(a);//=>Uncaught ReferenceError: a is not defined
console.log(typeof a);//=>"undefined" 在原有浏览器渲染机制下,基于typeof等逻辑运算符检测一个未被声明过的变量,不会报错,返回UNDEFINED
// console.log(a);//=>Uncaught ReferenceError: a is not defined
console.log(typeof a);//=>Uncaught ReferenceError: a is not defined
let a;//=>如果当前变量是基于ES6语法处理,在没有声明这个变量的时候,使用TYPEOF检测会直接报错,不会是UNDEFINED,解决了原有的JS的死区
-------------------------------------------------------------------------------------------------------------------------------------------------------------
·闭包作用域(scope)
1.作用域处理机制
+全局变量和私有变量
+作用域链机制
+闭包的作用:保存&保护
2.闭包作用域练习
+JQ源码分析
+选项卡剖析
+面试题练习
3.ES6中的块级作用域
+let可以形成块级作用域
+实际意义
+const
闭包、作用域(scope)
1.作用域处理机制
闭包:形成私有作用域保护变量不受外界的干扰,这种保护机制可以理解为闭包。私有作用域下的数据和全局的无关
作用域:函数执行会形成一个私有作用域,给代码执行提供一个环境
1.1全局变量和私有变量
在全局作用域下声明的变量是全局变量,
在私有作用域下声明的变量是私有变量。
var a = 123;
if (true) {
a = 321;// Cannot access 'a' before initialization
let a = 888;
}
var a = 12, b = 13, c = 14;
function fn(a) {
console.log(a, b, c);//12 undefined 14(C是全局的)
var b = c = a = 20;
/*var b=20; c=20;把全局的c修改为20 a=20;*/
console.log(a, b, c); // 20 20 20
}
fn(a);
/*把FN执行(小括号中是实参:值)
执行FN把全局变量A的值12当做实参传递给函数的形参
fn (12)
*/
console.log(a, b, c);//12 13 20
-------------------------------------------------------------------------------------------------------------------
8-关于私有变量的一点练习
var ary = [12, 23];
function fn(ary) {
console.log(ary); //[12, 23]
ary[0] = 100;
ary = [100];
ary[0] = 0;
console.log(ary); //[0]
}
fn(ary);
console.log(ary); //[100,23]
1.2作用域链机制
上级作用域的查找
var a = 12;
function fn() {
console.log(a);
}
fn();//12
var a = 12;
function fn() {
console.log(a);
}
function sum() {
var a = 120;
fn(); fn函数是在全局下创建的,他要向上查找到 a = 12;
}
sum();//12
当前函数执行,形成一个私有作用域A,A的上级作用域是谁,和他在哪执行的没有关系。
和他在哪创建(定义)的有关系,在哪创建的,它的上级作用域就是谁。
var a = 12;
function fn() {
// arguments:实参集合
// 了解 arguments.callee:函数本身fn
// 了解 arquments.callee.caller:当前函数在哪执行的,CALLER就是谁(记录的是它?执行的宿主环境),在全局下执行caller的结果是null
console.log(arguments.callee.caller);
}
function sum() {
var a = 120;
fn();
}
sum();// sum() { var a = 120; fn(); }
function aa(){
fn();
}
aa();//ƒ aa(){ fn(); }
var n = 10;
function fn() {
var n = 20;
function f() {
n++;
console.log(n);
}
f();
return f;
}
var x = fn(); //21
x();//22
x();//23
console.log(n);//10
堆栈内存
JS中的内存分为堆内存和栈内存
- 堆内存:存储引用数据类型值(对象:键值对 函数:代码字符串)
- 栈内存:提供JS代码执行的环境和存储基本类型值
-----------------------------------------------------------------------------------------
[堆内存释放]
- 让所有引用堆内存空间地址的变量赋值为null即可(没有变量占用这个堆内存了,浏览器会在空闲的时候把它释放掉)
[栈内存释放]
- 一般情况下,当函数执行完成,所形成的私有作用域(栈内存)都会自动释放掉(在栈内存中存储的值也都会释放掉),但是也有特殊不销毁的情况:
- 1.函数执行完成,当前形成的栈内存中,某些内容被栈内存以外的变量占用了,此时栈内存不能释放(一旦释放外面找不到原有的内容了)
- 2.全局栈内存只有在页面关闭的时候才会被释放掉
- …
- 如果当前栈内存没有被释放,那么之前在栈内存中存储的基本值也不会被释放,能够一直保存下来
???
var i = 1;
function fn(i) {
return function (n) {
console.log(n + (++i));
}
}
var f = fn(2);
f(3); 6
fn(5)(6); 12
fn(7)(8); 16
f(4); 8
1.3闭包的作用:保存&保护
[闭包]
-
=>函数执形成一个私有的作用域,保护里面的私有变量不受外界的干扰,这种保护机制称之为“闭包”
-
=>市面上的开发者认为的闭包是:形成一个不销毁的私有作用域(私有栈内存)才是闭包
柯里化函数
function fn() {
return function () {
}
}
var f = fn();
惰性函数
var utils = (function () {
return {
}
})();
/闭包项目实战应用
真实项目中为了保证Js的性能(堆栈内存的性能优化),应该尽可能的减少闭包的使用(不销毁的堆栈内存是耗性能的)
1.闭包具有”保护”作用:保护私有变量不受外界的干扰
2.闭包具有"保存”作用:形成不销毁的栈内存,把一些值保存下来,方便后面的调取使用