一、作用域
- 全局作用域
- 私有作用域
- ES6的块级作用域
1、全局作用域
当打开页面的时候,会提供一个給js代码执行的环境全局作用域,默认提供一个最大的window对象
【全局变量】:在全局作用域中保存的变量
全局变量与window的关系
判断一个对象有没有某个属性:属性名 in 对象
var a = 10;
undefined
"a" in window; // 返回值为布尔类型
true
- 在全局作用域下声明的变量,相当于給window添加的一个属性(属性名就是变量名,属性值就是变量值)
- window上的方法,可以省略window。(eg:window.alert(“1”可以写成alert(“1”)))
- ES6中let、const声明的变量,阻断了与window的关系
在全局变量中带var 和 不带var
- 相同点:都是給window添加属性
不同点:
- 带var 有变量提升,不带var 没有变量提升;
- 带var 通过delete window.属性名 删除不掉;不带var的可以删除掉(也就说明隐含全局变量严格来说不是真正的变量,而是全局对象的属性,因为属性可以通过delete删除,而变量不可以。) ```
var a = 10; “a” in window; true b = 20; delete window.a; false delete window.b; true ```
2、私有作用域
函数执行的时候,形成的作用域就是私有的,保存里面的变量不受干扰
【私有变量】:
- 函数里边声明的变量
- 形参
二、作用域链
查找变量的时候,先看自己私有作用域中有没有,没有的话,就向上一级作用域继续查找
获取
- 未定义也未声明的变量,console的时候, 会报错:n is not defined;
function fn() {
a = 10;
console.log(n);
}
undefined
fn(); // Uncaught ReferenceError: n is not defined
赋值
- 赋值但未声明的变量,会被默认定义未全局变量
function fn() {
a = 10;
n = 20;
console.log(n); // 20
}
fn();
console.log(n); // 20
三、堆栈内存
栈内存
- 供js代码运行的环境(window全局作用域、函数执行时的局部作用域)
- 存储基本数据类型
堆内存
- 存储引用数据类型
- 对象:键值对形式存储
- 函数:字符串形式存储
四、变量提升
- 代码执行的时候,首先会形成一个供js执行的环境,接下来在代码自上而下执行之前有一步操作:“变量提升”(会把带var 和带function的变量找出来)
- var:只声明
- function:声明并且定义(赋值)变量
- 变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的变量进行提升,因为此时函数中存储的都是字符串),栈内存形成之后,代码执行之前。
- 当代码执行遇到创建函数(除匿名函数(函数表达式和立即执行函数)外)这部分代码后,会直接跳过,因为在提升阶段就已经完成函数的赋值操作;函数执行前 首先 生成私有作用域,私有作用域形成之后,先形参赋值,再进行变量提升。
变量提升的特殊性!!
1、判断语句:不论条件是否成立,都会进行变量提升
- VAR 还是只声明,不定义
- FUNCTION
- 在IE10以及其以下老版本的浏览器中:声明+定义
- 在最新版本的浏览器中:只声明
// 例题1
console.log(a);
if(1==2){
var a=12;
}
console.log(a);
(1) 自执行函数在全局作用域下不进行变量提升
- 最新版本浏览器之条件语句中的变量提升(FUNCTION变量提升时只声明不定义)
// 例题2 360面试题
f=function(){
return true;
};
g=function(){
return false;
};
~function(){
if(g()&&[]==![]){
f=function(){return false;};
function g(){
return true;
}
}
}();
console.log(f());
console.log(g());
// 题目解析:(最新版本浏览器中)
1、在全局作用域中,变量提升:无
2、代码开始自上往下执行 f 就是window.f=function(){}, g就是window.g=function(){}
自执行函数,形成私用作用域,变量提升:不管条件是否成立,判断语句里面的代码都会进行变量提升,function g 在新版本浏览器中只声明未定义。 接着走到if语句中,g(),此时的g只声明未定义,相当于undefined(),所以g( )会报类型错误,下面的代码都不会执行Uncaught TypeError: g is not a function
- IE10以及其以下老版本浏览器之 条件语句中的变量提升(FUNCTION变量提升时 声明+定义)
(2)条件判断下的变量提升到底有多坑!
在条件判断语句中,如果条件成立,会把执行体当成私有作用域,再进行变量提升
// 例题3
console.log(fn);
if(1==1){
console.log(fn);
function fn(){
console.log("ok");
}
}
console.log(fn)
解析:
console.log(fn); // undefined 在新版本浏览器中,不管条件是否成功,都会进行变量提升,function 只声明,
if(1==1){
console.log(fn);// fn 函数:在条件判断语句中,如果条件成立,会把执行体当成私有作用域,再进行变量提升
// 再从上往下执行代码,此时fn 定义完成。
function fn(){
console.log("ok");
}
}
console.log(fn) // 条件成立,给fn进行了赋值,打印出fn函数
在条件判断下,如果有function定义的变量,在这个function这个函数后面的更改变量的值,更改的都是私有变量。
// 例题4
var a=0;
if(true){
a=1;
function a(){}
a=21;
console.log(a);
}
console.log(a);
2、只对等号左边的做变量提升
console.log(fn);
console.log(fn(1, 2));
var fn=function (n, m){
return n+m;
}
console.log(fn(3, 4));
3、函数里return 下面的代码本身是不执行的,但是可以进行变量提升;而return后边的代码不进行变量提升
function fn(){
console.log(f2);
return function f1(){
}// return后面的代码 不会进行变量提升
// return下面的代码可以进行变量提升
function f2(){
console.log("f2")
}
}
fn();
4、如果变量名字重复该如何?(重复变量只会声明一次,但可以多次赋值)
对于VAR的不会进行重复声明,但会重新赋值
var num=2;
var num=3;
console.log(num);
对于FUNCTION 在变量提升阶段,遇到重复声明定义的,会进行重新赋值
fn();
function fn(){
console.log(1);
}
function fn(){
console.log(2);
}
fn();
function fn(){
console.log(3);
}
fn=100;
function fn(){
console.log(4);
}
fn();
5、自执行函数 在当前所在的作用域(包含自执行函数的区域)中,不进行变量提升(自执行函数自己所形成的私有作用域照常进行)
function f2(){
console.log("f2");
}
console.log(f1); // 报错 f1 is not defined;
(function f1(){
console.log(a); // undefined, 照常进行变量提升
var a=3;
})();
五、练习题
练习题1、2、7、11、15 ——-重点
- 1、 !!!
var ary = [12, 23];
var s = 100;
function fn(ary,s) {
// ary的形参赋值为空间地址(全局ary的空间地址)
s = 200;
ary[0] = 100;
console.log(ary);
console.log(s);
ary = [100]; // ary赋值 重新开启一个空间地址
console.log(ary);
}
fn(ary,s); // ary = 空间地址传入函数
console.log(ary);
console.log(s)
- 2、 !!
console.log(a);
console.log(b);
console.log(c);
var a, b, c = 10;
console.log(a);
console.log(b);
console.log(c);
- 3、
console.log(a, b);
var a = 12,
b = 12;
function fn() {
console.log(a, b);
var a = b = 13;
console.log(a, b);
}
fn();
console.log(a, b);
- 4、
var a = 4;
var f = 3;
function b(x,y,a) {
alert(a); //4
// 形参和arguments存在映射关系;
arguments[2]=10;
alert(a); 10
}
a=b(1,2,f);
alert(a);
console.log(f); //10
- 5、
fn();
function fn() {
console.log(1);
};
fn();
function fn() {
console.log(2);
};
fn();
var fn = function () {
console.log(3);
};
fn();
function fn() {
console.log(4);
};
fn();
function fn() {
console.log(5);
};
- 6、
var foo='hello';
(function(foo){
console.log(foo);
var foo=foo||'world';
console.log(foo);
})(foo);
console.log(foo);
- 7、(自执行函数没有变量提升) !!
f = function(){return true}
g = function(){return false}
~function(){
if(g() && [] == ![]){
f = function(){return false}
function g(){return true}
}
}();
console.log(f())
console.log(g())
* 8、
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
console.log(foo); //
}
bar();
* 9、
var a = 10; (function () { console.log(a); // undefined a = 5; console.log(window.a); // 10 var a = 20; console.log(a); // 20 })()
* 10、
console.log(a);
console.log(b);
var a = 1;
function a(){}
var b= function(){};
console.log(a);
* 11、 !!!!
// 函数提升优先级高于变量提升,且不会被同名变量声明时覆盖,但是会被变量赋值后覆盖 fun(); function fun() { console.log(1); } var fun = function () { console.log(2); } function fun() { console.log(3); } fun(); function fun() { console.log(4); } fun(); var fun = function () { console.log(5); } fun();
* 12、
console.log(fn); if (1 == 1) { console.log(fn);
function fn() {
console.log("ok");
}
} console.log(fn);
-13、
let a=10,
b=10;
fn=function(){
console.log(a);
let a=b=20;
console.log(a,b);
};
fn();
console.log(a,b)
-14、
function forEach() {
var i = 0;
for (i = 0; i <= 5; i++) {
console.log(i);
}
console.log(i);
}
forEach();
-15、 !!!!!!
var a=9;
function fn(){
a=0;
return function(b){
return b+a++; // 先+ 再 ++ (先return b+a,再a++)
}
}
var f=fn(); // 把函数function(b) { ... }的空间地址赋值给f
console.log(f(5));
console.log(fn()(5)); // fn() 重新执行一次,a又赋值为0
console.log(f(5));
console.log(a);
-16、
var i=10;
function fn(){
return function(n){
console.log(n+(++i));
}
}
var f=fn();
f(20);
fn()(20);
fn()(30);
f(30);