《JavaScript 教程》笔记 + 代码练习

js在视频和菜鸟入坑之后,进入了一个卡顿期,边学ES6边看红宝书《Javascript高级程序设计》【我吐槽一下,这书明明得入坑ES6一段时间后看,才是进步最快的哇?好哇!还是太菜,今年重来,我马上能行!】

21年年末绝对发现一本JS入坑一段时间就可以看的书籍,我太太太感谢阮一峰老师了!!!
这书绝对大赞一波,我真真正正见识到了this,闭包,原型链,对象,AJAX,单线程,事件处理等等!!!
这绝对是走向JS高深之路的必读书籍啊,太推了呀,相见恨晚!一定得多读几遍,呜呜呜

电子书链接:JavaScript 教程 - 网道 (wangdoc.com)

文章标题

0序: 练习代码

1. 数组.html

```html
<!DOCTYPE html>
Document ```

2、 console对象

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div class="aaa">
        <ul>
            <li>dejad</li>
            <li></li>
            <li></li>
        </ul>
    </div>
    <script>
        console.log(
            '%cThis text is styled!',
            'color: red; background: yellow; font-size: 24px;'
        )

        console.info('你好');
        console.warn('警告⚠');
        console.error('错了,个');

        // ['log', 'info', 'warn', 'error'].forEach(function(method) {
        //     console[method] = console[method].bind(
        //         console,
        //         new Date().toISOString()
        //     );
        // });
        // console.log("出错了!");

        console.error('Error: %s (%i)',
                'Server is not responding', 500)
            // Error: Server is not responding (500)
        console.warn('Warning! Too few nodes (%d)', document.childNodes.length)
            // Warning! Too few nodes (1)
        var languages = [{
            name: "JavaScript",
            fileExtension: ".js"
        }, {
            name: "TypeScript",
            fileExtension: ".ts"
        }, {
            name: "CoffeeScript",
            fileExtension: ".coffee"
        }];

        console.table(languages);
        console.dir({
            f1: 'foo',
            f2: 'bar'
        })

        // console.dir(obj, {
        //     colors: true
        // })

        console.dirxml(document.body)
        console.log(document.body);

        //<!--assert-->
        console.assert(false, '判断条件不成立');
        // 相当于
        try {
            if (!false) {
                throw new Error('判断条件不成立');
            }
        } catch (e) {
            console.error(e);
        }
    </script>
</body>

</html>

0-3 Object对象

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <script>
        // Object对象的方法
        Object.print = function(o) {
            console.log(o);
        }

        // Object 的实例方法
        Object.prototype.print = function() {
            console.log(this);
        }
        var obj = new Object();
        obj.print()
    </script>
</body>

</html>

0-4 arr数组.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var list = [1, 2, 3, 4];
        var item;

        while (item = list.shift()) {
            console.log(item);
        }

        list // []

        var a = ['a', 'b', 'c', 'd', 'e', 'f'];
        a.splice(4, 2, 1, 2) // ["e", "f"]
        console.log(a); // ["a", "b", "c", "d", 1, 2]

        var a = ['a', 'b', 'c', 'd', 'e', 'f'];
        a.splice(-4, 2) // ["c", "d"]
        console.log(a);

        var out = [];

        [1, 2, 3].forEach(function(elem) {
            this.push(elem * elem);
        }, out);
        console.log(out);

        // filter()方法用于过滤数组成员,满足条件的成员组成一个新数组
        var b = [1, 2, 3, 4, 5].filter(function(elem) {
            return (elem > 3);
        })
        console.log(b);
        // 返回数组arr里面所有布尔值为true的成员
        var arr = [0, 1, 'a', false];
        var temarr = arr.filter(Boolean);
        console.log(temarr, arr);

        // filter方法可以接受三个参数 : elem,index和arr
        var c = [1, 2, 3, 4, 5].filter(function(elem, index, arr) {
            return index % 2 === 0;
        })
        console.log(c);

        // filter() 方法可以接受第二个参数, 用来绑定函数内部的this变量
        var obj = {
            MAX: 3
        };
        var myFilter = function(item) {
            if (item > this.MAX) return true;
        };
        var arr = [2, 8, 3, 4, 1, 3, 2, 9];
        console.log(arr.filter(myFilter, obj));
    </script>
</body>

</html>

0-5 string.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 生成随机字符的例子
        function random_str(length) {
            var ALPHABET = 'ABCDEFGHIGKLMNOPQRSTUVWXYZ';
            ALPHABET += 'abcdefghijklmnopqrstuvwxyz';
            ALPHABET += '0123456789-_';
            var str = '';
            for (var i = 0; i < length; ++i) {
                var rand = Math.floor(Math.random() * ALPHABET.length);
                str += ALPHABET.slice(rand, rand + 1);

            }
            return str;
        }

        console.log(random_str(6));

        var a = 'The quick for jumped over the lazy dog.';
        var pattern = /quick|brown|lazy/ig;

        var d = a.replace(pattern, function replacer(pattern) {
            return pattern.toUpperCase();
        })
        console.log(d);
    </script>
</body>

</html>

0-6 this指向.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var obj = {
            foo: function() {
                console.log(this);
            }
        };

        console.log(obj.foo()); // obj
        console.log(obj.foo);
    </script>
</body>

</html>

一、 入门篇

1-1导论+js简介

1-1-1 简介

  1. 轻量级脚本,嵌入式语言,
    • 嵌入JS的宿主环境有浏览器,服务器环境,Node项目
  2. 语法角度:
    • JavaScript 语言是一种“对象模型”语言
    • JavaScript 并不是纯粹的“面向对象语言”,还支持其他编程范式(比如函数式编程)
  3. JavaScript 的核心语法部分相当精简,只包括两个部分:基本的语法构造(比如操作符、控制结构、语句)和标准库(就是一系列具有各种功能的对象比如ArrayDateMath等)。除此之外,各种宿主环境提供额外的 API(即只能在该环境使用的接口),以便 JavaScript 调用。以浏览器为例,它提供的额外 API 可以分成三大类。
    • 浏览器控制类:操作浏览器
    • DOM 类:操作网页的各种元素
    • Web 类:实现互联网的各种功能

1-1-2 历史

  1. 1995年,网景(Netscape)开发JS【Ns与Sun公司】联合发布: 对外宣布JS是JAva的补充,,属于轻量级的Java,专门用于操作网页
  2. 1996年,Navigator2.0
  3. 1996,微软模仿JS开发Jscript;Ns讲JS提交给ECMA(European Computer Manufacturers Association)
  4. 1997年,ECMA发布262号标准文件的第一版,规定了浏览器脚本语言的标准

二、数据类型

1-1 数据类型

1-1- 1 数据类型

  1. 六种数据类型:

    • number
    • string
    • boolean
    • undefined
    • null
    • object

1-1-2 确定值是什么类型(3)

  1. typeof

    typeof window // "object"
    typeof {} // "object"
    typeof [] // "object"
    typeof null // "object"
    
  2. instanceof

    var a = []
    a instanceof Array; // true
    
  3. Object.prototype.toString

1-1-3 数据类型详解

1-1-3-1 null,undefined和布尔值
  1. null是一个表示“空”的对象,转为数值时为0undefined是一个表示"此处无定义"的原始值,转为数值时为NaN
Number(undefined) // NaN
5 + undefined // NaN
  1. undefined的典型场景

    // 变量声明了,但没有赋值
    var i;
    i // undefined
    
    // 调用函数时,应该提供的参数没有提供,该参数等于 undefined
    function f(x) {
      return x;
    }
    f() // undefined
    
    // 对象没有赋值的属性
    var  o = new Object();
    o.p // undefined
    
    // 函数没有返回值时,默认返回 undefined
    function f() {}
    f() // undefined
    
  2. 转换规则是除了下面六个值被转为false,其他值都视为true

    • undefined
    • null
    • false
    • 0
    • NaN
    • ""''(空字符串)
    if ([]) {
      console.log('true');
    }
    // true
    
    if ({}) {
      console.log('true');
    }
    // true
    
1-1-3-2 数值
  1. JS底层根本没有整数

    1 === 1.0 // true
    
  2. 精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。

    • 如果一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity
    Math.pow(2, 1024) // Infinity
    
    • 如果一个数小于等于2的-1075次方(指数部分最小值-1023,再加上小数部分的52位),那么就会发生为“负向溢出”,即 JavaScript 无法表示这么小的数,这时会直接返回0。
    Math.pow(2, -1075) // 0
    
  3. JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的具体的最大值和最小值。

    Number.MAX_VALUE // 1.7976931348623157e+308
    Number.MIN_VALUE // 5e-324
    
  4. 数值的进制:

    • Oo前缀: 八进制
    • Ox:十六进制
    • Ob: 二进制
  5. JS特殊值:NaN,Infinity

    typeOf undefined = NaN
    typeof NaN = 'number'
    NaN === NaN // false
    
1-1-3-3 与数值相关的全局方法
  1. parseInt(): 字符串转化为整数, 结果只有NaN / 整数

    parseInt('123') // 123
    
    • 如果parseInt的参数不是字符串,则会先转为字符串再转换。

      parseInt(1.23) // 1
      // 等同于
      parseInt('1.23') // 1
      
    • 可以解析------> 十进制,不认识科学计数法的数字

    • 可以两个参数:::参数2: 指定进制

  2. parseFloat(): 将一个字符串转为浮点数。

  3. isNaN():

  4. isFinite(): 某个值是否为正常的数值

1-1-3-4 字符串
  1. 字符串分成多行,须在尾部使用反斜杠
var longString = 'Long \
long \
long \
string';

longString
// "Long long long string"
  1. 反斜杠还有三种特殊用法:

    • 三个八进制数(000-377),代表一个字符

    • \x后面紧跟两个十六进制数(00FF),代表一个字符。

    • \u后面紧跟四个十六进制数(0000FFFF),代表一个字符。

      '\251' // "©"
      '\xA9' // "©"
      '\u00A9' // "©"
      
      '\172' === 'z' // true
      '\x7A' === 'z' // true
      '\u007A' === 'z' // true
      
  2. 无法改变字符串中的单个字符

1-1-3-5 对象
  1. 键名都是字符(数字会被自动转化为字符串)

  2. 每个“键名”又称为“属性”

  3. 表达式还是语句:

    • 这种差异在eval语句(作用是对字符串求值)中反映得最明显。

      eval('{foo: 123}') // 123
      eval('({foo: 123})') // {foo: 123}
      
  4. 属性的操作:

    • 读取属性使用点运算符,方括号运算符

      • var obj = {
          p: 'Hello World'
        };
        
        obj.p // "Hello World"
        obj['p'] // "Hello World"
        
      • 数字键名不能使用点运算符,只能使用方括号运算符

  5. 属性的查看:

    • 使用Object.keys

    • var obj = {
        key1: 1,
        key2: 2
      };
      
      Object.keys(obj);
      // ['key1', 'key2']
      
  6. 属性是否存在:

    • in运算符: 无法识别哪些属性是继承的
      • toString()
    • 使用对象的hasOwnProperty方法判断一下,是否是对象自身的属性
    var obj = {}if ('toString' in obj){
        log(obj.hasOwnProperty('toString')) // false
    }
    
  7. 属性的遍历: for…in…循环

    var obj = {a:1, b:2, c:3};
    
    for(var i in obj){
        log('键名', i);
        log('键值',obj[i]);
    }
    
    var obj = {};
    for(var i in obj){
    if(obj.hasOwnProperty(key)){
    log(key);
    }
    }
    
  8. with语句: 操作同一个对象的多个属性时【不要用,我看了个寂寞,去了】

    • with区块内部有变量的赋值操作,必须是当前对象已经存在的属性
    • 否则会创造一个当前作用域的全局变量
    var obj = {
        p1: 1,
        p2: 2}
    with(obj){
        p1 = 4;
        p2 = 5;
    }
    
    console.log(document.links[0].href);
    console.log(document.links[0].title);
    console.log(document.links[0].style);
    // 例二
    with(document.links[0]){
        console.log(href);
        console.log(href)
        console.log(href)
    }
    
  9. 建议使用临时变量替代with

    with(obj1.obj2.obj3) {
      console.log(p1 + p2);
    }
    
    // 可以写成
    var temp = obj1.obj2.obj3;
    console.log(temp.p1 + temp.p2);
    

1-2 函数

1-2-1 概述

1-2-1-1 函数的声明(3)
  1. function函数:

    function print(s) {
      console.log(s);
    }
    
  2. 函数表达式:

    var print = function(s) {
        log(s);
    }
    
    • 将一个匿名函数赋值给变量
    • 该匿名函数又称函数表达式
    var f = function f() {};
    
  3. function构造函数(不用)

  4. 递归计算斐波那契数列

    function fib(num) {
        if(num === 0) return 0;
        if(num === 1) return 1;
        return fib(num - 2) = fib(num - 1);
    }
    
    fib(6)
    
1-2-1-2 函数的属性和方法
  1. toString() : 函数的toString()方法返回一个字符串,内容是函数的源码。

    • 注释内容也可以返回
  2. 对于那些原生的函数,toString()方法返回function (){[native code]}

    Math.sqrt.toString()
    // "function sqrt() { [native code] }"
    
  3.   <script>
            var multiline = function(fn) {
                console.log(fn);
                var arr = fn.toString().split('\n');
                console.log(arr);
                console.log(arr.slice(1, arr.length - 1).join('\n'));
            };
    
            function f() {
                WaveShaperNodedf
                fsf
            }
            multiline(f);
        </script>
    
    ƒ f() {
                WaveShaperNodedf
                fsf
            }
    25.集合实践.html:26 (4) ["function f() {", "            WaveShaperNodedf", "            fsf", "        }"]
    25.集合实践.html:27             WaveShaperNodedf
                fsf
    
    1-2-1-3 函数本身的作用域
    1. 函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关

      • 总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。
      var a = 1;
      var x = function () {
        console.log(a);
      };
      
      function f() {
        var a = 2;
        x();
      }
      
      f() // 1
      
      var x = function () {
        console.log(a);
      };
      
      function y(f) {
        var a = 2;
        f();
      }
      
      y(x)
      // ReferenceError: a is not defined
      
1-2-1-3 arguments对象
  1. arguments可以在函数体内部读取所有参数

  2. arguments对象可以在运行时修改

    • 严格模式下不会影响到实际的函数参数
    var f = function(a, b) {
      arguments[0] = 3;
      arguments[1] = 2;
      return a + b;
    }
    
    f(1, 1) // 5
    
  3. 通过arguments对象的length属性,可以判断函数调用时到底带几个参数。

    function f() {
      return arguments.length;
    }
    
    f(1, 2, 3) // 3
    f(1) // 1
    f() // 0
    
  4. arguments是一个对象,不是数组

    • 转化为 一个数组

      var args = Array.prototype.slice.call(arguments);
      
      // 或者
      var args = [];
      for (var i = 0; i < arguments.length; i++) {
        args.push(arguments[i]);
      }
      
1-2-1-4 闭包

JS 语言的一个特色 + 难点

  1. 全局作用域+ 函数作用域
    • 函数内部读取全局变量
    • 函数外部无法读取函数内部声明的变量
  2. 链式作用域结构(chain scope):
    • 子对象会一级一级向上寻找父对象的变量
    • 父对象的所有变量,对子对象都是可见的,反之不成立
  3. 闭包的用处:
    • 可以读取外层函数内部的变量
    • 让这些变量始终保持的在内存中

1-3 数组

1-3-1 数组的简介

  1. 数组的特殊性体现在,它的键名是按次序排列的一组整数(0,1,2…)。

  2. var arr = ['a', 'b', 'c'];
    
    Object.keys(arr)
    // ["0", "1", "2"]
    
  3. JavaScript 使用一个32位整数,保存数组的元素个数。这意味着,数组成员最多只有 4294967295 个(232 - 1)个

  4. in运算符

    var arr = [ 'a', 'b', 'c' ];
    2 in arr  // true
    '2' in arr // true
    4 in arr // false
    

    上面代码表明,数组存在键名为2的键。由于键名都是字符串,所以数值2会自动转成字符串。

1-3-2 数组的遍历

  1. 数组的forEach方法,也可以用来遍历数组,详见《标准库》的 Array 对象一章。
    
    var colors = ['red', 'green', 'blue'];
    colors.forEach(function (color) {
      console.log(color);
    });
    // red
    // green
    // blue
    
  2. var a = [,,,];
    a.forEach(function(x,i){
        log(i+'.'+x);
    })
    
  3. 数组的slice方法可以将“类似数组的对象”变成真正的数组。

    var arr = Array.prototype.slice.call(arrayLike);
    

三、 运算符

3-1 算术比较符

3-1-1

  1. 除了加法运算符之外的都不会发生重载。所有运算子一律转为数值,在进行相应的数学运算
    • 1-‘2’ //-1
    • 1*‘2’ //2
    • 1 / ‘2’ //0.5

3-1-2 对象的相加

  1. 运算子是对象,必须先转成原始类型的值

    var obj = {p:1};
    obj + 2   // "[object Object]2"
    
    
    • 转为原始类型的值【对象得到toString方法默认返回[object Object]

      var obj = {p:1};
      obj.valueOf().toString()  // "[object O]"
      
    • 自己定义valueof或者toString方法

      var obj = {
          valueof: function(){
              return 1;
          }
      };
      obj + 2;  // 3 
      
    • 运算符是一个Date对象的实例,优先执行toString()

      var obj = new Date();
      obj.valueOf = function () { return 1 };
      obj.toString = function () { return 'hello' };
      
      obj + 2  //'hello2'
      

3-1-3 余数运算符

运算结果的正负号由第一个运算子的正负号决定

3-2 布尔运算符

3-2-1 !和&&和||

  1. 以下六个值取反之后值为true

    • undefined
    • null
    • false
    • 0
    • NaN
    • 空字符串 (’ ')
  2. && 运算符:

    • 如果第一个运算符是true,则返回第二个运算子的值
    • 第一个运算符是false,直接返回第一个运算符的值
  3. 使用“短路”取代if结构

    i && dosomething();
    
  4. 运算符用于为变量设置默认值

    saveText(this.text || '')
    

四、 语法专题

4-1 数据类型的转换

4-1-1

  1. JS是一种动态类型的语言
  2. 强制转换类型
    • Number();
    • String()
    • Boolean()

4-1-2 Number()

  1. Number('abc123')      // NaN
    Number(undefined)     // NaN
    Number(null)     // 0
    
  2. Number({
        valueOf: function () {
    		return 2;
        },
        toString:function () {
            return 3;
        }
    })
    
  3. String方法背后的转换规则,与Number方法基本相同,只是互换了valueOf方法和toString方法的执行顺序。

    • 先调用对象自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。

    • 如果toString方法返回的是对象,再调用原对象的valueOf方法。如果valueOf方法返回原始类型的值,则对该值使用String函数,不再进行以下步骤。

    • 如果valueOf方法返回的是对象,就报错。

4-1-3 Boolean()

  1. 以下五个值转换为false
    • undefined
    • null
    • 0
    • NaN
    • ‘’

4-2 错误处理机制

4-2-1

  1. JS原生提供了Error构造函数,所有抛出的错误都是这个构造函数的实例

    var err = new Error('出错了')
    err.message;  // '出错了'
    
    • 调用Error()构造函数,生成实例对象err
    • 抛出Error实例对象之后,整个程序中断在发生错误的地方
  2. 对Error实例还提供了namehestack属性

    • name
    • message
    • stack:错误的堆栈
  3. if(error.name){
        log(error.name + ':' + error.message);
    }
    
    function throwit() {
        throw new Error('');
    }
    function catchit() {
        try {
            throwit();
        } catch(e) {
            log(e.stack);  // print stack trace
        }
    }
    

4-2-2 原生错误类型

  1. Error 实例对象是最一般的错误类型
    • 在其基础上,定义了其他六种错误对象
    • 存在Error的6个派生对象
4-2-2-1 SyntaxError对象
  1. syntaxerror对象是解析代码时发生语法错误

    var 1a;
    // Uncaught SyntaxError: Invalid or unexpected token
    
    // 缺少括号
    log 'log')
    //Uncaught SynataxError: Unexpected string
    
4-2-2-2 ReferenceError对象
  1. // 使用一个不存在的变量
    unknownVariable
    // Uncaught ReferenceError: unknownVariable is not defined
    
  2. 另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果赋值。

    // 等号左侧不是变量
    console.log() = 1
    // Uncaught ReferenceError: Invalid left-hand side in assignment
    
4-2-2-3 RangeError对象
  1. RangeError对象是一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。
// 数组长度不得为负数
new Array(-1)
// Uncaught RangeError: Invalid array length
4-2–2-4 TypeError对象
  1. TypeError对象是变量或参数不是预期类型时发生的错误。比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。

  2. 调用对象不存在的方法,也会抛出TypeError错误,

    new 123
    // Uncaught TypeError: 123 is not a constructor
    
    var obj = {};
    obj.unknownMethod()
    // Uncaught TypeError: obj.unknownMethod is not a function
    
4-2-2-5 URIError对象
  1. URIError对象是 URI 相关函数的参数不正确时抛出的错误

4-2-3 自定义错误

  1. function UserError(message) {
        this.message = message || '默认信息';
        this.name = 'UserError';
    }
    
    UserError.prototype = new Error("这是自定义的错误!");
    UserError.prototype.constructor = UserError;
    

4-2-4 throw语句

  1. throw语句的作用是手动终端程序执行,抛出一个错误

    var x= -1;
    if (x <= 0){
        throw new Error('x必须是正数')
    }
    // Uncaught Error: x 必须为正数
    
  2. throw也可以抛出自定义错误

    function UserError(message) {
        this.message = message || '默认信息';
        this.name = 'UserError';
    }
    
    throw new UserError('出错了!');
    // Uncaught UserError {message: "出错了!", name: "UserError"}
    
    • throw抛出的是一个UserError实例

4-2-5 try…catch结构

  1. try {
        throw new Error('出错了!');
    } catch(e) {
        log(e.name + ':' + e.message);
        log(e.stack);
    }
    
    • 错误被catch代码块捕获课
    • catch接收的参数是try代码块抛出的值

4-2-6 总结

  1. 充分反映try...catch..finally三者的执行顺序

    function f() {
      try {
        console.log(0);
        throw 'bug';
      } catch(e) {
        console.log(1);
        return true; // 这句原本会延迟到 finally 代码块结束再执行
        console.log(2); // 不会运行
      } finally {
        console.log(3);
        return false; // 这句会覆盖掉前面那句 return
        console.log(4); // 不会运行
      }
    
      console.log(5); // 不会运行
    }
    
    var result = f();
    // 0
    // 1
    // 3
    
    result
    // false
    

4-3 编程风格

编写代码的样式规则

编译器的规范叫做“语法规则”,编译器忽略的部分就叫做“编程风格”

  1. 圆括号

    • 一种表示函数的调用
    • 表示表达式的组合
  2. 函数调用和定义时

    • 函数名和左括号之间没有空格
  3. 其他情况下,前面位置的语法元素与左括号之间,都有一个空格

    foo(bar)
    return (a+b);
    if (a === 0) {...}
    function foo(b) {...}
    function (x) {...}
    
  4. 分号

    • do…while循环要有分号

      do {
        a--;
      } while(a > 0); // 分号不能省略
      
    • 函数表达式仍然要使用分号

      var f = function f() {
      };
      
  5. ASI:Automatic Semicolon Insertion,分号的自动添加

  6. ==(相等运算符会自动转换变量类型),只使用严格相等运算符

  7. 建议自增(++)和自减(--)运算符尽量使用+=-=代替。

  8. switch…case建议携程对象结构

    function doAction(action) {
      switch (action) {
        case 'hack':
          return 'hack';
        case 'slash':
          return 'slash';
        case 'run':
          return 'run';
        default:
          throw new Error('Invalid action.');
      }
    }
    
    function doActive(action) {
        var actions = {
            'hack' : function () {
                return 'hack';
            },
            'slash' : function () {
                return 'slash';
            },
            'run': function (){
                return 'run';
            }
        };
        
        if(typeof actions[action] !== 'function') {
            throw new Error('Invalid action.');
        }
        
        return actions[action]();
    }
    

4-4 cosole对象与控制台

4-4-1 console对象以及静态方法

    • performance: 查看网页的性能情况,比如CPU和内存消耗
  1. console.log用于在控制台输出信息

    • 支持以下占位符,不同数据必须使用对应的占位符

      • %s : 字符串

      • %d: 整数

      • %i : 整数

      • %f: 浮点数

      • %o : 对象的链接

      • %c : CSS格式字符串

        console.log(
          '%cThis text is styled!',
          'color: red; background: yellow; font-size: 24px;'
        )
        
  2. 参数是一个对象,console.log会显示对象的值

    console.log({foo: 'bar'})
    // Object {foo: "bar"}
    console.log(Date)
    // function Date() { [native code] }
    

4-4-2 console.info ; console.debug;error;warn;

  1. info会在输出信息前面,加上一个蓝色图标(貌似没有)

  2. debug : 会在控制台输出调试信息

    • 只有在打开显示级别在verbose的情况下,才会显示
  3. warn 方法和 error方法 在控制台输出信息

    • error

      console.error('Error: %s (%i)', 'Server is not responding', 500)
      // Error: Server is not responding (500)
      console.warn('Warning! Too few nodes (%d)', document.childNodes.length)
      // Warning! Too few nodes (1)
      
  4. log方法写入标准输出(stdout),warn和error方法写入标准错误(stderr)

  5. console对象的所有方法,都可以被覆盖

    ['log', 'info', 'warn', 'error'].forEach(function(method) {
        console[method] = console[method].bind(
        console,
         new Date().toISOString()
        );
    })
    
    log("出错了!");
    //
    

4-4-3 console.table()

  1. console.table 方法可以将其转为表格显示

    var languages = [
        { name: 'JavaScript', fileExtension: '.js' },
        { name: 'TypeScript', fileExtension: '.ts' },
    ];
    table(languages);
    

4-4-4 console.count()

  1. count 方法用于计数,输出它被调用次数

    function aa() {
        console.count();
        return 'hi';
    }
    
  2. count 可以接收一个字符串作为参数

    function greet(user) {
        console.count(user);
        return "hi " + user;
    }
    
    greet('bob')
    // bob: 1
    greet('alice')
    // alice: 1
    greet('bob')
    // bob: 2
    

4-4-5 console.dir() 和 dirxml()

  1. dir方法用来对一个对象进行检查(inspect),并以易于阅读和打印的格式

    console.log({f1: 'foo', f2: 'bar'})
    // Object {f1: 'foo', f2: 'bar'}
    
    dir({f1: 'foo', f2: 'bar'})
    // Object
    //   f1: "foo"
    //   f2: "bar"
    //   __proto__: Object
    
  2. console.dirxml() == console.log():

    • 主要用于以目录树的形式,显示DOM结点

      console.dirxml(document.body)
      

4-4-6 console.assert()

  1. 接受两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会提示有错误,在控制台输出第二个参数,否则不会有任何结果。

    console.dirxml([1, 2, 3])
    // 等同于
    console.dir([1, 2, 3])
    
  2. 节点数>50,才会提示错误

    console.assert(list.childNodes.length < 500, '节点个数大于等于500')
    

五、标准库

5-1 Object 对象

5-1-1

  1. JS原生提供Object对象
    • javascript的所有其他对象都继承自Object对象,那些对象都是Object的实例
  2. Object 对象的原生方法分为:Object 本身的方法Object的实例方法
5-1-1-1 Object对象本身的方法
  1. 所谓本身的方法 : 定义在Object 对象的方法

    Object.print = function (o) { console.log(o) };
    
5-1-1-2 Object的实例方法
  1. Object 的实例方法: 定义在Object 原型对象Object.prototype上的方法,可以被Object实例直接使用

    Object.prototype.print = function () {
        console.log(this);
    };
    
    var obj = new Object();
    obj.print()    // Object
    
5-1-1-3 Object()
  1. 写一个判断变量是否为对象的函数
function isObject(value){
    return value === Object(value);
}

isObject([])   // true
isObject(true)   // false
5-1-1-4 Object构造方法
  1. var obj = {}   ====  var obj = new Object()
    
5-1-1-5 Object.keys(),Object.getOwnPropertyNames()

都是遍历对象的属性

  1. Object.keys()

    • 参数是对象,结果是数组

    • 该数组的成员都是该对象自身的所有属性名

      var obj = {
        p1: 123,
        p2: 456
      };
      
      Object.keys(obj) // ["p1", "p2"]
      Object.getOwnPropertyNames(obj) // ["p1", "p2"]
      
  2. Object.getOwnPropertyNames方法 也是接受一个对象作为参数,返回一个数组,包含该对象自身的所有属性名

  3. 由于JavaScript没有提供计算对象属性个数的方法

    var obj = {
        p1: 123,
        p2: 456
    }
    
    Object.keys(obj).length // 2
    Object.getOwnPropertyNames(obj).length // 2
    

5-1-2 Object的实例对象

  1. Object实例对象的方法,主要有以下六个。
    • Object.prototype.valueOf():返回当前对象对应的值。
    • Object.prototype.toString():返回当前对象对应的字符串形式。
    • Object.prototype.toLocaleString():返回当前对象对应的本地字符串形式。
    • Object.prototype.hasOwnProperty():判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
    • Object.prototype.isPrototypeOf():判断当前对象是否为另一个对象的原型。
    • Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。

5-2 Array对象

5-2-1 构造函数

  1. Array是javascript的原生对象,同时也是一个构造函数,用它生成新的数组
5-2-1-1 Array.isArray()
  1. Array.isArray方法返回一个布尔值,表示参数是否为数组,可以弥补typeof运算符的不足

    var arr = [1, 2, 3];
    
    typeof arr // "object"
    Array.isArray(arr) // true
    
5-2-1-2 实例方法
  1. valueof方法返回数组本身

    var arr = [1, 2, 3];
    arr.valueOf() // [1, 2, 3]
    
  2. toString()返回数组的字符串形式

    var arr = [1, 2, 3];
    arr.toString() // "1,2,3"
    
    var arr = [1, 2, 3, [4, 5, 6]];
    arr.toString() // "1,2,3,4,5,6"
    
  3. push(): 末尾添加元素,并返回添加新元素后的数组长度

  4. pop() : 删除数组最后一个元素

    • 对空数组使用pop方法,不会报错,而会返回undefined
  5. shift() : 删除数组的第一个元素

    var a = ['a', 'b', 'c'];
    
    a.shift() // 'a'
    a // ['b', 'c']
    
  6. unshift() : 第一个位置添加元素,返回添加新元素后的数组长度

    var a = ['a', 'b', 'c'];
    
    a.unshift('x''d'); // 4
    a // ['x', 'd','a', 'b', 'c']
    
  7. join() : 以指定参数作为分割符,将所有数组成员连接为一个字符串返回

    • 不提供参数,默认用逗号分隔
    var a = [1, 2, 3, 4];
    
    a.join(' ') // '1 2 3 4'
    a.join(' | ') // "1 | 2 | 3 | 4"
    a.join() // "1,2,3,4"
    
    [undefined, null].join('#')    //  '#'
    ['a',,'b'].join('-')    // 'a--b'
    
    • 数组成员是undefined或null或空位时,会被转为空字符串

    • 通过call方法,可以用于字符串或类似数组的对象

      Array.prototype.join.call('hello','-')    // "h-e-l-l-o"
      var obj = {0: 'a', 1: 'b', length: 2};
      Array.prototype.join.call(obj, '-')   // 'a-b'
      
  8. concat() : 多个数组的合并

    • 数组成员包括对象,concat方法返回当前数组的一个浅拷贝

    • 浅拷贝,指的是新数组拷贝的是对象的引用

      var obj = { a: 1 };
      var oldArray = [obj];
      
      var newArray = oldArray.concat();
      
      obj.a = 2;
      newArray[0].a // 2
      
  9. reverse() :

    var obj = { a: 1 };
    var oldArray = [obj];
    
    var newArray = oldArray.concat();
    
    obj.a = 2;
    newArray[0].a // 2
    
  10. slice() :提取目标数组的一部分,并返回

    var a = ['a', 'b', 'c'];
    
    a.slice(0) // ["a", "b", "c"]
    a.slice(1) // ["b", "c"]
    a.slice(1, 2) // ["b"]
    a.slice(2, 6) // ["c"]
    a.slice() // ["a", "b", "c"]
    
    • 重要应用: 是将类似数组的对象转为真正的数组

      Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
      // ['a', 'b']
      
      Array.prototype.slice.call(document.querySelectorAll("div"));
      Array.prototype.slice.call(arguments);
      
  11. splice() : 删除原数组的一部分成员,删除的位置添加新的数组成员

    • 返回原数组
    arr.splice(start, count, addElement1, addElement2, ...);
    
    • 删除成员,还插入了两个新成员。

      var a = ['a', 'b', 'c', 'd', 'e', 'f'];
      a.splice(4, 2, 1, 2) // ["e", "f"]
      a // ["a", "b", "c", "d", 1, 2]
      
    • 插入元素,splice可以将第二个参数设为0、

      var a = [1, 1, 1];
      
      a.splice(1, 0, 2) // []
      a // [1, 2, 1, 1]
      
    • 拆分数组: 只提供第一个参数

      var a = [1, 2, 3, 4];
      a.splice(2) // [3, 4]
      a // [1, 2]
      
  12. sort() : 对数组进行排序,按照字典顺序排

    • 数值会被先转为字符串,再按照字典顺序进行比较
    ['d', 'c', 'b', 'a'].sort()
    // ['a', 'b', 'c', 'd']
    
    [4, 3, 2, 1].sort()
    // [1, 2, 3, 4]
    
    [11, 101].sort()
    // [101, 11]
    
    [10111, 1101, 111].sort()
    // [10111, 1101, 111]
    
    • sort按照字定义方式排序,可以传入一个函数作为参数

      [10111, 1101, 111].sort(function (a, b) {
        return a - b;
      })
      // [111, 1101, 10111]
      
      [
        { name: "张三", age: 30 },
        { name: "李四", age: 24 },
        { name: "王五", age: 28  }
      ].sort(function (o1, o2) {
        return o1.age - o2.age;
      })
      // [
      //   { name: "李四", age: 24 },
      //   { name: "王五", age: 28  },
      //   { name: "张三", age: 30 }
      // ]
      
      [1, 4, 2, 6, 0, 6, 2, 6].sort((a, b) => a - b)
      
  13. map() : 将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回

    var nums = [1,2,3];
    
    nums.map(function (n) {
        return n + 1;
    })
    // [2,3,4]
    
    nums;
    
  14. forEach() :

    • 遍历数组的目的是为了得到返回值,使用map()方法,只是为了在屏幕上输出内容使用forEach()方法
  15. filter

       // filter()方法用于过滤数组成员,满足条件的成员组成一个新数组
            var b = [1, 2, 3, 4, 5].filter(function(elem) {
                return (elem > 3);
            })
            console.log(b);
            // 返回数组arr里面所有布尔值为true的成员
            var arr = [0, 1, 'a', false];
            var temarr = arr.filter(Boolean);
            console.log(temarr, arr);
    
            // filter方法可以接受三个参数 : elem,index和arr
            var c = [1, 2, 3, 4, 5].filter(function(elem, index, arr) {
                return index % 2 === 0;
            })
            console.log(c);
    
            // filter() 方法可以接受第二个参数, 用来绑定函数内部的this变量
            var obj = {
                MAX: 3
            };
            var myFilter = function(item) {
                if (item > this.MAX) return true;
            };
            var arr = [2, 8, 3, 4, 1, 3, 2, 9];
            console.log(arr.filter(myFilter, obj));
    
  16. some(),every()

    var arr = [1, 2, 3, 4, 5];
    arr.some(function (elem, index, arr) {
      return elem >= 3;
    });
    // true
    
    var arr = [1, 2, 3, 4, 5];
    arr.every(function (elem, index, arr) {
      return elem >= 3;
    });
    // false
    
    • 只要一个成员的返回值是true,则整个some返回值是true,否则返回false

    • every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false

      function isEven(x) { return x % 2 === 0 }
      
      [].some(isEven) // false
      [].every(isEven) // true
      

记得看最后一个例子

5-3 包装对象

  1. Number,String,Boolean

    var v1 = new Number(123);
    var v2 = new String('abc');
    var v3 = new Boolean(true);
    
    typeof v1 // "object"
    typeof v2 // "object"
    typeof v3 // "object"
    
    v1 === 123 // false
    v2 === 'abc' // false
    v3 === true // false
    
    • 如果不作为构造函数,而是作为普通函数,常常用于将任意类型的值转为数值、字符串和布尔值
  2. Boolean对象

    • !! 可以将任意值转为对应的布尔值
  3. Number对象(实例)

    • number.prototype.toString( ) :

      (10).toString(2) // "1010"
      (10).toString(8) // "12"
      (10).toString(16) // "a"
      
      
      • 通过方括号运算符也可以调用toString

        10['toString'](2) // "1010"
        
  4. String对象

        // 生成随机字符的例子
            function random_str(length) {
                var ALPHABET = 'ABCDEFGHIGKLMNOPQRSTUVWXYZ';
                ALPHABET += 'abcdefghijklmnopqrstuvwxyz';
                ALPHABET += '0123456789-_';
                var str = '';
                for (var i = 0; i < length; ++i) {
                    var rand = Math.floor(Math.random() * ALPHABET.length);
                    str += ALPHABET.slice(rand, rand + 1);
    
                }
                return str;
            }
    
            console.log(random_str(6));
    

5-4 Date对象

以国际标准时间(UTC)1970年1月1日00:00:00作为时间的零点

5-5 正则(RegExp对象)

5-5-1

  1. 新建正则表达式:
    • 字面量: var regex = /xyz;
      • 引擎编译代码时,新建正则表达式
      • 效率高
    • RegExp构造函数 : var trgrx = new RegExp('xyz');
      • 运行时新建

5-5-2 字面量 字符和元字符

  1. 点字符(.) : 匹配除回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。

    /c.t/
    

    上面代码中,c.t匹配ct之间包含任意一个字符的情况

    • [^]包含一切字符
    • [\S\s]指代一切字符。
  2. 位置字符:

    • 表示提示字符所处的位置,主要有两个字符
    • ^ : 字符开始位置
    • $: 表示字符串的结束位置
  3. 选择符(|):

    竖线符号(|)在正则表达式中表示“或关系”(OR),即cat|dog表示匹配catdog

    /11|22/.test('911') // true
    
  4. 预定义模式

    预定义模式指的是某些常见模式的简写方式。

    • \d 匹配0-9之间的任一数字,相当于[0-9]
    • \D 匹配所有0-9以外的字符,相当于[^0-9]
    • \w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
    • \W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
    • \s 匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]
    • \S 匹配非空格的字符,相当于[^ \t\r\n\v\f]
    • \b 匹配词的边界。
    • \B 匹配非词边界,即在词的内部。
  5. 量词符

    量词符用来设定某个模式出现的次数。

    • ? 问号表示某个模式出现0次或1次,等同于{0, 1}
    • * 星号表示某个模式出现0次或多次,等同于{0,}
    • + 加号表示某个模式出现1次或多次,等同于{1,}

5-6 jSON

  1. JSON(JavaScript Object Notion): 用于数据交换的文本格式
    • 取代繁琐的XML格式
    • 可以优解释引擎直接处理,不用另外添加解析代码
    • 复合类型的值只能是数组或对象,不能是函数、正则表达式对象、日期对象。
    • 原始类型的值只有四种:字符串、数值(必须以十进制表示)、布尔值和null(不能使用NaN, Infinity, -Infinityundefined)。
    • 字符串必须使用双引号表示,不能使用单引号。
    • 对象的键名必须放在双引号里面。
    • 数组或对象最后一个成员的后面,不能加逗号。
  2. JSON的两个静态方法:
    • JSON.stringify()
      • 将一个值转为JSON字符串
    • JSON.parse()
      • 使用这个还原

六、 面向对象编程(Object Oriented Progranmming,OOP)

6-1 实例对象与new命令

  1. JS不是基于类的,而是基于构造函数(constructor)和原型链(prototype)的
  2. 构造函数:
    • 第一个字母通常要大写
  3. new: 执行构造函数,返回一个实例对象
  4. 使用new命令时,它后面的函数依次执行下面的步骤。
    1. 创建一个空对象,作为将要返回的对象实例。
    2. 将这个空对象的原型,指向构造函数的prototype属性。
    3. 将这个空对象赋值给函数内部的this关键字。
    4. 开始执行构造函数内部的代码。

6-2 this关键字

6-2-1 含义

    • this可以用在构造函数中,表示实例对象
  1. this都有一个共同点:返回一个对象

6-3 对象的继承

JS继承机制的设计思想就是

  • 原型对象的所有属性和方法,都能被实例对象 共享

6-3-1 原型对象

  1. 函数默认拥有prototype属性。

    • prototype属性 : 指向一个对象

    • function f() {}
      typeof f.prototype // "object"
      
  2. function Animal(name) {
      this.name = name;
    }
    Animal.prototype.color = 'white';
    
    var cat1 = new Animal('大毛');
    var cat2 = new Animal('二毛');
    
    cat1.color // 'white'
    cat2.color // 'white'
    

    上面代码中,构造函数Animalprototype属性,就是实例对象cat1cat2的原型对象。原型对象上添加一个color属性,结果,实例对象都共享了该属性。

6-3-2 原型链

  1. “原型链”(prototype chain):对象到原型,再到原型的原型……

  2. Object.prototype对象有没有它的原型呢?回答是Object.prototype的原型是nullnull没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null

    Object.getPrototypeOf(Object.prototype)
    // null
    
  3. 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。

6-3-3 Object的原型对象的方法

  1. 有亿点点的头脑风暴

    var F = function () {
      this.foo = 'bar';
    };
    
    var f = new F();
    // 等同于
    var f = Object.setPrototypeOf({}, F.prototype);
    F.call(f);
    

    上面代码中,new命令新建实例对象,其实可以分成两步。第一步,将一个空对象的原型设为构造函数的prototype属性(上例是F.prototype);第二步,将构造函数内部的this绑定这个空对象,然后执行构造函数,使得定义在this上面的方法和属性(上例是this.foo),都转移到这个空对象上。

八、 DOM

  • 父结点(parentNode): 直接的上级节点
  • 子结点(childNodes): 直接的下级节点
  • 同级节点关系(sibling) : 拥有同一个父节点的节点

8-1 Node接口

所有DOM节点都继承了Node接口,拥有一些共同的属性和方法

    • 文档节点(document):9,对应常量Node.DOCUMENT_NODE
    • 元素节点(element):1,对应常量Node.ELEMENT_NODE
    • 属性节点(attr):2,对应常量Node.ATTRIBUTE_NODE
    • 文本节点(text):3,对应常量Node.TEXT_NODE
    • 文档片断节点(DocumentFragment):11,对应常量Node.DOCUMENT_FRAGMENT_NODE
    • 文档类型节点(DocumentType):10,对应常量Node.DOCUMENT_TYPE_NODE
    • 注释节点(Comment):8,对应常量Node.COMMENT_NODE

8-2 ParentNode 接口,ChildNode 接口

8-3 Document 节点

  1. 代码检查网页是否加载成功

    // 基本检查
    if (document.readyState === 'complete') {
      // ...
    }
    
    // 轮询检查
    var interval = setInterval(function() {
      if (document.readyState === 'complete') {
        clearInterval(interval);
        // ...
      }
    }, 100);
    
  2. 下面一点点没看

8-4 Element 节点

  1. Element.clientHeight,Element.clientWidth

    • document.documentElementclientHeight属性,返回当前视口的高度(即浏览器窗口的高度),等同于window.innerHeight属性减去水平滚动条的高度
    • 一般来说,document.body.clientHeight大于document.documentElement.clientHeight
  2. Element.scrollHeight,Element.scrollWidth

    • Element.scrollHeight属性返回一个整数值(小数会四舍五入),表示当前元素的总高度(单位像素),包括溢出容器、当前不可见的部分。它包括padding,但是不包括bordermargin以及水平滚动条的高度(如果有水平滚动条的话),还包括伪元素(::before::after)的高度。

    • Element.scrollWidth属性表示当前元素的总宽度(单位像素),其他地方都与scrollHeight属性类似。这两个属性只读。

      整张网页的总高度可以从document.documentElementdocument.body上读取。

      // 返回网页的总高度
      document.documentElement.scrollHeight
      document.body.scrollHeight
      

      注意,如果元素节点的内容出现溢出,即使溢出的内容是隐藏的,scrollHeight属性仍然返回元素的总高度。

      // HTML 代码如下
      // <div id="myDiv" style="height: 200px; overflow: hidden;">...<div>
      document.getElementById('myDiv').scrollHeight // 356
      

      上面代码中,即使myDiv元素的 CSS 高度只有200像素,且溢出部分不可见,但是scrollHeight仍然会返回该元素的原始高度。

    • Element.scrollLeft属性表示当前元素的水平滚动条向右侧滚动的像素数量,Element.scrollTop属性表示当前元素的垂直滚动条向下滚动的像素数量。对于那些没有滚动条的网页元素,这两个属性总是等于0。

    • 如果要查看整张网页的水平的和垂直的滚动距离,要从document.documentElement元素上读取。

    document.documentElement.scrollLeft
    document.documentElement.scrollTop
    

    这两个属性都可读写,设置该属性的值,会导致浏览器将当前元素自动滚动到相应的位置。

  3. Element.offsetLeft,Element.offsetTop

    Element.offsetLeft返回当前元素左上角相对于Element.offsetParent节点的水平位移,Element.offsetTop返回垂直位移,单位为像素。通常,这两个值是指相对于父节点的位移。

    下面的代码可以算出元素左上角相对于整张网页的坐标。

    function getElementPosition(e) {
      var x = 0;
      var y = 0;
      while (e !== null)  {
        x += e.offsetLeft;
        y += e.offsetTop;
        e = e.offsetParent;
      }
      return {x: x, y: y};
    }
    

8-5 属性的操作

  1. 下面代码可以遍历一个元素节点的所有属性。
    
    var para = document.getElementsByTagName('p')[0];
    var result = document.getElementById('result');
    
    if (para.hasAttributes()) {
      var attrs = para.attributes;
      var output = '';
      for(var i = attrs.length - 1; i >= 0; i--) {
        output += attrs[i].name + '->' + attrs[i].value;
      }
      result.textContent = output;
    } else {
      result.textContent = 'No attributes to show';
    }
    

8-6 CSS操作

下面一半和MUtation Observar API,我好桑心,仿佛没学过JS

第九章 事件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值