JavaScript教程学习笔记

1. JavaScript的函数亦是对象。
// 第一种方法:函数声明
function abs() {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}
abs(10); // 返回10
// 第二种方法:函数表达式
var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};
abs(-9); // 返回9

第一种定义的abs()函数实际为一个函数对象,而函数名abs可以视为指向该函数的变量。
第二种function (x) { ... } 为一个匿名函数,然后把这个匿名函数赋值给了变量abs,所以通过变量abs可以调用该函数。

2. 小心return语句。
// 第一种方式
function testReturn(a) {
    return 
        a;
}
var b = testReturn(1);
console.log(b);  // 输出undefined
// 第二种方式
function testReturn(a) {
    return a;
}
var b = testReturn(1);
console.log(b);  // 输出 1;

由于JavaScript引擎在行末自动添加分号的机制,第一种方式种return后会自动变为return;后面语句就无法执行到,所以输出undefined;

3. 变量提升
function foo() {
    var x = 'Hello, ' + y;
    console.log(x);
    var y = 'world';
}
foo(); // 输出 Hello, undefined

JavaScript函数的定义会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但不会提示升变量的赋值。相当于:

function foo() {
    var y; // 提升变量的声明,此时y为undefined
    var x = 'Hello, ' + y;
    console.log(x);
    y = 'world';
}
foo(); // 输出 Hello, undefined
4. 高阶函数(Higher-order function)
function add(x, y, f) {
    return f(x) + f(y);
}
console.log(add(-5, 6, Math.abs)); // 输出11

函数的参数能够接收别的函数,这种函数即高阶函数。

// 示例 1
function pow(x) {
    return x * x;
}
var arr = [1, 2, 3, 4];
var res = arr.map(pow);
console.log(res); // [1, 4, 9, 16]
// 示例 2
var arr = [1, 2, 3, 4];
var res = arr.map(String);
console.log(res); // ["1", "2", "3", "4"]
5. 闭包
function lazySum(arr) {
    var sum = function() {
        return arr.reduce(function(x,y) {
            return x + y;
        });
    }
    return sum;
}
var arr = [1, 2, 3, 4];
var f1 = lazySum(arr);
var f2 = lazySum(arr);
console.log(f1()); // 输出 10
console.log(f2()); // 输出 10
console.log(f1 === f2); // false

1.函数作为返回值;
2.当lazySum返回函数sum时,相关参数和变量都保存在返回的函数中,此时形成了闭包(Closure);
3.调用lazySum()时,每次都返回一个新的函数;
4.返回的函数并没有立即执行,直到调用了f1()才执行;

// 示例 1
    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];

    console.log(f1()); \\ 16
    console.log(f2()); \\ 16
    console.log(f3()); \\ 16

返回的函数中引用了变量 i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量 i 已变成了4,因此最终结果16。

\\ 示例 2
    function count() {
        var arr = [];
        for (var i = 1; i <= 3; i++) {
            arr.push((function(n) {
                return function () {
                    return n * n;
                }
            })(i))
        }
        return arr;
    }
    var res = count();
    var f1 = res[0];
    var f2 = res[1];
    var f3 = res[2];

    console.log(f1()); \\ 1
    console.log(f2()); \\ 4
    console.log(f3()); \\ 9

用到了一个“创建一个匿名函数并立即执行”语句。
无论该循环变量后续如何更改,已绑定到函数参数的值不变。

注:返回闭包时牢记:返回函数不要引用任何循环变量或者后续会发生变化的变量。如果一定要引用循环变量,方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。

6. Generator
function* fib(max) {
        var
            t,
            a = 0,
            b = 1,
            n = 0;
        while (n < max) {
            var c = yield a;
            console.log("c is " + c);
            [a, b] = [b, a + b];
            n ++;
        }
        return;
    }
    var f = fib(3);
    // 方法一
    console.log(f.next(1)); // {value: 0, done: false}
    console.log(f.next(2)); // c is 2 {value: 1, done: false}
    console.log(f.next(3)); // c is 3 {value: 1, done: false}
    console.log(f.next(4)); // c is 4 {value: undefined, done: false}
   // 方法二
  for (var item of f) {
        console.log(item); // 0 1 1
  }

① yield 关键字用来暂停和继续执行一个生成器函数。当外部调用生成器的next()方法时,yield关键字右侧的表达式才会执行。
② 每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束。如果done为true,则value的值为return 的返回值。
③ 对于 var foo = yield expression;yield 左侧变量foo的值将在下一次调用next()方法时获得,并且等于调用next方法的参数。

7. Date
var now = new Date();
    console.log(now); // Tue Jun 05 2018 10:18:12 GMT+0800 (中国标准时间)
    console.log(now.getFullYear()); // 2018, 年份
    console.log(now.getMonth()); // 5, 表示6月,0-11 表示 1-12月
    console.log(now.getDate()); // 5, 表示5号
    console.log(now.getDay()); // 2, 表示周二
    console.log(now.getHours()); // 10, 表示10点,24小时制
    console.log(now.getMinutes()); //18, 分钟
    console.log(now.getSeconds()); // 12, 秒
    console.log(now.getMilliseconds()); //405 , 毫秒
    console.log(now.getTime()); // 1528165092405, 时间戳

Date 对象月份值从0开始,取值范围[0, 11],依次表示 1-12月。

// 创建指定日期
    // 方法一
    var date1 = new Date(2018,5,5,10,15,0);
    console.log(date1); // Tue Jun 05 2018 10:15:00 GMT+0800 (中国标准时间)
    // 方法二
    var d = Date.parse('2015-06-24T19:49:22.875+08:00'); // 参数须符合ISO 8601格式的字符串,返回值为时间戳
    var date2 = new Date(d);
    console.log(date2); // Wed Jun 24 2015 19:49:22 GMT+0800 (中国标准时间)
    // 方法三
    var date3 = new Date(1528165092405);
    console.log(date3); // Tue Jun 05 2018 10:18:12 GMT+0800 (中国标准时间)
8. JSON
/* 
      * 序列化
      */
    var lucy = {
        name: 'Lucy',
        age: 17,
        gender: false,
        height: 165,
        grade: 2,
        skills: ['JavaScript', 'Java', 'Python']
    };
    // 将对象序列化成JSON格式的字符串
    var s1 = JSON.stringify(lucy);
    console.log(s1);

    // 控制格式,按缩进输出
    var s2 = JSON.stringify(lucy, null, ' ');
    console.log(s2);

    // 筛选对象的键值
    var s3 = JSON.stringify(lucy, ['name', 'skills'], ' ');
    console.log(s3);

    // 传入函数,对象的每个键值对都会被函数先处理
    function convert(key, value) {
        if (typeof value === 'string') {
            return value.toUpperCase();
        }
        return value;
    }
    var s4 = JSON.stringify(lucy, convert, ' ');
    console.log(s4);

    // toJSON
    var andy = {
        name: 'andy',
        age: 17,
        gender: true,
        height: 175,
        skills: ['JavaScript', 'Java', 'Python'],
        toJSON: function() {
            return {
                'Name': this.name,
                'Age': this.age
            }
        }
    }
    var s5 = JSON.stringify(andy);
    console.log(s5);
输出结果
/*
     * 反序列化
     */

    // 将一个JSON格式的字符串变成一个JavaScript对象
    console.log(JSON.parse('[1, 2, 3, true]')); // [1, 2, 3, true]
    console.log(JSON.parse('{"name": "andy", "age": "17"}')); // {name: "andy", age: "17"}
    console.log(JSON.parse('true')); // true
    console.log(JSON.parse('123.45')); // 123.45

    // 传入函数用来转换解析出的属性
    var obj = JSON.parse('{"name": "小明", "age": "17"}', function(key, value) {
        if (key === 'name') {
            return value + '同学';
        }
        return value;
    });
    console.log(JSON.stringify(obj)); // {"name":"小明同学","age":"17"}
9. 面向对象

① JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。

// 示例 1
var arr = [1, 2, 3];
原型链为:
arr ---- > Array.prototype ---- > Object.prototype ---- > null
// 示例 2
function foo() { ... }
原型链为:
foo ---- > Function.prototype ---- > Object.prototype ---- > null

② 构造函数

/* 
 *创建对象
 */
//  方法一
var obj = { ... }

// 方法二 构造函数
function Student(name) {
  this.name = name;
  this.hello = function() {
    console.log('Hello ' + this.name);
  }
}
var andy = new Student('Andy');
console.log(andy.name); // Andy
andy.hello(); // Hello Andy

andy.constructor === Student.prototype.construtor; // true
Student.prototype.constructor === Student; // true
Object.getPrototypeOf(andy) === Student.prototype; // true
andy instanceof Student; // true;

注: 如果不用关键字new,函数Student即一个普通函数,返回undefined。用new去调用之后,变成一个构造函数,绑定的this指向新创建的对象,并默认返回this,无需在函数体末尾写return this;

  • andy原型链为:
    andy ---- > Student.prototype ---- > Object.prototype ---- > null
  • 用 new Student() 创建的对象从原型上获得一个constructor属性,指向函数Student本身。

③ 原型继承

function inherits(Child, Parent) {
        var F = function () {};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
    }
    function Student(props) {
        this.name = props.name || 'Unnamed';
    }
    Student.prototype.hello = function() {
        console.log('Hello' + this.name);
    }
    function PrimaryStudent(props) {
        Student.call(this, props);
        this.grade = props.grade || 1;
    }
    // 实现原型继承链
    inherits(PrimaryStudent, Student);
    var andy = new PrimaryStudent({
        name: 'Andy',
        grade: '2'
    });
    console.log(andy.name); // Andy
    console.log(andy.grade); // 2
    // 验证原型
    console.log(andy.__proto__ === PrimaryStudent.prototype); // true
    console.log(andy.__proto__.__proto__ === Student.prototype); // true
    // 验证继承关系
    console.log(andy instanceof PrimaryStudent); // true
    console.log(andy instanceof Student); // true

④ class 继承

// Student 类
    class Student {
        constructor(name) {
            this.name = name;
        }
        hello() {
            console.log('Hello ' + this.name);
        }
    }
    var andy = new Student('Andy');
    andy.hello(); // Hello Andy
    // 实现继承
    class PrimaryStudent extends Student {
        constructor(name, grade){
            super(name); // super调用父类的构造函数
            this.grade = grade;
        }
        myGrade() {
            console.log(this.name + ' at grade ' + this.grade);
        }
    }
    var lucy = new PrimaryStudent('Lucy', 1);
    lucy.myGrade(); // Lucy at grade 1
10. Promise
new Promise(function(resolve, reject) {
        console.log('start promise...');
        var t = Math.random() * 2;
        console.log("Timeout is " + t );
        setTimeout(function(){
            if (t <1) {
                console.log('call resolve()...');
                resolve('200 OK');
            } else {
                console.log('call reject()...');
                reject('timeout in ' + t);
            }
        }, t * 1000);
    }).then(function(r) {
        console.log('Done: ' + r);
    }).catch(function(reason) {
        console.log('Failed: ' + reason);
    });
输出结果
// Promise.all() --- 并行执行异步任务
var p1 = new Promise(function(resolve, reject) {
     setTimeout(resolve, 500, 'P1');
 });
 var p2 = new Promise(function(resolve, reject) {
      setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then
Promise.all([p1, p2]).then(function(results) {
      console.log(results); // 输出 ["P1", "P2"]
});
// 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(results) {
    console.log(results); //  P1
});
11. 错误处理
/* 
    * try ... catch ... finally
   */
    var r1, r2, s = null;
    try {
        r1 = s.length; // 产生错误
        r2 = 100; // 不会执行
    } catch (e) {
        console.log('出错了:' + e);
        // Error 派生 TypeError、ReferenceError
        if (e instanceof TypeError) {
            console.log('Type Error'); // 输出 Type Error
        } else if (e instanceof Error) {
            console.log(e.message);
        }
    } finally {
        console.log('finally always execute'); // 无论是否有错误,finally一定会被执行
    }
    console.log('r1= ' + r1);
    console.log('r2 = ' + r2);
输出结果
/*
     * 抛出错误
     */
    var num;
    try {
        num = prompt('输入一个数字');
        if (isNaN(parseInt(num))) {
            throw new Error('输入错误'); // 抛出异常
        }
        console.log(num + '*' + num + '=' + num * num);
    } catch(e) {
        // 当输入的不为数字时,捕获抛出的异常
        console.log('出错了:' + e); // 出错了:Error: 输入错误
    }

注: 异步代码和事件绑定代码处,无法捕获事件处理函数的错误。

12. underscore

jQuery在加载时,会把自身绑定到唯一的全局变量$上,underscore与其类似,会把自身绑定到唯一的全局变量_上。

  • Collections
    ① map/filter
var obj = {
    name: 'Andy',
    age: 17,
    grade: 1
}
var obj1 = _.map(obj, function(value, key){
    return value;
});
console.log(JSON.stringify(obj1)); // ["Andy", 17 ,1]

var obj2 = _.filter(obj, function(value, key) {
    return typeof value === 'string';
})
console.log(JSON.stringify(obj2)); // ["Andy"]

② every/some

//  _.every() -- 所有元素满足条件返回true;_.some() -- 有一个元素满足条件,返回true
var arr = [1, 4, 7, -3, -9];
var flag1 = _.every(arr, (x) => x > 0);
var flag2 = _.some(arr, (x) => x > 0);
console.log(flag1); // false
console.log(flag2); // true

③ max/min

var arr = [1, 2, 3, 4];
var obj = {
    a: 1,
    b: 2,
    c: 3
};

console.log(_.max(arr)); // 4
console.log(_.min(arr)); // 1
// 空数组会返回-Infinity和Infinity
console.log(_.max([])); // -Infinity
console.log(_.min([])); // Infinity
// 对象会忽略键名
console.log(_.max(obj)); // 3;
console.log(_.min(obj)); // 1

④ groupBy

// 把集合的元素按照key归类, key由传入的函数返回
var scores = [20, 81, 75, 40, 91, 59, 77, 66, 75, 88, 99];
var groups = _.groupBy(scores, function(x) {
    if (x < 60) {
        return 'C';
    } else if (x < 80) {
        return 'B';
    } else {
        return 'A';
    }
});
console.log(groups); // {A: [81, 91, 88, 99], B: [75, 77, 66, 75], C: [20, 40, 59]}

⑤ shuffle/sample

var arr = [1, 2, 3, 4, 5, 6];

// shuffle() 用洗牌算法随机打乱一个集合(每次结果都不一样)
// shuffle(list)
console.log(_.shuffle(arr)); // [2, 1, 3, 6, 4, 5]

// sample() 随机选择一个或多个元素(每次结果都不一样)
// sample(list, n)
console.log(_.sample(arr)); // 5
console.log(_.sample(arr, 3)); // [2, 5, 3]
  • Arrays
    ① first / last
var arr = [1, 2, 3, 4];
// 取第一个元素
console.log(_.first(arr)); // 1
// 取最后一个元素
console.log(_.last(arr)); // 4

② flatten

// 无论嵌套了多少个Array,最后变成一个一维数组
var arr = [1, [2], [3, [[4], [5]]]];
console.log(_.flatten(arr)); // [1, 2, 3, 4, 5]

③ zip / unzip

// zip()把两个或多个数组的所有元素按索引对齐,然后按索引合并成新数组
var names = ["Andy", "Lucy", "Lily"];
var scores = [90, 96, 98];
console.log(_.zip(names, scores)); // [["Andy", 90], ["Lucy", 96], ["Lily], 98]
// unzip 反过来
var namesAndScores = [["Any", 90], ["Lucy", 96], ["Lily", 98]];
console.log(_.unzip(namesAndScores)); // [["Andy", "Lucy", "Lily"], [90, 96, 98]]

④ object

// object() 变成一个对象
var names = ["Andy", "Lucy", "Lily"];
var scores = [90, 96, 98];
console.log(_.object(names, scores)); // {Andy: 90, Lucy: 96, Lily: 98}

⑤ range

// range(start, end, step) 快速生成一个序列
// 从0开始小于10:
console.log(_.range(10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// 从1开始小于11:
console.log(_.range(1, 11)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 从0开始小于30,步长5:
console.log(_.range(0, 30, 5)); // [0, 5, 10, 15, 20, 25]

// 从0开始大于-10,步长-1:
console.log(_.range(0, -10, -1)); // [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
  • Function
    ① bind
var s = ' Hello ';
// 直接调用
console.log(s.trim()); // Hello
// 用一个变量保存起来
var fn = s.trim;
console.log(fn.call(s));  // Hello
// 用bind把对象s绑定在f()的指针上
var f = _.bind(s.trim, s);
console.log(f()); // Hello

② partial

// 为函数创建一个片函数 例如:2的y次方
var pow2N = _.partial(Math.pow, 2);
console.log(pow2N(3)); // 8
console.log(pow2N(5)); // 32

③ memoize

// 缓存上次的计算结果,如果调用的函数相同
var factorial = _.memoize(function(n) {
    console.log('start calculate ' + n + '!...');
    var s = 1, i = n;
    while (i > 1) {
        s = s * i;
        i --;
    }
    console.log(n + '! = ' + s);
    return s;
});
// 第一次调用 
factorial(10); // start calculate 10!... 10! = 3628800
console.log(factorial(10)); // 仅输出 3628800 ;没有重新计算
factorial(9); // start calculate 9!... 9! = 362880

④ delay

// delay() 作用和 setTimeout() 一样
_.delay(console.log('2 秒后输出'), 2000); // 2  秒后输出
  • Objects
    ① keys / allKeys
// keys()可以非常方便地返回一个object自身所有的key,但不包含从原型链继承下来的
function Student(name, age) {
    this.name = name;
    this.age = age;
}
var andy = new Student("Andy", 17);
console.log(_.keys(andy)); //["name", "age"]
// allKeys()除了object自身的key,还包含从原型链继承下来的
function Student(name, age) {
    this.name = name;
    this.age = age;
}
Student.prototype.school = "No.1 Middle School";
var lucy = new Student("Lucy", 17);
console.log(_.allKeys(lucy)); // ["name", "age", "school"]

② values

// values()返回object自身但不包含原型链继承的所有值
var obj = {
    name: "Andy",
    age: 17
};
console.log(_.values(obj)); // ["Andy", 17]

③ mapObject

var obj = {
    a: 1,
    b: 2,
    c: 3
};
console.log( _.mapObject(obj, (v, k) => 100 + v)); // {a: 101, b: 102, c: 103}

④ invert

// 把对象的key-value作对话
var obj = {
    a: 1,
    b: 2,
    c: 3
};
console.log(_.invert(obj)); // {1: 'a', 2: 'b', 3: 'c'}

⑤ extend / extendOwn

// extend()把多个object的key-value合并到第一个object并返回
// extendOwn()和extend()类似,但获取属性时忽略从原型链继承下来的属性。
var a = {
    a: 1,
    b: 2,
    c: 3
};
_.extend(a, {d: 4}, {e: 5}, {f: 6, g: 7});
console.log(a); // {a: 1, b: 2, c: 3, d: 4, e: 5, f:6, g:7}

⑥ clone(浅拷贝)

// 复制一个object对象,把原有对象的所有属性都复制到新的对象中
var obj1 = {
    a: 1,
    b: 2,
    c: 3
};
var obj2 = _.clone(obj1);

console.log(obj1); // {a: 1, b:2, c: 3}
console.log(obj2); // {a: 1, b:2, c: 3}

delete obj1.a; // 删除obj1中属性,不会影响obj2
console.log(obj1); // {b: 2, c: 3}
console.log(obj2); // {a: 1, b:2, c: 3}

⑦ isEqual

// 对两个object进行深度比较,如果内容完全相同,则返回true
// 比较对象
var o1 = { name: 'Bob', skills: { Java: 90, JavaScript: 99 }};
var o2 = { name: 'Bob', skills: { JavaScript: 99, Java: 90 }};

console.log(o1 === o2); // false
console.log(_.isEqual(o1, o2)); // true

// 比较数组
var arr1 = ['Bob', { skills: ['Java', 'JavaScript'] }];
var arr2 = ['Bob', { skills: ['Java', 'JavaScript'] }];

console.log(arr1 === arr2); // false
console.log(_.isEqual(arr1, arr2)); // true

⑧ chain

// 把对象包装成能进行链式调用的方法
var arr = [1, 4, 9 ,16, 25];
var res = _.chain(arr).map(Math.sqrt).filter(x => x % 2 === 1).value();
console.log(res); // [1, 3, 5]

参考JavaScript教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值