那些我不知道的基础知识

1. function函数传参

1)传参类型为原始类型(字符,数值,布尔)时,为传值传递(passed by value);修改参数的值不会修改原始值。

        var a = 1;
        function name(a) { // 参数相当于name(var a; a=4), 这个a的作用域在函数内,和外部的a是两个变量
            a = 3
            console.log('--inner--',a); // 3
        }
        name(4);
        console.log('---outer--',a); // 1

2)传参类型为符合类型(对象,数组,函数等),为传址传递(passed by reference), 传入函数的是原始值的地址。

  • 在函数内部修改传入的值的参数会修改原始值。
var obj = {a:1};
function fn(obj) {
  obj.a = 2;
}
fn(obj);
console.log(obj); // { a: 2 }
  • 在函数内部整个替换传入的参数,不会修改原始值。原因是:传入的代表地址的形参,被赋值后,指向另一个地址。原来地址的值不变。
var obj = {a:1};
function fn(obj) {
  obj = { a: 2 }
}
fn(obj);
console.log(obj); // { a: 1 }

 2. 语句和表达式

函数式编程涉及到对表达式的理解。

区别:

  • 语句为了完成一段操作,一般不需要返回值;表达式是为了得到一个值,一定会返回一个值。
// 语句为了完成一段操作,如循环语句,赋值语句等
        for(let i=0; i<5;i++){
            console.log(i);
        };
       var a = 1 + 3;
// 表达式是为了返回一个值
    if (true && 1) // 括号中是表达式,返回1
  • 语句以分号结尾。表达式不能以分号结尾,加上分号,会变成语句。
  • js引擎规定,function出现在行首时,时函数声明语句。非行首时,是函数表达式。
// 此为函数表达式     
  var foo = function() {};

3. 变量(var/function/let/const/class)

1.区分大小写

a和A是两个不同的变量,对于上面四种变量声明方式来说都是。

var a = 1; 
var A = 1;

2.变量提升(var和function)

对于var和function声明的变量存在变量提升,提升到当前函数作用域顶部或者全局作用域顶部

js代码编译运行过程分为: 词法分析->语法解析(AST)->代码生成->代码执行;

编译器在进行代码生成时,遇到变量声明,编译器询问当前作用域是否已经存在该变量。

如果是,忽略;如果否,则要求作用域在当前作用域声明一个变量。

所以:

在js执行时,所有的声明类语句已经声明过了,他们在代码生成阶段阶段已经声明过了。

而且作用域声明的时候在当前作用域的顶部进行声明。

所以变量提升其实指的是变量声明提升,函数声明也会提升。

❌不要在块级作用域中声明函数,因为在ES5中function整个声明提升;ES6中只提升函数名。

console.log(a); // undefined
var a = 100;
// 相当于
var a; 
console.log(a);
a=100;

对于let/const/class声明的变量(或常量)不存在变量提升,并且存在暂时性死区。

<script>
    // 暂时性死区是说作用域内只要声明了let变量,声明之前就不能使用
    var a = 100;
    function add() {
        // 调用add()后报错;编译时不报错。
        a = 1000;// Uncaught ReferenceError: Cannot access 'a' before initialization
        let a;
        console.log(a);
    }
    add();
</script>
<script>
    // 下面是函数赋初值造成的“暂时性死区”现象
    var a = 100;
    // Uncaught ReferenceError: Cannot access 'a' before initialization
    function sum(a=a) { // 参数赋值形成单独的作用域 相当于在函数内let a=a;
    }
    sum(); // 声明时不会报错,调用时报错
</script>
    <script>
        // class变量也不存在变量提升;
        const b = new a();//Uncaught ReferenceError: Cannot access 'a' before initialization
        class a{ }
    </script>

3. 变量作用域

对于var和function变量来说只有函数作用域和全局作用域。

对于let/const/class存在块级作用。

对于for循环来说,如果循环变量使用var,其实循环变量是全局变量或者函数内变量;

如果循环变量使用let声明,实际生成了两层块级作用域;变量声明是一层,循环体是一层;

    <script>
        var a= []; // i是全局变量,跳出循环时i=3;
        for (var i=0; i<3; i++) {
            a[i] = function() {
                console.log(i);
            }
        };
        // 数组a包含三个函数元素
        console.log(a.toString()); 
        /*  
            function(){console.log(i)},
            function(){console.log(i)},
            function(){console.log(i)},
        */
        a[2](); // 3  
    </script>
    <script>
        var a= []; // let块级作用域变量,每次循环都相当于重新声明一个新的i变量,新的块级作用域
        for (let i=0; i<3; i++) {
            a[i] = function() {
                console.log(i);
            }
        };
        // 数组a包含三个函数元素
        console.log(a.toString()); 
        /*  
            function(){console.log(i)},
            function(){console.log(i)},
            function(){console.log(i)},
        */
        a[2](); // 2 
    </script>
    <script>
        // for循环生成两层块级作用域
        for (let i=0; i<3; i++) {// 声明循环变量的部分是外层块级作用域;会循环3次
            let i = 'hello'; // 循环体是内层块级作用域;内部作用域使用内部的块级变量
            console.log(i); 
        };
        // hello
        // hello
        // hello
    </script>

4.变量重复声明

var 和function允许重复声明,后面的会覆盖前面的;let和const不允许重复声明,会报错。

        var a = 1;
        var a = 2;
        console.log(a); // 2
// 只要出现了let声明,前后再次声明同一个变量就会报错      
 let a = 1;
 var a = 2; // Uncaught SyntaxError: Identifier 'a' has already been declared
 console.log(a); 

函数参数其实是一个隐藏的变量声明位置

        function add1(a) { //var a
            var a = 4; // 重复声明
            console.log(a); // 4
        }
        function add2(a) {// var a
            let a = 4;// Uncaught SyntaxError: Identifier 'a' has already been declared
            console.log(a); 
        }
        add1(); // 不会执行,因为在代码生成阶段就报错了
        add2(); // 不会执行

 5.顶层对象的属性

var和function在全局环境声明的变量,会成为全局对象的属性,即window对象的属性;

但是let,const, class的不会。

        var a = 100;
        console.log(window.a);//100
        let b = 10;
        console.log(window.b);//undefined
        class c{};
        console.log(window.c);//undefined
        const D = 1;
        console.log(window.D);//undefined

 6.变量命名规则

  • 汉字可以作为变量名
  • 保留字不能作为变量名
// 保留字有:
arguments、breakcasecatch、class、const、continuedebuggerdefaultdeletedoelse、enum、eval、export、
extends、falsefinallyforfunctionif、implements、import、ininstanceof、interface、let、newnull、package、private、
protected、public、return、static、super、switchthisthrowtruetrytypeofvarvoidwhilewith、yield

4.条件语句,循环语句

1)switch条件和case变量,比较是严格相等===;case后面要加break,否则会执行所有case语句;

2)break跳出所有的循环;continue语句跳出本次循环;

     如果存在多层嵌套循环,则两者都是针对最内层循环。

3)使用标签,可以配合break和continue跳出最外层循环。

❌一定注意不要把赋值表达式当成条件表达式,否则会出现死循环,因为赋值表达式永远是true;===

posted @ 2019-07-26 20:17  Lyra李  阅读(243)  评论(0编辑  收藏  举报