JS杂记

JS 杂记

  • 'use strict' JS文件行首声明,防止变量未声明成为全局变量

    // 对象内部定义的函数称为方法,其中this关键字代指对象本身
    // 如果不通过对象调用方法,那么this代指window,如果在'strict'模式下,this代指'undefined'
    
  • ES6 模板字符串

    // 需要放到 反引号 中才有效哦;另外 反引号 可以支持多行文本
    var name = '小明';
    var age = 20;
    var message = `你好, ${name}, 你今年${age}岁了!`;
    
  • in 判断对象是否拥有某属性。注意:JS对象的所有属性都是字符串!

    var xiaoming = {
        name: '小明',
        birth: 1990
    };
    
    "name" in xiaoming; // true
    
  • hasOwnProperty()判断一个对象是否拥有某属性(非继承属性)

    var xiaoming = {
        name: '小明',
        birth: 1990
    };
    
    xiaoming.hasOwnProperty('name'); // true
    xiaoming.hasOwnProperty('toString'); // false  toString 继承自object
    
  • 条件分支语句块只包含一条语句时,可以省略 { }花括号

    var balance = 99;
    if (balance >= 100)
        alert('rich');
    else
        alert('poor');
    
    // 但是建议每次都加上,因为一点语句块包含两条语句,那么第二条就不在else控制范围内
    
  • JS中的falsetrue:

    null;
    undefined;
    0;
    NaN;
    ''; // 空字符串
    // 除此之外都为true
    // 因此判断数组为空,需要如下:
    var arr = [];
    if (arr) 
        console.log('true');  // 打印true  
    
    if (arr.length) 
        console.log('true');  // 不打印
    
    // 通过lodash进行是否为空判断
    if (_.isEmpty(arr)) 
        console.log('true');  // 打印true
    
  • 浮点数/整数解析

    parseFloat('123.45')  // 123.45  解析为浮点数
    parseInt(123.45)  // 123 解析为整数
    
  • for ... in循环,遍历属性

    // 遍历对象的属性
    var obj = {
        name: 'Jack',
        age: 20,
        city: 'Beijing'
    };
    for (var i in obj) 
        console.log(i); // name age city
    
    // 遍历arrary的索引 (array也是对象,其属性是索引)
    var arr = ['a', 'b', 'c'];
    for (var i in arr) {
        console.log(i);  // 0 1 2
        console.log(arr[i]) // a b c
    }
    
  • do {...} while(...) 循环,先执行再判断,因此循环体至少执行一次。

  • Map 对象,ES6新特性

    // JS对象的属性只能是字符串。而map对象类似于python的字典,key可以为字符串或数字,查找速度极快
    // 初始化一个Map对象,接收二维数组
    var m = new Map([['ayhan', 'male'], ['lena', 'female']]);  // {"ayhan" => "male", "lena" => "female"}
    
    // 初始一个空Map对象
    var m = new Map();
    
    //添加元素及操作
    m.set('ayhan', 'male');  // 添加元素
    m.has('ayhan');  // true 
    m.get('ayhan'); // 'male'
    m.delete('ayhan'); // 删除, key存在返回true, 否则false
    
  • Set 集合对象,ES6新特性;类似python, 其中元素不能重复,可用于去重

    // 初始化一个Set对象
    var s = new Set([1, 2, 3, 3]); // {1, 2, 3}
    var s = new Set()
    
    // 常用操作
    s.add(5); // {1, 2, 3, 5}
    s.delete(3); // {1,2, 5}
    s.has(3); // false
    
    
  • iterable 可迭代对象,ES6新特性,统一了集合类型的遍历,包括:Array, Map, Set

    // 通过 for ... of 遍历
    var a = [1, 2, 3, 4];
    var m = new Map([['a', 1], ['b',2], ['c', 3]]);
    var s = new Set(['x', 'y', 'z']);
    
    for (var i of a) {
        console.log(i)// 1,2,3,4
    };
    
    for (var i of m) {
        console.log(i[0] + '=' + i[1])// a=1, b=2, c=3
    };
    
    for (var i of s) {
        console.log(i); // x, y, z
    }
    
    // 通过 forEach 遍历
    // 遍历Array
    a.forEach(function(ele, index, array) {
        // ele 当前元素
        // index 当前索引
        // array 可迭代对象本身
        console.log(ele + ', index= ' + index);  // 1, index= 0
    });
    
    // 遍历Map
    m.forEach(function(ele, key, map) {
        console.log(val);
    });
    
    // 遍历Set: 集合没有索引,因此前两个参数就是当前元素
    s.forEach(function(ele, sameEle, set) {
        console.log(sameEle);
    })
    
    // 虽然forEach可以接收3个参数,如果只需要第一个,下面这样也是可以的
    a.forEach(function(ele) {
        console.log(ele);  // 1
    });
    
  • 定义函数的两种方式:

    // function 声明
    function bar(x) {
        console.log(x);
    }
    
    // 通过匿名函数赋值
    var bar = function(x) {
        console.log(x);
    };
    
  • JS函数可以传入任意个参数不报错

  • 函数参数检查和异常抛出

    function abs(x) {
        if (typeof x !== 'number') {
            throw 'Not a number';
        }
        if (x >= 0) {
            return x;
        } else {
            return -x;
        }
    }
    
    abs('aaa')  // Uncaught Not a number
    
  • arguments 关键字,只在函数内部起作用,指向函数调用时传入的所有参数,类似于Array,可以进行遍历

  • rest参数 ES6 新特性,用于传给函数的多余的参数

    function bar(x, y, ...rest) {   //  ...rest  固定写法
        console.log(rest)
    };
    
    bar(1, 2, 3, 4);  // [3, 4]
    
  • 变量提升:扫描整个函数体的语句,把所有声明的变量“提升”到函数顶部,但是不提提升赋值:

    function foo() {
        var a = 'hello, ' + b;
        console.log(a);
        var b = 'ayhan';
        }
    
    foo(); //  hello, undefined
    
    // 执行时相当于:
    function foo() {
        var b; // 提升b的声明,但是不赋值,因此是undefined
        var a = 'hello, ' + b;
        console.log(a);
        var b = 'ayhan';
        }
    
  • 鉴于JS的变量提升特性,建议在函数体中首先通过var 声明所有的变量:

    function foo() {
        var              // 通过 var 声明函数内所有变量
        	b = 'ayhan';
        	a = 'hello, ' + b;
        console.log(a);
    }
    
  • JS有一个默认的全局对象window,因此任何全局作用域变量,都相当于绑定到window的一个属性

    var a = 1;
    a == window.a;  // true
    
    function foo() {
        console.log(foo);
    }
    foo == window.foo;  //true
    
  • JS 中变量作用域查找是层层网上找,直到唯一的全局作用域window,如果还是找不到,就报ReferenceError 错误。

  • 名字空间:由于JS中任何全局变量都会绑定到window上,容易发生命名冲突。解决方案:声明一个全局变量,把所有的变量和函数都绑定到这个变量上。

    var myspace = {};  // 声明一个全局变量
    myspace.x = 123;
    myspace.foo = function() {...}
    
    // jQuery等库其实就是这么干的
    
  • 块级作用域:传统的JS作用域是以函数划分。ES6中引入的let 可以声明一个块级作用域变量

    function foo() {
        for (var i=0; i<10; i++) {
            ;
        }
        console.log(i);
    }
    
    foo(); // 10
    
    // 通过 let 声明具有块级作用域的变量
    function foo() {
        for (let i=0; i<10; i++) {
            ;
        }
        console.log(i);
    }
    
    foo(); // undefined
    
  • const 常量。传统的JS中用大写字母声明常量,ES6中引入了const,与let一样,具有块级作用域

  • 解构赋值,类似于python中的解压序列,ES6

    // 将一个数组中的元素分别赋值给几个变量
    var [x, y, z] = [1, 2, 3]; // x=1, y=2, z=3
    
    // 也支持其他集合对象,比如集合
    var s = new Set([1,2,3]);
    var [x, y, z] = s;
    
    // 嵌套
    var [x, [y, z]] = ['hello', ['ayhan', 'papa']];  // 嵌套层级必须一致才行。
    
    // 忽略多余元素
    var [, y, ] = ['hello', 'ayhan', 'papa'];  // 只赋值 var y = 'ayhan'
    
    // 对象,如果有嵌套,层级需要保持一致
    var handsome = {
        name: 'ayahn', 
        age: '18', 
        phone: '110111110',
        addr: {
            city: 'beijing',
            strict: '海淀', 
        }
    }
    var {name, addr: {city, strict}} = handsome; 
    strict // 海淀
    
    // 对象属性不存在时,变量将被赋值为undefined。对于不存在的属性,可以设置默认值
    var {name, gender='male'} = handsome;
    
    // 将某个属性赋给另一个变量
    var {phone: mobile} = handsome;  // 将handsome对象的phone属性赋值给mobile变量
    phone  // Uncaught ReferenceError: phone is not defined
    mobile  // "110111110"
    
  • 解构赋值的应用场景:

    // 交换变量的值
    var x=1, y=2;
    [x, y] = [y, x];
    
    // 获取当前页面的域名和路径
    var {hostname:domain, pathname:path} = location;
    
    // 如果函数接收对象作为参数,通过解构赋值,可以直接该对象的属性绑定给变量
    function buildDate({year, month, day, hour=0, minute=0, second=0}) {
        return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
    }
    
    buildDate({year: 2017, month:12, day:12} );  // Tue Dec 12 2017 00:00:00 GMT+0800 (中国标准时间)
    
  • 函数apply方法:1. 指定this代指谁 2. 装饰器

    // 格式:func.apply(调用者, [arg1, arg2,...])  不同的调用者,this指代不一样
    

    // 利用apply实现装饰器,统计parseInt调用了几次。
    ‘use strict’

    var count = 0;
    var oldParseInt = parseInt;
    window.parseInt = function() {
    count += 1
    return oldParseInt.apply(null, arguments);
    }

    
    
  • call 方法,同apply,只是传参按顺序传入,而不是打包成数组传入

  • map, reduce 映射/聚合计算,返回新的结果

    // 将数组[1,2,3,4] 转为整数1234
    var arr = [1,2,3,4];
    arr.reduce(function(x, y) {
        return x*10 + y;
    })
    
    // 将数组[1,2,3,4] 的每个元素转为字符
    arr.map(String);   
    
  • parseInt 和map的坑

    // 因为parseInt其实还有第二个参数:radix,该参数为进行转换的基数,需要是2~36的一个值,默认为0是使用10,map函数实际会为它传入第二个参数:【当前数的索引值】。所以parseInt实际拿到的radix值在传入1时为0,所以无误,传入2时为1,返回NaN,传入3时为2,返回NaN
    
    // 把字符串变为整数
    var arr = ['1', '2', '3'];
    arr.map(parseInt);  // 1,NaN,NaN  // map将当前元素的索引传入作为第二个参数
    
    //正确做法
    arr.map(Number);
    
    
  • 深度克隆JS对象

    JSON.parse(JSON.stringify(obj)) 
    
    // 以上克隆方式仅在对象内部没有定义方法才可行。
    // 但是通过lodash库可以:
    var objB = _.cloneDeep(objA)
    
  • JS产生随机数:

    function getRandomNumber(min, max){
        return Math.floor(Math.random() * (max - min)) + min;
    }
    console.log(getRandomNumber(15, 20));
    
    // lodash做法:
    console.log(_.random(15, 20));
    
  • 扩展对象,类似python的update

    // 为对象扩展原型方法extend
    Object.prototype.extend = function(obj) {
      for (let i in obj) {
        if (obj.hasOwnProperty(i)) {    //判断被扩展的对象有没有某个属性,
          this[i] = obj[i];
        }
      }
    };
    
    var objA = {"name": "戈德斯文", "car": "宝马"};
    var objB = {"name": "柴硕", "loveEat": true};
    
    objA.extend(objB);
    console.log(objA);
    
    // 通过lodash来做
    console.log(_.assign(objA, objB));
    
  • var let const 辨析

    // var
    function temp() {
        if (1 !== 1) {
            var a = 1;
        }
        console.log(a);
    }
    temp();
    // 打印结果 "undefined",解析:
    // JS中作用域默认以函数划分,并对所有以var声明的变量进行提升,这意味着 var 声明在编译阶段被处理,JS引擎知道函数体内存在变量 a,但是其值只有在赋值语句执行时才真的存在(而以上赋值语句不可能执行),因此默认值为undefined。
    
    // let 
    'use strict';
    function temp() {
        if (1 !== 1) {
            let a = 1;
        }
        console.log(a);
    }
    temp();
    // 结果抛出ReferenceError异常:Uncaught ReferenceError: a is not defined,解析:
    // let 确保变量a只在if语句块中可见。所以在外部并不知道有变量a的存在,这与C,Java等语言的处理方式是一样的。
    
    // const
    'use strict';
    const a = 1;
    a = 2;
    // 结果抛出TypeError异常:Uncaught TypeError: Assignment to constant variable. 解析:
    // const 于C语言中的const相同,一旦为const声明的变量赋值,那么久无法再为该变量进行其他赋值
    // 但是不要将const 和 可变对象混淆,虽然你不可以进行其他赋值,但你可以修改它的值,如下:
    'use strict';
    const a = {};
    a.a = 1;
    
  • window.history属性指向 History 对象,它表示当前窗口的浏览历史。

    window.history.length // 3  一共访问了3个网址
    

    由于安全原因,浏览器不允许脚本读取这些地址,但是允许在地址之间导航。

    • History.back():移动到上一个网址,等同于点击浏览器的后退键。对于第一个访问的网址,该方法无效果。
    • History.forward():移动到下一个网址,等同于点击浏览器的前进键。对于最后一个访问的网址,该方法无效果。
    • History.go():接受一个整数作为参数,以当前网址为基准,移动到参数指定的网址,比如go(1)相当于forward()go(-1)相当于back()。如果参数超过实际存在的网址范围,该方法无效果;如果不指定参数,默认参数为0,相当于刷新当前页面。
  • filter 将函数作用于数组的每个元素上,并根据返回值决定保留(true)还是丢弃元素(false)

    var arr = ['A', '', 'B', null, undefined, 'C', '  '];
    var r = arr.filter(function(s) {
        return s && s.trim();
    })
    
    // filter 接收三个参数 ele, index, self。利用indexOf()总是返回找到的第一个元素的位置,后续重复元素的位置于indexOf()返回的不相等,可以去重
    arr.filter(function(ele, index, self) {
        return self.indexOf(ele) === index;
    })
    
  • sort 排序:默认将数组的所有元素转换为字符串,然后字符串最高位ASCII码进行排序。默认排序没卵用,还好sort可以接收函数。另外,sort会对数组就地进行修改。
    比较大小的逻辑:对于两个元素xy,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1

    // 对数组进行排序
    var arr = [10, 20, 1, 2];
    arr.sort(function (x, y) {
        if (x < y) {
            return -1;
        }
        if (x > y) {
            return 1;
        }
        return 0;
    });
    
  • 闭包

    function lazySum(arr) {
        var sum = function() {
            return arr.reduce(function(x, y) {
                return x + y;
            });
        };
        return sum
    }
    
    var sum = lazySum([1,2,3,4,5]);
    sum(); // 15
    
    // lazySum中定义了函数sum,内部函数sum引用外部函数lazySum的参数和局部变量。
    // 作用域在定义阶段就确定了。因此当lazySum返回sum函数时,sum仍然保留之前参数和局部变量的引用
    

    返回闭包时牢记的一点就是:
    返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }
    
    var res = count();
    var f1 = res[0];
    var f2 = res[1];
    var f3 = res[2];
    f1(); // 16
    f2(); // 16
    f3(); // 16
    
    // 解析,执行函数,循环结束后,arr中推入了3个函数,这个3个函数时闭包函数(定义在内部的函数)。函数引用其定义阶段的外部变量 i, 循环结束后,i的值是4,因此出现以上结果。
    
    // 解决办法查看:https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00143449934543461c9d5dfeeb848f5b72bd012e1113d15000
    
  • 箭头函数

    相当于匿名函数,并简化了函数定义。

    // 简单箭头函数,只包含一个表达式
    x => x * x
    
    // 包含多条语句的,不能省略 {...} 和 return
    x => {
        if (x > 0) {
            return x * x;
        }
        return -x * x;
    }
    
    // 如果参数不止一个,就要用 () 括起来
    (x, y) => x * x + y * y;
    
    // 无参数
    () => 3.14
    
    // 可变参数
    (x, y, ...rest) => {
        var i, sum = x + y;
        for (i=0; i<rest.length; i++) {
            sum += rest[i];
        }
        return sum;
    }
    
    // 如果返回对象,需要用()括起来,以免和{}函数体语法冲突
    x => ({ foo: x })
    
    // this问题:在匿名函数中this指代window或者undefined,箭头函数修复了这个问题,总实指向词法作用域,也就是外层调用者obj
    箭头函数
    阅读: 131908
    ES6标准新增了一种新的函数:Arrow Function(箭头函数)。
    
    为什么叫Arrow Function?因为它的定义用的就是一个箭头:
    
    x => x * x
    上面的箭头函数相当于:
    
    function (x) {
        return x * x;
    }
    在继续学习箭头函数之前,请测试你的浏览器是否支持ES6的Arrow Function:
    
    'use strict';
    
    var fn = x => x * x;
    
    console.log('你的浏览器支持ES6的Arrow Function!');
     Run
    箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }return:
    
    x => {
        if (x > 0) {
            return x * x;
        }
        else {
            return - x * x;
        }
    }
    如果参数不是一个,就需要用括号()括起来:
    
    // 两个参数:
    (x, y) => x * x + y * y
    
    // 无参数:
    () => 3.14
    
    // 可变参数:
    (x, y, ...rest) => {
        var i, sum = x + y;
        for (i=0; i<rest.length; i++) {
            sum += rest[i];
        }
        return sum;
    }
    如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:
    
    // SyntaxError:
    x => { foo: x }
    因为和函数体的{ ... }有语法冲突,所以要改为:
    
    // ok:
    x => ({ foo: x })
    this
    箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。
    
    回顾前面的例子,由于JavaScript函数对this绑定的错误处理,下面的例子无法得到预期结果:
    
    var obj = {
        birth: 1990,
        getAge: function () {
            var b = this.birth; // 1990
            var fn = function () {
                return new Date().getFullYear() - this.birth; // this指向window或undefined
            };
            return fn();
        }
    };
            
    var obj = {
        birth: 1990,
        getAge: function () {
            var b = this.birth; // 1990
            var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
            return fn();
        }
    };
    obj.getAge(); // 28
            
    // 由于this在箭头函数中已经按照吃法作用域绑定了,所以call()/apply()调用箭头函数时,无法对this及进行绑定
    var obj = {
        birth: 1990,
        getAge: function(year) {
            var b = this.birth;
            var fn = (y) => y - this.birth;
            return fn.call({birth:2000}, year);
        }
    };
    
  • generator 生成器函数,同python, ES6特性
    function* 声明,并且函数体内使用yield关键字

    // 定义生成器函数
    function* foo(x) {
        yield x + 1;
        yield x + 2;
        return x + 3;
    }
    
    var g = foo(1);  // 拿到生成器对象
    g.next();  // {value: 2, done: false}
    g.next();  // {value: 3, done: false}
    g.next();  // {value: 4, done: true}  迭代完了
    g.next();  // {value: undefined, done: false}
    
    // 通过 for ... of 进行迭代:
    for (var i of g) {
        console.log(i);
    }
    
    // generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。没有generator之前的黑暗时代,用AJAX时需要这么写代码:
    ajax('http://url-1', data1, function (err, result) {
        if (err) {
            return handle(err);
        }
        ajax('http://url-2', data2, function (err, result) {
            if (err) {
                return handle(err);
            }
            ajax('http://url-3', data3, function (err, result) {
                if (err) {
                    return handle(err);
                }
                return success(result);
            });
        });
    });
    
    // 用生成器来写:
    try {
        r1 = yield ajax('http://url-1', data1);
        r2 = yield ajax('http://url-2', data2);
        r3 = yield ajax('http://url-3', data3);
        success(r3);
    }
    catch (err) {
        handle(err);
    }
    // 看上去是同步的代码,实际执行是异步的。
    
  • JS 时间对象

    var now = new Date(); // 当前时间对象,从本机操作系统获取,不一定准确
    var timer = new Date('2000-12-12') // 指定时间对象
    var ts = Date.parse('2018-06-07 12:00:00'); // 时间戳毫秒1528344000000 
    var d = new Date(ts); // 时间戳转时间对象(浏览器会自动把时间戳转化为本地时间)
    var t = Date.now(); // 获取当前时间戳
    
    d.toLocaleString(); // 转字符串
    d.toUTCString();  // 转化为UTC时间,与UTC8差8个小时
    
  • JS 基于原型创建对象

    // 原型对象:
    var Student = {
        name: '',
        height: 1.2,
        run: function () {
            console.log(this.name + ' is running...');
        }
    };
    
    // 通过Object.create(原型对象)创建新对象
    function createStudent(name) {
        // 基于Student原型创建一个新对象:
        var s = Object.create(Student);
        // 初始化新对象:
        s.name = name;
        return s;
    }
    
    var xiaoming = createStudent('小明');
    xiaoming.run(); // 小明 is running...
    xiaoming.__proto__ === Student; // 小明的原型时Student true
    
  • JS基于构造函数创造对象

    // 构造函数,首字符大写!区分普通函数
    function Student(name) {
        this.name = name;
        this.hello = function() {
            alert('hello, ' + this.name + '!');
        }
    }
    
    // new 实例化对象
    var s = new Student('二狗子')
    
    // 但是所有对象都会拥有一个自己的hello函数,浪费内存
    // 正确做法如下:
    function Student(name) {
        this.name = name;
    }
    Student.prototype.hello = function () {
        alert('hello, ' + this.name + '!');
    }
    
  • JS 属性查找顺序:当前对象 =》 原型对象 =》Object

    var arr = [1,2,3]
    arr => Array.prototype => Object.prototype
    
    // 函数的原型链
    function foo() {
        console.log('foo');
    }
    
    foo => Function.prototype  => Object.prototype  // apply() 就是Function.prototype上的方法
    
  • class 关键字 定义类 ;ES6特性

    // 之前用函数实现Student
    function Student(name) {
        this.name = name;
    }
    Student.prototye.hello = function () {
        alert('hello, ' + this.name + '!');
    }
    
    // class 关键字实现
    class Student {
        constructor(name) {  //构造函数 constructor
            this.name = name;
        }
        
        hello() {   // 没有function 关键字!,所有对象共享hellO方法,不需要如上单独定义
            alert('hello, ' + this.name + '!');
        }
    }
    
    // 创建新的的对象时,也是通过new关键字
    
  • class 类的继承
    没有class关键字之前,JS要实现继承很繁琐(借助prototype),现在通过extends即可

    class PrimaryStudent extends Student {
        constructor(name, grade) {
            super(name);  // super调用父类的构造方法
            this.grade = grade;
        }
        
        myGrade() {
            alert('My grade is ' + this.grade);
        }
    }
    
    // 利用babel可以将class 转为 prototype 语法
    
  • || 短路运算符。在JS逻辑运算中,0、""、null、false、undefined、NaN都会判为false,其他都为true.

    • 只要第一个值的布尔值为false,那么永远返回第二个值。
    • 逻辑或属于短路操作,第一个值为true时,不再操作第二个值,且返回第一个值
    ar attr = attr || "";  // 判断一个变量是否已定义,如果没有定义就给他一个初始值
    
    console.log(0 || '我是string,boolean值为true');          // 返回字符串
    console.log(NaN || '我是string,boolean值为true');        // 返回字符串
    console.log('' || '我是string,boolean值为true');         // 返回字符串
    console.log(null || '我是string,boolean值为true');       // 返回字符串
    console.log(undefined || '我是string,boolean值为true');  // 返回字符串
    // 我们知道 0、NaN、' '、null、undefined转换为boolean值都为flase,若第一个值为false,第二个值为true,则返回第二个值。
    
    console.log(0 || 'NaN');           // 返回 NaN
    console.log(NaN || '');            // 返回 '' 空字符串
    console.log('' || null);           // 返回 null
    console.log(null || 'undefined');  // 返回 undefined
    console.log(undefined || 0);       // 返回 0
    
    // 只要第一个值的布尔值为false,那么永远返回第二个值,**不管第二个值的布尔值是true还是false。
    

    &&

    • 只要第一个值的布尔值为true,那么永远返回第二个值。
    • 逻辑与属于短路操作,第一个值为false时,不再操作第二个值,且返回第一个值。
    &&,它先计算第一个表达式,若为假,就不会去处理第二个表达;否则继续处理后继表达式。从左到右选取表达式的第一个为非true的表达式的值,如果一直未找到则返回最后一个表达式的值。 
    
    例:(其中的味道还需要细心琢磨) 
    
    2 && 's1' && '123' && 'sss' 表达式的值等于 'sss' 
    
    if(a >=5){ 
    alert("你好"); 
    } 
    可以简成: 
    a >= 5 && alert("你好"); 
    
  • 浏览器对象

    • window 对象

      • window.innerWidth 浏览器窗口的内部宽度和高度
      • window.innerHeight
    • navigator 对象 表示浏览器的信息。请注意navigator的信息可以很容易地被用户修改,所以JavaScript读取的值不一定是正确的

      • navigator.appName:浏览器名称;
      • navigator.appVersion:浏览器版本;
      • navigator.language:浏览器设置的语言;
      • navigator.platform:操作系统类型;
      • navigator.userAgent:浏览器设定的User-Agent字符串。
    • screen 对象

      • screen.width:屏幕宽度,以像素为单位;
      • screen.height:屏幕高度,以像素为单位;
      • screen.colorDepth:返回颜色位数,如8、16、24。
    • location 当前页面的URL信息,比如http://www.example.com:8080/path/index.html?a=1&b=2#TOP

      • location.href 获取当前URL

      • location.protocol; // ‘http’
        location.host; // ‘www.example.com
        location.port; // ‘8080’
        location.pathname; // ‘/path/index.html’
        location.search; // ‘?a=1&b=2’
        location.hash; // ‘TOP’

      • location.assign(path) 加载新页面

      • location.reload() 刷新页面

        if (confirm('重新加载当前页' + location.href + '?')) {
            location.reload();  // 刷新页面
        } else {
            location.assign('/'); // 跳回首页
        }
        
  • Promise 对象
    JS代码都是单线程的,这就要求所有的网络请求,浏览器事件,都必须时异步操作。异步操作通常用回调函数实现。
    通过setTimeout模拟网络请求

https://segmentfault.com/a/1190000011652907

function callback() {
    console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');

// 执行结果 
before setTimeout()
VM5314:6 after setTimeout()
undefined  1秒后... 
VM5314:2 Done

异步操作就是会在将来某个时间点触发一个函数调用。

Promise 概念:它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。

Promise一般形式:

new Promise(
    /* executor */
    function(resolve, reject) {
        if (/* success */) {
            // ...执行代码
            resolve();
        } else { /* fail */
            // ...执行代码
            reject();
        }
    }
);

Promise对象有3个状态:

  • pending:初始化状态
  • fulfilled: 完成状态
  • rejected: 失败状态

Promise 的参数是一个执行器函数,它有两个参数:resolve, 和 reject。执行器函数内部执行一些异步操作(比如网络请求):

  • 如果异步操作成功,则调用resolve(),将Promise实例的状态设置为fulfilled
  • 如果异步操作失败,则调用reject(),将Promise实例的状态设置为reject

Promise状态的转化(单向,不可逆)

  • 操作成功:pending -> fulfilled
  • 操作失败:pending -> rejected

示例:

function test(resolve, reject) {
  var timeOut = Math.random()*2;
  console.log('set timeout to: '+timeOut+' seconds');
  setTimeout(function() {
    if (timeOut < 1) {
      console.log('call resolve()...');
      resolve('200 ok');
    }
    else {
      console.log('call reject()...');
      reject('timeout in  ' + timeOut + ' seconds');
    }
  }, timeOut*1000)

}

// Promise 对象来执行函数,并在某个时刻获得成功或失败的结果。Promise对象串联,可以简化为
new Promise(test).then(function (result) {
  console.log('成功: ' + result);
}).catch(function (reason) {
  console.log('失败: ' + reason);
})

Promise的方法:
then() 方法:调用后返回一个Promise对象,因此可以实现链式调用。then() 接收两个函数,一个是处理成功后的函数,一个是处理错误结果的函数。

var p1 = new Promise(function(resolve, reject) {
  // 模拟网络请求成功
  setTimeout(function () {
    resolve('success response')
  }, 2000);
});

p1
  .then(function(data){
  console.log(data); // 成功,执行
}, function (err) {
  console.log(err);  // 不执行
})
  .then(function(data) {
  // 上一步then()没有返回值,data undefined
  console.log('链式调用: ' + data);
  return NaN
}, function(err) {
  console.log(err)
})
  .then(function (data) {
    console.log(data)
  }, function (err) {
    console.log(err)  // 失败执行
  })

/* 返回的这个Promise对象的状态主要是根据promise1.then()方法返回的值,大致分为以下几种情况:

如果then()方法中返回了一个参数值,那么返回的Promise将会变成接收状态。
如果then()方法中抛出了一个异常,那么返回的Promise将会变成拒绝状态。
如果then()方法调用resolve()方法,那么返回的Promise将会变成接收状态。
如果then()方法调用reject()方法,那么返回的Promise将会变成拒绝状态。
如果then()方法返回了一个未知状态(pending)的Promise新实例,那么返回的新Promise就是未知状态。
如果then()方法没有明确指定的resolve(data)/reject(data)/return data时,那么返回的新Promise就是接收状态,可以一层一层地往下传递。
*/

var promise2 = new Promise(function(resolve, reject) {
  // 2秒后置为接收状态
  setTimeout(function() {
    resolve('success');
  }, 2000);
});

promise2
  .then(function(data) {
    // 上一个then()调用了resolve,置为fulfilled态
    console.log('第一个then');
    console.log(data);
    return '2';
  })
  .then(function(data) {
    // 此时这里的状态也是fulfilled, 因为上一步返回了2
    console.log('第二个then');
    console.log(data);  // 2

    return new Promise(function(resolve, reject) {
      reject('把状态置为rejected error'); // 返回一个rejected的Promise实例
    });
  }, function(err) {
    // error
  })
  .then(function(data) {
    /* 这里不运行 */
    console.log('第三个then');
    console.log(data);
    // ....
  }, function(err) {
    // error回调
    // 此时这里的状态也是fulfilled, 因为上一步使用了reject()来返回值
    console.log('出错:' + err); // 出错:把状态置为rejected error
  })
  .then(function(data) {
    // 没有明确指定返回值,默认返回fulfilled
    console.log('这里是fulfilled态');
});

catch() 方法:同then() ,返回新的Promise对象,主要用于捕获处理异常,因此可以省略then()方法的第二个参数。

var p3 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    reject('reject')
  }, 2000);
});

p3
  .then(function(data) {
  console.log('fulfilled status');  // 不会执行
})
  // 最后的catch()方法可以捕获在这一条Promise链上的异常
  .catch(function (err) {
  console.log(err)
})

all() 方法,接收一个promise对象构成数组。处理一些并发的异步操作,其结果互不干扰,只是需要异步执行。它最终只有两种状态:成功或者失败

它的状态受参数内各个值的状态影响,即里面状态全部为fulfilled时,它才会变成fulfilled,否则变成rejected。成功调用后返回一个数组,数组的值是有序的,即按照传入参数的数组的值操作后返回的结果

var arr = [1,2,3];
var ps = arr.map(function(ele){
  return new Promise(function (resolve, reject) {
    resolve(ele * 5);
  });
});

Promise.all(ps).then(function (data) {
  console.log(data); // [ 5, 10, 15 ]
})

race() 方法:类似all(),但是谁跑得快,就取谁的值。有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});

// 由于p1执行较快,Promise的then()将获得结果'P1'。p2仍在继续执行,但执行结果将被丢弃。
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

resolve()
Promise.resolve()接受一个参数值,可以是普通的值具有then()方法的对象Promise实例。正常情况下,它返回一个Promise对象,状态为fulfilled。但是,当解析时发生错误时,返回的Promise对象将会置为rejected态。如下:

// 参数为普通值
var p4 = Promise.resolve(5);
p4.then(function(data) {
  console.log(data); // 5
});


// 参数为含有then()方法的对象
var obj = {
  then: function() {
    console.log('obj 里面的then()方法');
  }
};

var p5 = Promise.resolve(obj);
p5.then(function(data) {
  // 这里的值时obj方法里面返回的值
  console.log(data); // obj 里面的then()方法
});


// 参数为Promise实例
var p6 = Promise.resolve(7);
var p7 = Promise.resolve(p6);

p7.then(function(data) {
  // 这里的值时Promise实例返回的值
  console.log(data); // 7
});

// 参数为Promise实例,但参数是rejected态
var p8 = Promise.reject(8);
var p9 = Promise.resolve(p8);

p9.then(function(data) {
  // 这里的值时Promise实例返回的值
  console.log('fulfilled:'+ data); // 不执行
}).catch(function(err) {
  console.log('rejected:' + err); // rejected: 8
});

reject() 方法Promise.reject()和Promise.resolve()正好相反,它接收一个参数值reason,即发生异常的原因。此时返回的Promise对象将会置为rejected态。如下:

var p10 = Promise.reject('手动拒绝');
p10.then(function(data) {
  console.log(data); // 这里不会执行,因为是rejected态
}).catch(function(err) {
  console.log(err); // 手动拒绝
}).then(function(data) {
 // 不受上一级影响
  console.log('状态:fulfilled'); // 状态:fulfilled
});

除非Promise.then()方法内部抛出异常或者是明确置为rejected态,否则它返回的Promise的状态都是fulfilled态,即完成态,并且它的状态不受它的上一级的状态的影响。

  • 模块

    Node环境中,一个.js文件就称之为一个模块(module) ,模块的导入和导出:

    // 导出模块
    'use strict';
    
    var s = 'Hello';
    
    function greet(name) {
        console.log(s + ', ' + name + '!');
    }
    
    module.exports = greet;  // 导出模块
    
    // 导入模块
    'use strict';
    
    // 引入hello模块:
    var greet = require('./hello');  // 导入模块
    
    var s = 'Michael';
    
    greet(s); // Hello, Michael!
    
    // 导出模块:输出对象/函数
    module.exports = {
        foo: function () { return 'foo'; }
    };
    module.exports = function () { return 'foo'; };
    
  • Node 模块之 fs 文件读写模块

    // 回调函数function 接收两个参数:
    // 如果读取异常时,err为一个错误对象,否则为null
    // data 为读到的字符串,如果异常时,undefined
    'user strict'
    var fs = require('fs')
    
    fs.readFile('sample.txt', 'utf-8', function(err, data) {
        if (err) {
            console.log(err);
        } else {
            console.log(data);
        }
    })
    
    // 读取二进制文件时,不传入文件编码时,data返回一个Buffer对象(直接构成的数组)
    // Buffer和String 转换:
    var text = data.toString('utf-8')
    bar buf = Buffer.from(text, 'utf-8')
    
    // writeFile() 写文件
    var data = 'Hello, Node.js';
    fs.writeFile('output.txt', data, function (err) {
        if (err) {
            console.log(err);
        } else {
            console.log('ok.');
        }
    });
    
    fs.stat('sample.txt', function(err, stat) {
        stat.isFile(); 判断是否是文件
        stat.isDirectory(); 是否是目录
        // 文件时的属性
        stat.size;
        stat.birthtime; // 创建时间
        stat.mtime; // 修改时间
    })
    
    // readFile 和 WriteFile 都是异步执行的;也有同步方法:readFileSync / writeFileSync,基本没啥用。
    /*
    由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。
    */
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值