zoukankan      html  css  js  c++  java
  • JS基础与查缺

    JS进阶与查缺(上)

    基础知识、常用方法+ES6的一些新知识。

    01 变量的声明

    以前我们在声明变量时只有一种方法,就是使用var来进行声明,甚至于都没有常量的声明。

    而ES6对声明的进行了扩展,现在可以有三种声明方式了——varletconst

    先稍微解释一下:

    1. var:很明显,它是variable的简写,可以理解成变量的意思;
    2. let:也可以理解为一种声明的意思,即为“让”xxx变量存放xxx值;
    3. const:在英文中是常量的意思,在ES6也是用来声明常量的。

    1.1 var

    var在ES6里和之前是一样的,他会升级全局变量。(这个升级可不是好事,和游戏里的打怪升级是不一样的)

    那什么是升级全局变量呢?

    我们可以先作一个最简单的实例,用var声明一个变量a,然后用console.log进行输出:

    var a = "hello world";
    console.log(a);
    

    我们可以看到hello world在控制台已经被打印出来了。

    那么接下来,我们使用匿名函数给他进行一个包裹,然后再在匿名函数中调用这个a变量,看看能不能调用到:

    var a = 'hello world';
    // 这里尽量不要使用window.onload,因为这样如果同时声明了两处,会导致冲突
    // 所以能用addEventListener就用
    window.addEventListener("load", function() {
        console.log(a);
    })
    

    可以看到控制台输出了"hello world",这证明var确实是全局的

    我们再试试:

    var a=2;
    {
       var a=3;
    }
    console.log(a);
    

    这时打印出来的值是多少呢?对,应该是3,因为var是全局声明的!!!

    1.2 let

    基本上是个敲过两天代码的都看得出来,var没有块级作用域是多么的恶心人,所以我们引入了let。

    我们来试着在区块里用let声明:

    var a = 2;
    {
       let a = 3;
    }
    console.log(a);
    

    这时候控制台打印出来的值就是2了!

    但是如果我们只在区块里声明,不在外部声明,我们打印a时就会报错,显示找不到变量:

    {
       let a=3;
    }
    console.log(a);
    

    用var声明的循环:

    for(var i = 0; i < 10; i++){
    	console.log('循环体中:' + i);
    }
    console.log('循环体外:' + i);
    

    如果在浏览器里粘贴这段代码,就能看到,最后一条输出居然是“循环体外:10”!

    这两个例子说明了let是局部变量声明,只在区块内起作用,外部是不可以调用的。它不会像var一样疯狂污染你的全局命名空间。

    所以我们应该用let声明,彻底代替var声明(除非万不得已),去避免污染全局空间。

    1.3 const

    在程序开发中,有些变量是希望声明后在业务层就不再发生变化了。简单来说就是从声明开始,这个变量始终不变,那么就需要用const进行声明。

    我们线来一段用const声明错误的代码:

    const a = "hello world";
    var a = "hello China";
    console.log(a);
    

    在编译这段代码的过程中,你就会发现已经报错,无法编译了!

    原因就是我们const声明的变量是不可以改变的。


    02 函数

    JS中函数属于JS中的“上流社会”,具有很强的抽象能力,我们可以像变量一样去使用她。

    2.1 函数的定义

    方式一

    function abs(x) {
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
    

    方式二

    let abs = function (x) {
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    };
    

    方式三:Arrow Function

    箭头函数,顾名思义,就是看起来像个箭头:x => x * x

    x => x * x 等价于:function (x) { return x * x; }

    1. 参数等于一个,可以省略括号;
    2. 语句等于一条,可以省略大括号。

    关于箭头函数的this

    箭头函数的this指向和之前的function() {}声明不一样,function的this指向是:

    1. this指向调用此函数的对象;
    2. 如果函数用作构造函数,那么this指向构造函数创建的对象实例。

    箭头函数则是不创建this:在声明时,捕获作用域内的this(就近原则)。

    2.2 函数的调用

    我们都知道立即执行函数:

    function foo() {
      
    }()
    

    只需要在后面加上括号即可。

    同样的,如果说我们在绑定click事件时:

    xxxx.addEventListener("click", function() {
      
    }())
    

    他也会立即执行,任何时候,加一个括号,都会立即执行!

    2.3 函数的参数

    JS的函数调用允许传入任意多的参数——无论是比定义时多还是少,都不影响函数调用。

    例如:

    let abs = x => x * 50;
    abs(2); // 100
    abs(2, 5, "hello"); // 100
    abs(); // NaN
    

    为了避免得到一个NaN,可以对参数进行检查:

    function abs(x) {
        if (isNaN(x)) {
            throw 'Not a number'; // 自定义一个报错
          	// 或者return "wrong"
        }
    }
    

    2.3.1 arguments

    在传入参数之后,JS会帮我们将所有的参数都放进一个类似数组的东西里面,她就是arguments。

    他酷似数组,但不是数组。

    function abs() {
        console.log(arguments)
    }
    
    abs(); // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
    abs(10, "hello"); // Arguments [10, "hello", callee: ƒ, Symbol(Symbol.iterator): ƒ]
    

    这也就意味着,我们可以在不定义参数的情况下,拿到传入的所有参数,并通过arguments[i]的形式来调用。

    arguments最常见的用法,是利用他完成类似重载的功能——因为JS没有重载这个概念。

    function abs(a, b, c) {
      if(arguments.length == 1){
        console.log(a);
      } else if(arguments.length == 2) {
        console.log(a, b);
      } else if(arguments.length == 3) {
        console.log(a, b, c);
      } else {
        console.log("lalala")
      }
    }
    

    2.3.2 rest

    我们如何拿到函数调用时多的参数?

    function foo(a, b) {
        let i;
        let rest = [];
        if (arguments.length > 2) {
            for (i = 2; i<arguments.length; i++) {
                rest.push(arguments[i]); // 如果多出两个参数,就把多出来的放入rest数组
            }
        }
    }
    

    这样非常的麻烦,庆幸的是在ES6标准中我们引入了rest参数:

    function foo(a, b, ...rest) {
        console.log(rest);
    }
    
    foo(1, 2, 3, 4, 5); // Array [ 3, 4, 5 ]
    
    foo(1); // Array []
    

    rest参数只能作为最后一个参数,前面用...标识。

    扩展运算符

    看着...rest,有没有很好奇...究竟是什么?

    在Swift中,...用来表示一个闭区间,a...b表示 [a,b] 区间。

    而JS中,...叫做扩展运算符,是ES6的新特性,可以非常好的增强代码的可读性。

    下面来看几个例子。

    1. 函数传参

    function foo (a, b, c) {
      console.log(a);
      console.log(b);
      console.log(c);
    }
    
    let arrray = [1, 2, 3];
    foo(...array); // 等价于foo(array[0], array[1], array[2])
    

    2. 数组和对象的拷贝

    扩展运算符大幅度减少拷贝所需代码量。

    数组:

    let a = [1, 2, 3];
    let b = [...a];
    
    b[0] = 8;
    console.log(a); // [1, 2, 3]
    console.log(b); // [8, 2, 3]
    

    对象:

    let a = {
      name: "lalala",
      age: 12,
      parents: ["dad", "mom"]
    };
    let b = {...a};
    
    b.parents[0] = "dilidili";
    b.age = 100;
    console.log(a); // age:12, parents: ["dilidili"]
    console.log(b); // age:100, parents: ["dilidili"]
    

    这里我们可以看到,扩展运算符其实只遍历一层,如果有多层嵌套,第二层往里就会变成浅复制。

    3. 构造字面量数组

    在没有展开语法的时候,只能组合使用 push, splice, concat 等方法,来将已有数组元素变成新数组的一部分。

    利用扩展运算符,让数组的拼接一目了然:

    let arr1 = [1,2,3];
    let arr2 = [4,5,...arr1]; // [4, 5, 1, 2, 3]
    arr1 = [...arr1, ...arr2]; // [1, 2, 3, 4 , 5, 1, 2, 3]
    

    4. 拆分字符串

    ...也可以将字符串拆分成单个字符。

    let str = "hello";
    let a = [...str]; // ["h", "e", "l", "l", "o"]
    

    5. 作为rest参数和扩展运算符时易搞混

    let foo = (...m) => fn(...m);
    let fn = (a, b) => console.log(a+b);
    foo(1,2);
    

    其中第一个...m收集了传入的参数,将1和2放入了数组m = [1, 2]

    第二个...mm = [1, 2],进行扩展运算,将1和2作为形参传入fn函数。

    2.3.4 默认参数

    若传入该参数,则取传入的值;若为传入该参数,则取默认值。

    let fn = (a, b = 5, c = 12) => {
      console.log(a);
      console.log(b);
      console.log(c);
    }
    
    fn(1); // a = 1, b = 5, c = 12
    fn(1, 2, 3); // a = 1, b = 2, c = 3
    

    03 解构赋值

    一般来说,如果我们let a = [1, 2, 3],那么我们要将每一个元素分别赋值给不同的变量,我们需要:

    let a = [1, 2, 3];
    let x = a[0];
    let y = a[1];
    let z = a[2];
    

    ES6之后我们可以使用解构赋值来完成这些冗杂的操作。

    解构赋值注意事项:

    1. 左右两边必须一致;
    2. 声明必须合法;
    3. 声明与赋值同步进行。

    3.1 数组的解构赋值

    let a = [1, 2, 3];
    let [x, y, z] = a;
    x; // 1
    y; // 2
    z; // 3
    

    如果有嵌套关系:

    let a = [1, [2, 3]];
    let [x, [y, z]] = a;
    x; // 1
    y; // 2
    z; // 3
    

    3.2 对象的解构赋值

    解构赋值可以用于对象:

    let a = {
        name: "孙悟空",
        age: 18
    };
    let {age, name} = a;
    let {lala, lili} = a;
    name; // "孙悟空"
    age; // 18
    lala; // undefined
    lili; // undefined
    

    可以看出来,对象的解构赋值是根据属性名来判断的,如果属性名对不上。

    3.2.1 如果不希望用原属性名

    let person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678',
        school: 'No.4 middle school'
    };
    
    // 把passport属性赋值给变量id:
    let {name, passport:id} = person;
    passport; // undefined
    id; // 'G-12345678'
    

    同时也可以看出,对象的解构赋值不仅不需要一一对应,而且不需要数量相同。

    3.2.2 可以直接赋值

    let person = {
        name: '小明',
        age: 20,
    };
    
    // 如果person对象没有single属性,默认赋值为true:
    let {name, single=true} = person;
    name; // '小明'
    single; // true
    

    3.2.3 如果有嵌套关系

    let person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678',
        school: 'No.4 middle school',
        address: {
            city: 'Beijing',
            street: 'No.1 Road',
            zipcode: '100001'
        }
    };
    let {name, address: {city, zip}} = person;
    

    3.3 另外

    let x, y;
    {x, y} = { name: '小明', x: 100, y: 200};
    

    会报错!

    因为JS把 { } 解析到了一起,代码就变成了:x, y} = { name: '小明', x: 100, y: 200

    解决方案:添加括号。

    let x, y;
    ({x, y} = { name: '小明', x: 100, y: 200});
    

    04 数组

    in的用法

    in可以解决一个ES5判断的弊端。

    以前会使用length属性进行判断,为0就表示没有数组元素。但是这并不准确。

    let arr=[,,,,,];
    console.log(arr.length); //5
    

    上边的代码输出了5,但是数组中其实全是空值,这就是一个坑!

    用ES6的in就可以解决这个问题。

    let arr=[,,,,,];
    console.log(0 in arr); //false
    
    let arr1=['xhy','lh'];
    console.log(0 in arr1);  // true
    console.log('xhy' in arr1); // false
    

    注意,这里表示脚标位置是否有元素

    也可以用来判断对象的key值:

    let obj = {
        a: 'xhy',
        b: 'lh'
    }
    console.log('a' in obj);  // true
    // 注意,不是判断值,而是key值
    console.log('xhy' in obj); // false
    

    join()方法

    let arr=['xhy','lh','lalalala'];
    
    console.log(arr.join('|'));
    

    join()方法就是在数组元素中间,加了一些间隔,和toString方法比较像,但toString转换时是用逗号隔开的,join我们可以自定义。

    toString()方法:

    let arr=['xhy','lh','forever'];
    
    console.log(arr.toString());
    

    Array.of()方法

    它负责把一堆文本或者变量转换成数组。

    在开发中我们经常拿到了一个类似数组的字符串,需要使用eval来进行转换,但是不得不说,eval的效率是很低的,它会拖慢我们的程序。

    这时候我们就可以使用Array.of方法。我们看下边的代码把一堆数字转换成数组并打印在控制台上:

    let arr = Array.of(3,4,5,6);
    console.log(arr);
    

    当然它不仅可以转换数字,字符串也是可以转换的,看下边的代码:

    let arr = Array.of('xhy','lh','lalalala');
    console.log(arr);
    

    find()实例方法

    所谓的实例方法就是并不是以Array对象开始的,而是必须有一个已经存在的数组,然后使用的方法,这就是实例方法。

    这里的find方法是从数组中查找。

    在find方法中需要传入一个匿名函数,函数需要传入三个参数:

    • value:表示当前查找的值。
    • index:表示当前查找的数组索引。
    • arr:表示当前数组。

    在函数中如果找到符合条件的数组元素就进行return,并停止查找。

    我们来尝试一下:

    let arr = [1,2,3,4,5,6,7,8,9];
    console.log(arr.find(function (value, index, arr) {
        return value > 5; // 返回一个布尔值
    }))
    

    控制台输出了6,说明找到了符合条件的值,并进行返回了,如果找不到会显示undefined。

    fill()实例方法

    fill()也是一个实例方法,它的作用是把数组进行填充。

    它接收三个参数,第一个参数是填充的变量,第二个是开始填充的位置,第三个是填充到的位置。

    let arr = [0,1,2,3,4,5,6,7,8,9];
    arr.fill('xhy', 2, 5);
    console.log(arr);
    

    上边的代码是把数组从第二位到第五位用xhy进行填充。

    数组的遍历

    for…of循环:

    这种形式比ES5的for循环要简单而且高效。先来看一个最简单的for…of循环:

    let arr=['xhy', 'lh', 'forever']
    for (let item of arr){
        console.log(item);
    }
    

    for…of数组索引:

    有时候开发中是需要数组的索引的,那我们可以使用下面的代码输出数组索引:

    let arr=['xhy', 'lh', 'forever']
    for (let index of arr.keys()){
        console.log(index);
    }
    

    可以看到这时的控制台就输出了0,1,2,也就是数组的索引。

    同时输出数组的内容和索引:

    我们用entries()这个实例方法,配合我们的for…of循环就可以同时输出内容和索引了。

    let arr=['xhy', 'lh', 'forever']
    for (let [index,val] of arr.entries()){
        console.log(index + ':' + val);
    }
    

    entries()实例方式生成的是Iterator形式的数组,那这种形式的好处就是可以让我们在需要时用next()手动跳转到下一个值。我们来看下面的代码:

    let arr=['xhy', 'lh', 'forever']
    let list=arr.entries();
    console.log(list.next().value);
    console.log(list.next().value);
    console.log(list.next().value);
    

    除了for循环的形式,更推荐使用以下的方法进行遍历:

    1. forEach()

    let arr=['xhy','lh','like'];
    
    arr.forEach((val,index) => console.log(index,val));
    

    forEach循环的特点是会自动省略为空的数组元素,相当于直接给我们筛空了。有时候很好,但有时候也会给我们帮倒忙。

    2. filter()

    创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素:

    let arr=['xhy','lh','like'];
    
    arr.filter(x => console.log(x));
    

    3. some

    some会依次执行数组的每个元素:

    • 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测;
    • 如果没有满足条件的元素,则返回false。
    let arr=['xhy','lh','like'];
    
    arr.some(x => console.log(x));
    

    4. map

    通过函数设定规则,根据规则替换:

    let arr=['xhy','lh','like'];
    
    console.log(arr.map(x => 'web'));
    

    我在网上看到过一张图,很好的说明了map() filter()之间的关系。

    filter,相当于把你想要的菜挑出来;而map相当于把菜做成菜。

    JSON数组格式转换

    JSON的数组格式就是为了前端快速的把JSON转换成数组的一种格式,我们先来看一下JSON的数组格式怎么写。

    let  json = {
        '0': 'xhy',
        '1': 'lh',
        '2': 'forever',
        length: 3
    }
    

    这就是一个标准的JSON数组格式,跟普通的JSON对比是在最后多了一个length属性。

    只要是这种特殊的json格式都可以轻松使用ES6的语法转变成数组:

    let  json = {
        '0': 'xhy',
        '1': 'lh',
        '2': 'forever',
        length: 3
    }
    
    let arr = Array.from(json);
    console.log(arr)
    

    打印结果:["xhy", "lh", "forever"]


    05 字符串

    两个新方法

    1. starts With

    let str = "https://www.baidu.com";
    if(str.startsWith("https://")) {
      console.log("安全网址");
    } else if(str.startsWith("http://")) {
      console.log("普通网址");
    } else if(str.startsWith("git://")) {
      console.log("git地址");
    } else if(str.startsWith("svn://")) {
      console.log("svn地址");
    } else {
      console.log("其他");
    }
    

    判断该字符串开头是不是"https://"。

    2. endsWith

    let str = "xhy.txt";
    if(str.endsWith(".txt")) {
      console.log("文本文档");
    } else if(str.startsWith(".jpg")) {
      console.log("jpg图片");
    } else if(str.startsWith("git://")) {
      console.log("git地址");
    } else if(str.startsWith("svn://")) {
      console.log("svn地址");
    } else {
      console.log("其他");
    }
    

    字符串模板

    let a = 12;
    let str = `abc${a}dd`
    

    可以用于连接字符串:

    let content = "hello world";
    let text = "nihao";
    let str = `<div>${content}</div>
    					 <p>${text}</p>`
    

    06 关于JS的数字

    二进制声明:

    二进制的英文单词是Binary,二进制的开始是0(零),然后第二个位置是b(注意这里大小写都可以实现),然后跟上二进制的值就可以了。

    let binary = 0B010101;
    console.log(binary);
    

    这时候浏览器的控制台显示出了21

    八进制声明

    八进制的英文单词是Octal,也是以0(零)开始的,然后第二个位置是O,然后跟上八进制的值就可以了。

    let b=0o666;
    console.log(b);
    

    这时候浏览器的控制台显示出了438。

    如果,后面的数字超出了八进制的范围,浏览器会自动忽略前面的0o,当作十进制处理!

    数字验证Number.isFinite(xx)

    可以使用Number.isFinite()来进行数字验证,只要是数字,不论是浮点型还是整形都会返回true,其他时候会返回false。

    let a= 11/4;
    console.log(Number.isFinite(a));//true
    console.log(Number.isFinite('jspang'));//false
    console.log(Number.isFinite(NaN));//false
    console.log(Number.isFinite(undefined));//false
    

    NaN验证

    可以使用Number.isNaN()来进行验证。下边的代码控制台返回了true。

    console.log(isNaN(NaN)); // true
    console.log(isNaN('A')); // true
    console.log(isNaN('1')); // false 因为会自动转化为数字
    

    判断是否为整数

    Number.isInteger(xx)判断是否为整数。

    let a=123.1;
    console.log(Number.isInteger(a)); //false
    

    整数转换浮点型转换

    Number.parseInt(xxx)Number.parseFloat(xxx)

    let a='9.18';
    console.log(Number.parseInt(a)); 
    console.log(Number.parseFloat(a));
    

    整数取值范围操作

    整数的操作是有一个取值范围的,它的取值范围就是2的53次方。我们先用程序来看一下这个数字是什么.

    let a = Math.pow(2,53)-1;
    console.log(a); //9007199254740991
    

    在我们计算时会经常超出这个值,所以我们要进行判断,ES6提供了一个常数,叫做最大安全整数,以后就不需要我们计算了。

    最大安全整数

    console.log(Number.MAX_SAFE_INTEGER);
    

    最小安全整数

    console.log(Number.MIN_SAFE_INTEGER);
    

    安全整数判断isSafeInteger( )

    let a= Math.pow(2,53)-1;
    console.log(Number.isSafeInteger(a));//false
    
  • 相关阅读:
    Bean作用域
    处理自动装配的歧义
    IOC/DI 依赖注入
    Session管理
    JSP
    Jenkins(持续集成)Windows版本安装
    Jenkins安装插件提速(Windows)终极解决办法
    Jenkins(持续集成)Windows版本解决插件安装缓慢
    Please wait while Jenkins is getting ready to work...(Jenkins访问资源慢的问题)
    Spring Boot 小技巧
  • 原文地址:https://www.cnblogs.com/xhyccc/p/12707411.html
Copyright © 2011-2022 走看看