变量提升(20)

<!--
  一般都把JS放到BODY的末尾
    1.为啥?
    2.放在HEAD中可不可以? 如何放到HEAD中也可以实现出放到BODY末尾的效果?
    3.SCRIPT标签中有两个属性:defer / async,这两个属性是做什么的
-->
<!--
    1.BODY中编写的都是HTML标签,JS很多时候需要操作这些元素,
    首先我们要保证元素加载成功,才可以在JS中获取到,所以我们
    通常会把JS放在BODY的末尾。
-->
<!--
    2.利用 window.onload = function(){} 代码包裹
-->
<!--
    3.async:他是异步加载,不确定何时会加载好;页面加载时,
    带有 async 的脚本也同时加载,加载后会立即执行,
    如果有一些需要操作 DOM 的脚本加载比较慢时,这样会造成 DOM 
    还没有加载好,脚本就进行操作,会造成错误。
    defer:页面加载时,带有 defer 的脚本也同时加载,加载后会等待
    页面加载好后,才执行。
-->
var a = 12;
var b = a;
b = 13;
console.log(a);

var ary1 = [12, 23];
var ary2 = ary1;
ary2.push(100);
console.log(ary1);

function sum() {
    var total = null;
    for (var i = 0; i < arguments.length; i++) {
        var item = arguments[i];
        item = parseFloat(item);
        !isNaN(item) ? total += item : null;
    }
    return total;
}
console.log(sum(12, 23, '34', 'AA'));


 变量提升

/*
 * 变量提升:
 *   =>当栈内存(作用域)形成,JS代码自上而下执行之前,浏览器首先会把所有带 “VAR”/“FUNCTION” 关键词的进行提前 “声明” 或者 “定义” ,这种预先处理机制称之为 “变量提升”
 *
 *   =>声明(declare):var a  (默认值undefined)
 *   =>定义(defined):a=12 (定义其实就是赋值操作)
 *
 *   [变量提升阶段]
 *   =>带“VAR”的只声明未定义
 *   =>带“FUNCTION”的声明和赋值都完成了
 *
 *   =>变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的进行提升,因为此时函数中存储的都是字符串而已)
 *   =>在全局作用域下声明的函数或者变量是“全局变量”,同理,在私有作用域下声明的变量是“私有变量” [带VAR/FUNCTION的才是声明]
 *
 *   =>浏览器很懒,做过的事情不会重复执行第二遍,也就是,当代码执行遇到创建函数这部分代码后,直接的跳过即可(因为在提升阶段就已经完成函数的赋值操作了)
 */
console.log(a);//=>undefined
var a = 12;


带VAR和不带的区别

//=>在全局作用域下声明一个变量,也相当于给WINDOW全局对象设置了一个属性,变量的值就是属性值(私有作用域中声明的私有变量和WINDOW没啥关系)
/*
console.log(a);//=>undefined
console.log(window.a);//=>undefined
console.log('a' in window); //=>TRUE 在变量提升阶段,在全局作用域中声明了一个变量A,此时就已经把A当做属性赋值给WINDOW了,只不过此时还没有给A赋值,默认值UNDEFINED  in:检测某个属性是否隶属于这个对象
var a = 12;//=>全局变量值修改,WIN的属性值也跟着修改
console.log(a);//=>全局变量A  12
console.log(window.a);//=>WINDOW的一个属性名A  12

a = 13;
console.log(window.a);//=>13

window.a = 14;
console.log(a);//=>14
//=>全局变量和WIN中的属性存在 “映射机制”
*/

//=>不加VAR的本质是WIN的属性
/*
// console.log(a);//=>Uncaught ReferenceError: a is not defined
console.log(window.a);//=>undefined
// console.log('a' in window);//=>false
a = 12;//=>window.a=12
console.log(a);//=>12
console.log(window.a);//=>12
*/
/*var a = 12,
    b = 13;//=>这样写B是带VAR的*/
/*var a = b = 12;//=>这样写B是不带VAR的*/

console.log(a, b);//=>undefined undefined
var a = 12,
    b = 12;

function fn() {
    console.log(a, b);//=>undefined 12
    var a = b = 13;
    /*var a=13;  b=13;*/
    console.log(a, b);//=>13 13
}

fn();
console.log(a, b);//=>12 13


作用域链

function fn() {
    /*
     * 变量提升:无
     */
    // console.log(b);//=>Uncaught ReferenceError: b is not defined
    b = 13;
    //console.log('b' in window);//=>true  在作用域链查找的过程中,如果找到WIN也没有这个变量,相当于给WIN设置了一个属性B (window.b=13)
    console.log(b);//=>13
}

fn();
console.log(b);//=>13

关于条件判断下的变量提升

/*
 * 变量提升:
 *   var fn;   =>只对等号左边进行变量提升
 *   sum = AAAFFF111;
 */
sum();
fn();//=>Uncaught TypeError: fn is not a function

//=>匿名函数之函数表达式  (更多采用函数表达式的方式)
var fn = function () {
    console.log(1);
};//=>代码执行到此处会把函数值赋值给FN

fn();

//=>普通的函数
function sum() {
    console.log(2);
}

/*
 * 在当前作用域下,不管条件是否成立都要进行变量提升
 *   =>带VAR的还是只声明
 *   =>带FUNCTION的在老版本浏览器渲染机制下,声明和定义都处理,但是为了迎合ES6中的块级作用域,新版浏览器对于函数(在条件判断中的函数),不管条件是否成立,都只是先声明,没有定义,类似于VAR
 */

/*
 * 变量提升
 *   var a;  =>在全局作用域下声明的全局变量也相当于给WIN设置了一个属性 window.a=undefined
 */
console.log(a);//=>undefined
if ('a' in window) {
    var a = 100;
}
console.log(a);//=>100
/*
 * 变量提升:无
 */
f = function () {return true;};//=>window.f=...(TRUE)
g = function () {return false;};//=>window.g=...(FALSE)
~function () {
    /*
     * 变量提升:
     *   function g;  //=>g是私有变量
     */
    if (g() && [] == ![]) {//=>Uncaught TypeError: g is not a function (此时的g是undefined)
        //=>[]==![]:TRUE
        f = function () {return false;};//=>把全局中的f进行修改 window.f=...(FALSE)
        function g() {return true;}
    }
}();
console.log(f());
console.log(g()); //=>Edge、谷歌等高版本浏览器直接报错,IE低版本 false false
/*
 * 变量提升:
 *   function fn;
 */
// console.log(fn);//=>undefined
if (1 === 1) {
    console.log(fn);//=>函数本身:当条件成立,进入到判断体中(在ES6中它是一个块级作用域)第一件事并不是代码执行,而是类似于变量提升一样,先把FN声明和定义了,也就是判断体中代码执行之前,FN就已经赋值了
    function fn() {
        console.log('ok');
    }
}
// console.log(fn);//=>函数本身

变量提升机制下重名的处理

/*
 * 1.带VAR和FUNCTION关键字声明相同的名字,这种也算是重名了(其实是一个FN,只是存储值的类型不一样)
 */
/*
var fn = 12;
function fn() {

}
*/

/*
 * 2.关于重名的处理:如果名字重复了,不会重新的声明,但是会重新的定义(重新赋值)[不管是变量提升还是代码执行阶段皆是如此]
 */

/*
 * 变量提升:
 *   fn = ...(1)
 *      = ...(2)
 *      = ...(3)
 *      = ...(4)
 */

fn();//=>4
function fn() {console.log(1);}
fn();//=>4
function fn() {console.log(2);}
fn();//=>4
var fn=100;//=>带VAR的在提升阶段只把声明处理了,赋值操作没有处理,所以在代码执行的时候需要完成赋值 FN=100
fn();//=>100() Uncaught TypeError: fn is not a function
function fn() {console.log(3);}
fn();
function fn() {console.log(4);}
fn();

ES6中的LET不存在变量提升

/*
 * 在ES6中基于LET/CONST等方式创建变量或者函数,不存在变量提升机制
 *
 *  =>切断了全局变量和WINDOW属性的映射机制
 *
 *  =>在相同的作用域中,基于LET不能声明相同名字的变量(不管用什么方式在当前作用域下声明了变量,再次使用LET创建都会报错)
 *
 *   虽然没有变量提升机制,但是在当前作用域代码自上而下执行之前,浏览器会做一个重复性检测(语法检测):自上而下查找当前作用域下所有变量,一旦发现有重复的,直接抛出异常,代码也不会在执行了(虽然没有把变量提前声明定义,但是浏览器已经记住了,当前作用域下有哪些变量)
 */

// console.log(a);//=>Uncaught ReferenceError: a is not defined
// let a = 12;
// console.log(window.a);//=>undefined
// console.log(a);//=>12

/*let a = 12;
console.log(a);
let a = 13;//=>Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);*/

/*b = 12;
console.log(b);//=>12
a = 12;//=>Uncaught ReferenceError: a is not defined
console.log(a);
let a = 13;
console.log(a);*/

let a = 10,
    b = 10;
let fn = function () {
    console.log(a, b);//=>Uncaught ReferenceError: a is not defined
    let a = b = 20;
    /*
     * let a=20;
     * b=20; //=>把全局中的 b=20
     */
    console.log(a, b);
};
fn();
console.log(a, b);

 暂时性死区

/*
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的死区

私有变量和全局变量

/*
 * 变量提升:
 *    var a;  var b;  var c;
 *    fn = xxx...
 */
var a = 12,
    b = 13,
    c = 14;

function fn(a) {
    /*
     * 形参赋值
     *   a = 12
     *
     * 变量提升
     *   var b;
     *
     * =>在私有作用域中,只有以下两种情况是私有变量
     *  A:声明过的变量(带VAR/FUNCTION)
     *  B:形参也是私有变量
     *
     *  剩下的都不是自己私有的变量,都需要基于作用域链的机制向上查找
     */
    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*3
}

fn(a);//=>把FN执行(小括号中是实参:值) =>执行FN把全局变量A的值12当做实参传递给函数的形参 =>fn(12)
console.log(a, b, c);//=>12 13 20
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]


上级作用域的查找

当前函数执行,形成一个私有作用域A,A的上级作用域是谁,和他在哪执行的没有关系,和他在哪创建(定义)的有关系,在哪创建的,它的上级作用域就是谁。

/*
var a = 12;
function fn() {
    console.log(a)
    //=>arguments:实参集合
    //=>arguments.callee:函数本身FN
    //=>arguments.callee.caller:当前函数在哪执行的,CALLER就是谁(记录的是它执行的宿主环境),在全局下执行CALLER的结果是NULL
    console.log(arguments.callee.caller);
}
function sum() {
    var a = 120;
    fn();
}
function aa() {
    fn();
}
aa(); //=>12
*/

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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值