0%

基础知识

一、关于变量

1.1 弱类型

​ JavaScript是一种弱类型语言,声明变量时不用指定变量的类型,变量类型由赋予的值所决定。

1
2
3
4
5
6
var web = "ashun.com";
console.log(typeof web); //string
web = 99;
console.log(typeof web); //number
web = {};
console.log(typeof web); //object

1.2 变量提升

​ 代码在执行前会进行预解析,会把var形式的变量的声明提升到当前作用域(window、function)最前。

预解析示例:

​ 我们知道while是js的关键字,是不能够作为变量名称的,下面代码在解析过程中发现while不能做为变量名,没有到执行环节就出错了,这是一个很好的预解析过程的体验。

1
2
3
var web = 'ashun';
console.log(web);
let while = 'ashuntefannao'; //Uncaught SyntaxError: Unexpected token 'while'

​ 使用 var 声明代码会被提升到前面,按理来说,变量未定义就使用应该报错,但是由于变量提升的缘故,并不会报错。

1
2
3
4
5
6
7
8
9
console.log(a); //undefined
var a = 1;
console.log(a); //1

//以上代码解析器执行过程如下
var a;
console.log(a); //1
a = 1;
console.log(a); //1

下面是 if(false) 中定义的var也会发生变量提升,注释掉if 结果会不同

1
2
3
4
5
6
7
8
var web = "ashun";
function as() {
if (false) {
var web = "阿顺";
}
console.log(web);
}
as();//undefined

1.3 TDZ

​ TDZ 又称暂时性死区,指变量在作用域内已经存在,但必须在let/const声明后才可以使用。

TDZ可以让程序保持先声明后使用的习惯,让程序更稳定。

  • 变量要先声明后使用
  • 建议使用let/const 而少使用var

使用let/const 声明的变量在声明前存在临时性死区(TDZ)使用会发生错误

1
2
console.log(x); // Cannot access 'x' before initialization
let x = 1;

run函数作用域中产生TDZ,不允许变量在未声明前使用。

1
2
3
4
5
6
as = "Ashuntefannao";
function run() {
console.log(as);
let as = "Ashun";
}
run();

函数参数的解析,自左向右,下面代码b没有声明赋值不允许直接使用

1
2
function test(a = b, b = 3) {}
test(); //Cannot access 'b' before initialization

因为a已经赋值,所以b可以使用a变量,下面代码访问正常

1
2
function test(a = 2, b = a) {}
test();

二、块级作用域

​ var不具有块级作用域,ES6的let、const具有块级作用域。但是它们都具有函数、全局作用域。

2.1 共同点

​ 在父级作用域中声明的变量,能够被子级作用域访问,但子级作用域中的变量不能够被父级访问。

1
2
3
4
5
6
7
8
9
var as = "ashunte";

function test() {
var site = "ashuntefannao.com";
console.log(as);
}

test(); //ashunte
onsole.log(site);//Uncaught ReferenceError: site is not defined

2.2 let

letconst都具有块级作用域,会与{ }形成一个作用域,块级作用域与全局、函数作用域一样:在父级作用域中声明的变量,能够被子级作用域访问,但子级作用域中的变量不能够被父级访问。

​ 但letconst形成的块级作用域所定义的变量,不会污染全局, 并且letconst定义的变量全局不会添加到window中。

1
2
3
4
5
6
7
 {
let as = "ashun";
{
console.log(as);//ashun
}
}
console.log(as);//Uncaught ReferenceError: as is not defined

每一层都是独立作用域,里层作用域可以声明外层作用域同名变量,但不会改变外层变量

1
2
3
4
5
6
7
8
9
function run() {
let as = "Ashuntefannao";
if (true) {
let as = "Ashun";
console.log(as); //Ashun
}
console.log(as); //Ashuntefannao
}
run();

–var不具有块级作用域,使用 var 声明的变量存在于最近的函数或全局作用域中。

没有块作用域很容易污染全局,下面函数中的变量污染了全局环境

1
2
3
4
5
function run() {
web = "ashun";
}
run();
console.log(web); //ashun

注意:上面示例的代码中,web变量没有使用var声明,如果使用var声明,就会报错。

使用var声明报错的原因:

​ 如果使用var声明了web变量,证明web变量属于run函数的作用域。所以外部作用域不能访问该变量。

没有使用var声明:

​ 证明使用的是全局变量,变量提升在全局作用域,因此在函数外部(全局作用域)能够访问。

1
2
3
4
5
6
var web;//变量提升过程
function run() {
web = "ashun";
}
run();
console.log(web); //ashun

这个问题虽然说出来很理所当然,但是在一开始可能很难get到这个点。

–没有块作用作用域时varfor循环中也会污染全局

1
2
3
4
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); //10

var 全局声明的变量也存在于 window对象中

1
2
var as = "ashun";
console.log(window.as); //ashun

–使用let、const全局声明的变量不会添加到window

1
2
let as = "ashun";
console.log(window.as); //undefined

2.3 const

使用 const 用来声明常量,这与其他语言差别不大,比如可以用来声明后台接口的URI地址。

  • 常量名建议全部大写

  • const常量只能声明一次

  • 声明时必须同时赋值

  • 不允许再次全新赋值

  • 拥有块、函数、全局作用域

  • 可以修改引用类型变量的值

    1
    2
    3
    4
    5
    6
    const INFO = {
    url: 'https://www.ashuntefannao.com',
    port: '8080'
    };
    INFO.port = '443';
    console.log(INFO); //443
    Object.freeze

    如果冻结变量后,变量也不可以修改了,若使用严格模式还会报出错误。

    1
    2
    3
    4
    5
    6
    7
    8
    "use strict";
    const INFO = {
    url: "https://www.ashun.com",
    port: "8080",
    };
    Object.freeze(INFO);
    INFO.port = "443"; //Cannot assign to read only property
    console.log(INFO);

2.4 重复定义

  • 使用var定义变量,可在同作用域下定义同名变量进行覆盖。
  • 使用let、const定义的变量,不可在同作用域下定义同名变量

使用 var 可能造成不小心定义了同名变量

1
2
3
4
5
//优惠价
var price = 90;
//商品价格
var price = 100;
console.log(`商品优惠价格是:${price}`);//100

使用let 可以避免上面的问题,因为let声明后的变量不允许在同一作用域中重新声明

1
2
let web = 'astfn.github.io';
let web = '阿顺'; //Identifier 'web' has already been declared

不同作用域可以重新声明

1
2
3
4
5
let web = "ashun.com";
if (1) {
let web = "Ashun";
console.log(web);//Ashun
}

三、 严格模式

​ 严格模式(“use strict”),可以定义在script标签对顶部定义(范围为该标签对内),也可以在函数体顶部定义(范围为该函数内部)。

​ 严格模式的使用,可以使我们的代码更加的规范,所以强力推荐使用"use strict"

3.1 不同点

  • 变量必须先定义后使用

  • 函数形参名称不能重复

  • 关键词不允许做变量使用

  • 单独为函数设置严格模式

    1
    2
    3
    4
    5
    6
    7
    function strict(){  
    "use strict";
    return "严格模式";
    }
    function notStrict() {
    return "正常模式";
    }

    为了在多文件合并时,防止全局设置严格模式对其他没使用严格模式文件的影响,将脚本放在一个执行函数中。

    1
    2
    3
    4
    (function () {
    "use strict";
    url = 'ashun.com';
    })();
  • 解构差异

    非严格模式可以不使用声明指令,严格模式下必须使用声明。所以建议使用 let 等声明语法进行声明。

    1
    2
    {name,url} = {name:'ashun',url:'ashuntefannao.com'};
    console.log(name, url); //ashun ashuntefannao.com
    1
    2
    3
    "use strict";
    {name,url} = {name:'ashun',url:'ashuntefannao.com'};
    console.log(name, url); //Uncaught ReferenceError: age is not defined
    1
    2
    3
    "use strict";
    const {name,url} = {name:'ashun',url:'ashuntefannao.com'};
    console.log(name, url); //ashun ashuntefannao.com

四、运算符与流程控制

​ 在JavaScript中,与其他语言一样,都具有常用的运算符、流程控制语法。这里不再对其进行赘述,只对比较特殊的运算符、流程控制语法进行详细的解释。

4.1 运算符

  • 赋值运算符(=)
  • 算数运算符(+、-、*、/、%)
  • 复合运算符(+=、-=、*=、/=、%=)
  • 一元运算符(++、–)
  • 比较运算符(>、>=、<、<=、==、===)
  • 逻辑运算符(&&、||、!)

4.2 流程控制

  • if、else、else if

  • switch

  • while

  • do/while

  • for

  • break/continue

  • 三元表达式

  • for/in

    for/in循环能够遍历Array,Object的key

    1
    2
    3
    4
    let arr = ["A", "s", "h", "u", "n"];
    for (let i in arr) {
    console.log(i); //0~4
    }
    1
    2
    3
    4
    let obj = { baseURL: "http://ashuntefannao.com", port: 8081 };
    for (let i in obj) {
    console.log(i); //baseURL port
    }

    –遍历window对象的所有属性

    1
    2
    3
    for (name in window) {
    console.log(window[name]);
    }
  • for/of

    for/of,能够遍历所有的可迭代数据结构,例如:Dom元素、Array、String、Set、Map。

    但是Object、WeakSet、WeakMap是不可迭代的,因此也就不能够使用for/in进行遍历

    1
    2
    3
    4
    let arr = [1, 2, 3];
    for (const iterator of arr) {
    console.log(iterator);
    }
    1
    2
    3
    4
    let str = 'Ashun';
    for (const iterator of str) {
    console.log(iterator);
    }

    --使用迭代特性遍历数组,后期在迭代器部分会详解。

    1
    2
    3
    4
    5
    let site = ["ashun", "Ashuntefannao"];
    for (const [key, value] of site.entries()) {
    console.log(key, value);
    }
    //0 ashun 1 Ashuntefannao

    使用for/of 也可以用来遍历DOM元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <body>
    <ul>
    <li></li>
    <li></li>
    </ul>
    </body>
    <script>
    let lis = document.querySelectorAll("li");
    for (const li of lis) {
    li.addEventListener("click", function() {
    this.style.backgroundColor = "red";
    });
    }
    </script>
  • lable

    标签(label) 为程序定义位置,可以使用continue/break跳到该位置。

    下面取i+n 大于15时退出循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ashuntefan: for (let i = 1; i <= 10; i++) {
    ashun: for (let n = 1; n <= 10; n++) {
    if (n % 2 != 0) {
    continue ashun;
    }
    console.log(i, n);
    if (i + n > 15) {
    break ashuntefan;
    }
    }
    }