前端进阶之路前端社团js基础

JavaScript学习笔记.md

2017-07-06  本文已影响182人  ccminn

学习材料————廖雪峰js教程

数据类型

===与==

== 自动转换数据类型再比较;
=== 不转换类型 (更好)
例外:
NaN === NaN // false

字符串

使用反引号来表示多行字符串

var a = `这是一个
多行
字符串`;

字符串不可变:使用a[0] = 'X'并没有改变效果

数组

直接给array的length赋新值会改变Array的大小;
length变大则新增元素为undefined;变小则删除超出长度的元素;

数组方法

slice   // 不加参数可以用于复制数组,不改变本身
pop
push
unshift // 在头部添加
shift   // 在头部删除
sort    // 字母排序
reverse // 反转
splice  // 从指定索引删除若干元素,在从该位置添加若干元素
concat  // 拼接,返回新Array
join    // 使用指定字符拼接数组元素 ==> String

Map 键值对结构,大数据量时具有极快的查找速度

根据key值迅速获取对应value

var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined

Set 只有非重复key,没有value值

var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
// 添加元素
s.add(4);
s; // {1, 2, 3, 4}
// 删除元素
s.delete(3);

iterable类型

包括Array、Map、Set等;
for。。。in
循环对象的属性名称key;一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。
for。。。of
循环对象键值对中的value;更适合遍历没有key的集合,如Set;
forEach方法 (更好)

a.forEach(function (element, index, array) {
    // element: 指向当前元素的值
    // index: 指向当前索引
    // array: 指向Array对象本身
    alert(element);
});

// Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身:
// Map的回调函数参数依次为value、key和map本身:

函数arguments参数

它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array:
利用arguments,你可以获得调用者传入的所有参数。即使函数不定义任何参数,还是可以拿到参数的值

当传入参数个数超过函数定义的参数个数,则将多余参数传入rest参数。

// rest参数只能写在最后,前面用...标识
// 传入的参数先绑定a、b,多余的参数以数组形式交给变量rest
// 所以,不再需要arguments我们就获取了全部参数。
function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 结果:
// a = 1
// b = undefined
// Array []

全局对象 window

全局作用域的变量实际绑定到window的一个属性

'use strict';

var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript'

// 全局变量course 等同于 window.course
// alert函数也是window的一个变量

减少全局变量的冲突:声明一个唯一的全局变量{};然后把所有变量和函数都绑定到这个唯一一个全局变量上。

局部作用域

for循环中,var i可以在循环块外部被引用;ES6的let i则解决了这个问题。

'use strict';

function foo() {
    for (var i=0; i<100; i++) {
        //
    }
    i += 100;      // 仍然可以引用变量i
}


'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    i += 1;       // SyntaxError
}

this丢失问题

对象方法中的this,必须由对象进行调用该方法才能获取this;
对象方法中的函数,this指向全局对象window;
解决方法1: that

// 保存当前this  ===> that
'use strict';

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: function () {
        var that = this; // 在方法内部一开始就捕获this
        function getAgeFromBirth() {
            var y = new Date().getFullYear();
            return y - that.birth; // 用that而不是this
        }
        return getAgeFromBirth();
    }
};

xiaoming.age(); // 25

解决方法2: apply与call

// 使用apply或call,控制函数的this指向传入的首个参数
function getAge() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getAge
};

xiaoming.age(); // 25

// apply()把参数打包成Array再传入;
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
// 把参数按顺序传入。
getAge.call(xiaoming)

解决方法3: IFEE思想,bind(this)

高阶函数

函数参数指向函数

map逐一映射

// *****
// “把f(x)作用在Array的每一个元素并把结果生成一个新的Array”。
// *****
function pow(x) {
    return x * x;
}

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce瀑布映射

原理:[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

// *****
// 把结果继续和序列的下一个元素做累积计算
// *****

// [1, 3, 5, 7, 9]变换成整数13579,reduce()也能派上用场:

var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x * 10 + y;
}); // 13579

filter过滤

filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。

// 利用filter,可以巧妙地去除Array的重复元素:

'use strict';

var r,
    arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];

r = arr.filter(function (element, index, self) {
    // indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,被过滤
    return self.indexOf(element) === index;
});

alert(r.toString());

sort 排序

sort()方法会直接对Array进行修改,它返回的结果仍是当前Array:
默认把所有元素先转换为String再排序,字符串根据首个字符的ASCII码进行排序
sort的自定义排序中,return -1不对调;return 1对调;

// 升序排列
var arr = [10, 20, 1, 2];
arr.sort(function (x, y) {
    if (x < y) {
        return -1;
    }
    if (x > y) {
        return 1;
    }
    return 0;
}); // [1, 2, 10, 20]

箭头函数

x => x * x   

等同于  

function (x) {
    return x * x;
}

箭头函数相当于匿名函数
格式一,只包含一个表达式,可以省略{ ... }和return
格式二,包含多条语句,不能省略{ ... }和return:

参数

// 两个参数:
(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;
}

返回对象

返回对象时存在语法冲突{} 所以改为({foo:x})

this作用域

箭头函数修复了this的指向,this总是指向词法作用域,也就是外层调用者obj:
也就是说,在对象(第一层)的方法(第二层)的函数(第三层)中,仍然可以用this调用到对象的属性值;

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth;    //this指向obj对象
        return fn();
    }
};
obj.getAge(); // 25

call()与apply()

由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定,即传入的第一个参数被忽略,直接传入参数即可

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth仍是1990
        return fn.call({birth:2000}, year);
        // 原本需要写作fn.call(this, {birth:2000}, year)
    }
};
obj.getAge(2015); // 25

generator生成器

yield 用于return之前的多次返回
generator

// 定义
function* foo(x) {
    yield x + 1;
    yield x + 2;
    return x + 3;
}

// 创建generator对象   与函数调用不同,不能直接调用
var f = foo(3);    // {[[GeneratorStatus]]: "suspended"}

// 实际获取返回值   done属性表示执行完毕
f.next();    // {value: 4, done: false}
f.next();    // {value: 5, done: false}
f.next();    // {value: 6, done: true}
f.next();    // {value: undefined, done: true}


// 直接用for ... of循环迭代generator对象,这种方式不需要我们自己判断done
for (var x of f(3)) {
    console.log(x); // 依次输出4,5,6
}

generator的优点:
1.因为generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。

2.把异步回调代码变成形似同步代码(主要是美观,实际仍未异步)

// 原来的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);
        });
    });
});

// 使用generator美化之后
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);
}

对象

// 标准对象
typeof 123; // 'number'
typeof NaN; // 'number'
typeof 'str'; // 'string'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Math.abs; // 'function'
typeof null; // 'object'
typeof []; // 'object'
typeof {}; // 'object'


// 包装对象  拆包前是Object;解包后是普通数据类型
var n = new Number(123); // 123,生成了新的包装类型
var b = new Boolean(true); // true,生成了新的包装类型
var s = new String('str'); // 'str',生成了新的包装类型

正则表达式

regExp.test(str); 返回值:false/trues
用途:

  1. 切割字符串
'a,b;; c  d'.split(/[\s\,\;]+/); // ['a', 'b', 'c', 'd']
  1. 分组,提取子串
var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); // null

JSON

字符串化 stringify

var xiaoming = {
    name: '小明',
    age: 14,
    gender: true,
    height: 1.65,
    grade: null,
    'middle-school': '\"W3C\" Middle School',
    skills: ['JavaScript', 'Java', 'Python', 'Lisp']
};

// 原始字符串显示
JSON.stringify(xiaoming);

// 缩进显示 
JSON.stringify(xiaoming, null, '  ');

// 传入Array,输出指定的属性
JSON.stringify(xiaoming, ['name', 'skills'], '  ');

// 传入一个函数,处理对象的每个键值
function convert(key, value) {
    if (typeof value === 'string') {
        return value.toUpperCase();
    }
    return value;
}

JSON.stringify(xiaoming, convert, '  ');

// JSON数据添加toJSON属性,控制JSON应该序列化的数据
toJSON: function () {
        return { // 只输出name和age,并且改变了key:
            'Name': this.name,
            'Age': this.age
        };
    }

JSON.stringify(xiaoming); // '{"Name":"小明","Age":14}'

反序列化 JSON.parse

JSON.parse()还可以接收一个函数,用来转换解析出的属性:

JSON.parse('{"name":"小明","age":14}', function (key, value) {
    // 把number * 2:
    if (key === 'name') {
        return value + '同学';
    }
    return value;
}); // Object {name: '小明同学', age: 14}

构造函数

构造函数与普通函数的声明几乎没有区别。
但是必须通过new来调用构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;
如果不写new,这就是一个普通函数,它返回undefined。

// 生成的每个实例都会有独立的name属性与hello属性;hello的函数代码重复,浪费内存空间
function Student(name) {
    this.name = name;
    this.hello = function () {
        alert('Hello, ' + this.name + '!');
    }
}

//  把hello函数放在prototype下,可以让不同的实例共享同个函数,节省内存空间
function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
};

原型继承(其实ES6的class优化更容易理解、好用)

重点理解!!!prototype指向的是一个对象,而不是prototype指向prototype属性

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null
必须想办法把原型链修改为:
new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null
不能直接改变指向;
必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Student.prototype,中间对象可以用一个空函数F来实现


// PrimaryStudent构造函数:
function PrimaryStudent(props) {
    // 调用了Student构造函数,绑定this变量
    // 但是PrimaryStudent的prototype并不指向Student;这并不是继承
    Student.call(this, props);    
    this.grade = props.grade || 1;
}

// 空函数F:
function F() {
}

// 把F的原型指向Student.prototype:
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();

// 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

// 创建xiaoming:
var xiaoming = new PrimaryStudent({
    name: '小明',
    grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2

// 验证原型:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true

// 验证继承关系:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true
// 封装空函数继承过程
function inherits(Child, Parent) {
    var F = function () {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}

// 实现原型继承链:
inherits(PrimaryStudent, Student);

class继承 ===> 原型继承的优化

// 使用构造函数
function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

// 使用class实现
class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

// 实例化
var xiaoming = new Student('小明');
xiaoming.hello();
// 使用extends关键字实现继承
class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 记得用super调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

浏览器对象

window对象

window对象不但充当全局作用域,而且表示浏览器窗口。

window对象有innerWidth和innerHeight属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。

navigator对象

navigator对象表示浏览器的信息,最常用的属性包括:

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

screen对象

screen对象表示屏幕的信息,常用的属性有:

screen.width:屏幕宽度,以像素为单位;
screen.height:屏幕高度,以像素为单位;
screen.colorDepth:返回颜色位数,如8、16、24。

location对象

location对象表示当前页面的URL信息。例如,一个完整的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()
重新加载当前页面,location.reload()

document对象

document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。

document对象还有一个cookie属性,可以获取当前页面的Cookie。

Cookie是由服务器发送的key-value标示符。因为HTTP协议是无状态的,但是服务器要区分到底是哪个用户发过来的请求,就可以用Cookie来区分。当一个用户成功登录后,服务器发送一个Cookie给浏览器,例如user=ABC123XYZ(加密的字符串)...,此后,浏览器访问该网站时,会在请求头附上这个Cookie,服务器根据Cookie即可区分出用户。

Cookie还可以存储网站的一些设置,例如,页面显示的语言等等。

JavaScript可以通过document.cookie读取到当前页面的Cookie:

为了解决这个问题,服务器在设置Cookie时可以使用httpOnly,设定了httpOnly的Cookie将不能被JavaScript读取。这个行为由浏览器实现,主流浏览器均支持httpOnly选项,IE从IE6 SP1开始支持。
为了确保安全,服务器端在设置Cookie时,应该始终坚持使用httpOnly。
效果==> 保证浏览器的document对象中就看不到cookie,不能通过客户端脚本访问;只能通过服务器端的http请求

DOM更新、插入、删除

修改节点

innerHTML: 以HTML的形式插入代码段,可更新节点的结构;
innerTEXT: 以字符串的形式插入到节点,内含的html标签失效;
element.style.color = 'red'; 通过DOM节点的style属性修改css

添加节点

document.createElement('p'); element.appendChild(newElement) 如果新节点已存在,不是简单在尾部添加,而是剪切该元素到目标位置;如果是一直创建新标签,则一直重复插入;
document.createElement('p'); element.insertBefore(newElement, referenceElement); 如果新节点已存在,就不是简单在节点前添加,而是剪切该元素到目标位置;同append;

删除节点

找到目标删除节点,寻至父节点,删除子节点; 被删除的节点仍然保存在内存中,可以再次插入 
// 拿到待删除节点:
var self = document.getElementById('to-be-removed');
// 拿到父节点:
var parent = self.parentElement;
// 删除:
var removed = parent.removeChild(self);
removed === self; // true

表单

使用onsubmit 与type="submit"进行提交表单;使用click会扰乱form的正常提交

<!-- HTML -->
<form id="test-form" onsubmit="return checkForm()">
    <input type="text" name="test">
    <button type="submit">Submit</button>
</form>

<script>
function checkForm() {
    var form = document.getElementById('test-form');
    // 可以在此修改form的input...
    // 继续下一步:
    return true;
    // return true来告诉浏览器继续提交
    // return false,浏览器将不会继续提交form,这种情况通常对应用户输入有误,提示用户错误信息后终止提交form。
}
</script>

HTML5中的File API允许JS读取文件内容

html5提供了 FileFileReader 两个主要对象,可以获得文件信息并读取文件。
File读取文件属性信息; FileReader读取文件内部数据(比如图片的base64)

// fileInput:选择文件的按钮; info:读取的文件属性信息;  preview:页面中的预览框  
var fileInput = document.getElementById('test-image-file'),
    info = document.getElementById('test-file-info'),
    preview = document.getElementById('test-image-preview');
// 监听change事件:
fileInput.addEventListener('change', function () {
    // 清除背景图片:
    preview.style.backgroundImage = '';
    // 检查文件是否选择:
    if (!fileInput.value) {
        info.innerHTML = '没有选择文件';
        return;
    }
    // 获取File引用:
    var file = fileInput.files[0];
    // 获取File信息:
    info.innerHTML = '文件: ' + file.name + '<br>' +
                     '大小: ' + file.size + '<br>' +
                     '修改: ' + file.lastModifiedDate;
    if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
        alert('不是有效的图片文件!');
        return;
    }
    // 读取文件:
    var reader = new FileReader();
    reader.onload = function(e) {
        var
            data = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...'            
        preview.style.backgroundImage = 'url(' + data + ')';
    };
    // 以DataURL的形式读取文件:
    reader.readAsDataURL(file);
});

AJAX

在现代浏览器上写AJAX主要依靠XMLHttpRequest对象
根据状态码判断响应进度

// 原生AJAX的写法

function success(text) {
    var textarea = document.getElementById('test-response-text');
    textarea.value = text;
}

function fail(code) {
    var textarea = document.getElementById('test-response-text');
    textarea.value = 'Error code: ' + code;
}

var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象

// 定义request的响应回调函数
request.onreadystatechange = function () { // 状态发生变化时,函数被回调
    if (request.readyState === 4) { // 成功完成
        // 判断响应结果:
        if (request.status === 200) {
            // 成功,通过responseText拿到响应的文本:
            return success(request.responseText);
        } else {
            // 失败,根据响应码判断失败原因:
            return fail(request.status);
        }
    } else {
        // HTTP请求还在继续...
    }
}

// 发送请求:
request.open('GET', '/api/categories');
request.send();

// XMLHttpRequest对象的open()方法有3个参数,第一个参数指定是GET还是POST,第二个参数指定URL地址,第三个参数指定是否使用异步,默认是true,所以不用写。
// 最后调用send()方法才是真正发送请求。
// GET请求不需要参数,POST请求需要把body部分以字符串或者FormData对象传进去。

alert('请求已发送,请等待响应...');

// 使用Promise的简化写法
// ajax函数将返回Promise对象:
function ajax(method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                }
            }
        };
        request.open(method, url);
        request.send(data);
    });
}

var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,获得响应内容
    log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
    log.innerText = 'ERROR: ' + status;
});




跨域请求

大多数浏览器只允许在js中请求域名、协议、端口相同的URL;因此存在跨域问题。
解决方法:
1.使用flash插件发送http请求;必须安装flash,不考虑。
2.在同源域名下架设一个代理服务器来转发,JavaScript负责把请求发送到代理服务器,代理服务器再把结果返回,这样就遵守了浏览器的同源策略。这种方式麻烦之处在于需要服务器端额外做开发。
3.JSONP,但只能用GET请求,并且要求返回JavaScript。这种方式跨域实际上是利用了浏览器允许跨域引用JavaScript资源;通常以函数调用的形式返回。


以163的股票查询URL为例,对于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice,你将得到如下返回(一个带参的函数调用):

refreshPrice({"0000001":{"code": "0000001", ... });
因此我们需要首先在页面中准备好回调函数:

function refreshPrice(data) {
    var p = document.getElementById('test-jsonp');
    p.innerHTML = '当前价格:' +
        data['0000001'].name +': ' + 
        data['0000001'].price + ';' +
        data['1399001'].name + ': ' +
        data['1399001'].price;
}

最后用getPrice()函数触发:通过新建script标签,访问API,调用refreshPrice函数,使用自定义的refreshPrice渲染新数据

function getPrice() {
    var js = document.createElement('script'),
        head = document.getElementsByTagName('head')[0];
    js.src = 'http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice';
    head.appendChild(js);
}

4.CORS (Cross-Origin Resource Sharing)是HTML5规范定义的如何跨域访问资源。
跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的Access-Control-Allow-Origin,决定权始终在对方手中。

Promise 承诺将来会执行

效果: 把原本的一种回调细分为两种回调

// 变量p1是一个Promise对象,它负责执行test函数。test函数在内部是异步执行的
// 当test函数执行成功时,我们告诉Promise对象,如果成功,执行.then()
// 当test函数执行失败时,我们告诉Promise对象,如果失败,执行.catch()
// 通过test函数中的resolve与reject来判断函数执行成功与失败  


// 链式简写
new Promise(test).then(function (result) {
    console.log('成功:' + result);
}).catch(function (reason) {
    console.log('失败:' + reason);
});


new Promise(function (resolve, reject) {
    log('start new Promise...');
    var timeOut = Math.random() * 2;
    log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            log('call resolve()...');
            resolve('200 OK');
        }
        else {
            log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}).then(function (r) {
    console.log('Done: ' + r);
}).catch(function (reason) {
    console.log('Failed: ' + reason);
});

Promise串行执行异步任务


job1.then(job2).then(job3).catch(handleError);

// job1、2、3都是Promise对象

Promise 并行执行多个异步任务

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


// 并行执行异步任务   Promise.all() 以数组形式返回数据
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});


// 多个异步任务竞争 ===> 为了容错,只需要获得先返回的结果即可;后完成的任务仍在执行,但执行结果将被丢弃
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
})

jQuery

jQuery === $ // 本质上是function,而function也是对象,于是$可以直接调用,也有很多其他属性。

组合查找: $('input[name=email]')
多项选择: $('p.red,p.green'); // 把<p class="red">和<p class="green">都选出来

查找

.find('')
.next()  // 可加参数,也可不加
.prev()  // 可加参数,也可不加

过滤

// 以下的[] 指若干DOM节点
[].filter('')  // 过滤掉不符合过滤条件的
[].filter(function(){ 
        return this.innerHTML.indexOf('S') === 0
        })   // 仅保留S开头的节点
() => [].map(function(){
    return this.innerHTML;
}).get();   // 用get()拿到包含string的Array

[].first()     //获取若干DOM节点的首个节点
[].last()      //获取若干DOM节点的末尾节点
[].slice()     //截取若干DOM节点的若干节点

修改DOM 与 CSS 与 添加、删除DOM

// 获取 
$().text()
$().html()

// 修改
$().text('')
$().html('')

// 修改css
$().css('key','value')
var div = $('#test-div');
div.css('color'); // 获取CSS属性
div.css('color', '#336699'); // 设置CSS属性
div.css('color', ''); // 清除CSS属性
div.hasClass('highlight'); // false, class是否包含highlight
div.addClass('highlight'); // 添加highlight这个class
div.removeClass('highlight'); // 删除highlight这个class

// 修改display属性
a.hide(); // 隐藏
a.show(); // 显示

// 获取DOM信息
// 浏览器可视窗口大小:
$(window).width(); // 800
$(window).height(); // 600

// HTML文档大小:
$(document).width(); // 800
$(document).height(); // 3500

// 某个div的大小:
var div = $('#test-div');
div.width(); // 600
div.height(); // 300
div.width(400); // 设置CSS属性 width: 400px,是否生效要看CSS是否有效
div.height('200px'); // 设置CSS属性 height: 200px,是否生效要看CSS是否有效

// 属性操作
div.attr('name', 'Hello');    // 设置属性
div.removeAttr('name');       // 移除属性
div.attr('name');             // 获取属性

// 无值属性,使用is()来判断
<input id="test-radio" type="radio" name="test" checked value="1">
var radio = $('#test-radio');
radio.is(':checked'); // true

// val()方法获取和设置对应的value属性:
$().val()
$().val('')


// 添加DOM
// 要添加的DOM节点已经存在于HTML文档中,它会首先从文档移除,然后再添加,相当于移动节点
$().append();
$().after();
、删除DOM
$().remove(); //移除本身

jQuery事件

事件绑定

$().on('event', fn)
$().on('click', fn) === $().click(fn)

鼠标事件
click: 鼠标单击时触发;
dblclick:鼠标双击时触发;
mouseenter:鼠标进入时触发;
mouseleave:鼠标移出时触发;
mousemove:鼠标在DOM内部移动时触发;
hover:鼠标进入和退出时触发两个函数,相当于mouseenter加上mouseleave。

键盘事件
键盘事件仅作用在当前焦点的DOM上,通常是<input>和<textarea>。
keydown:键盘按下时触发;
keyup:键盘松开时触发;
keypress:按一次键后触发。

其他事件
focus:当DOM获得焦点时触发;
blur:当DOM失去焦点时触发;
change:当<input>、<select>或<textarea>的内容改变时触发;
submit:当<form>提交时触发;
ready:当页面被载入并且DOM树完成初始化后触发。仅作用于document对象
ready事件的三次简化:   
`document.on('ready',fn)` ===>  `$(document).ready(fn)` ===> `$(function () {})`
如果你遇到$(function () {...})的形式,牢记这是document对象的ready事件处理函数。

取消绑定


$().off('click', fn);  // fn只能传入绑定时的函数,不可包含定义

off('click')           // 一次性移除已绑定的click事件的所有处理函数。

// 无效的解除绑定,因此此处的fn对应的是一个新函数
a.off('click', function () {
    alert('hello!');
});

事件触发条件

// 监控改动
$().change(fn)

jQuery AJAX

var jqxhr = $.ajax('/api/categories', {
    dataType: 'json'
});
// 请求已经发送了

ajax的链式调用

// ajax的链式调用
var jqxhr = $.ajax('/api/categories', {
    dataType: 'json'
}).done(function (data) {
    // 请求成功
    ajaxLog('成功, 收到的数据: ' + JSON.stringify(data));
}).fail(function (xhr, status) {
    // 请求失败
    ajaxLog('失败: ' + xhr.status + ', 原因: ' + status);
}).always(function () {
    // 必然调用
    ajaxLog('请求完成: 无论成功或失败都会调用');
});

辅助方法:

// get方法,直接传参;获取返回的json
var jqxhr = $.get('/path/to/resource', {
    name: 'Bob Lee',
    check: 1
}).done(function (data) {
    // data已经被解析为JSON对象了
});

// post方法,数据作为body被发送
var jqxhr = $.post('/path/to/resource', {
    name: 'Bob Lee',
    check: 1
});

jQuery插件扩展

给$.fn绑定函数,实现插件的代码逻辑;
插件函数最后要return this;以支持链式调用;
插件函数要有默认值,绑定在$.fn.<pluginName>.defaults上;
用户在调用时可传入设定值以便覆盖默认值。

// 给jQuery对象绑定一个新方法是通过扩展$.fn对象实现的。
$.fn.highlight = function (options) {
    // 合并默认值和用户设定值:
    // 使用jQuery提供的辅助方法$.extend(target, obj1, obj2, ...),它把多个object对象的属性合并到第一个target对象中,遇到同名属性,总是使用靠后的对象的值,也就是越往后优先级越高:
    var opts = $.extend({}, $.fn.highlight.defaults, options);
    // 函数内部的this在调用时被绑定为jQuery对象,所以函数内部代码可以正常调用所有jQuery对象的方法。
    this.css('backgroundColor', opts.backgroundColor).css('color', opts.color);
    return this;
    // 添加添加return this,是因为jQuery对象支持链式操作,我们自己写的扩展方法也要能继续链式下去:
}

// 设定默认值: 把默认值放在这个函数对象当中,允许用户修改,而且比较合适
$.fn.highlight.defaults = {
    color: '#d85030',
    backgroundColor: '#fff8de'
}

// 用户使用时,一次性设定默认值;
$.fn.highlight.defaults.color = '#fff';
$.fn.highlight.defaults.backgroundColor = '#000';

// 特定元素扩展
// 示例:给所有指向外链的超链接加上跳转提示
$.fn.external = function () {
    // return返回的each()返回结果,支持链式调用:
    return this.filter('a').each(function () {
        // 注意: each()内部的回调函数的this绑定为DOM本身!
        var a = $(this);
        var url = a.attr('href');
        if (url && (url.indexOf('http://')===0 || url.indexOf('https://')===0)) {
            a.attr('href', '#0')
             .removeAttr('target')
             .append(' <i class="uk-icon-external-link"></i>')
             .click(function () {
                if(confirm('你确定要前往' + url + '?')) {
                    window.open(url);
                }
            });
        }
    });
}

错误处理

捕获、处理错误

try {

} catch (e) {

} finally {
    
}

try{}包裹的代码,在执行中可能会发生错误,一旦出错,不再执行后续;跳转到catch块进行错误处理;最后无论有无错误,finally都执行。catch与finally可以只出现一个。

主动抛出错误

允许抛出任意对象,包括数字、字符串。但是,最好还是抛出一个Error对象。
throw new Error('');
错误会一直向上抛,直到遇到一个有try-catch语句的函数;

underscore第三方库,提供对Object的方法支持

使用“_”绑定全局变量;类似于jQuery的“$”

_.map(obj, fn)        // 返回Array
_.mapObject(obj, fn)  // 返回Obejct

// some 与 every   类似于filter的筛选功能
_.some(obj, fn)      // obj中的至少一个元素满足条件,有一个fn返回true,_.some()就返回true
_.every(obj, fn)     // 对每一个obj的所有元素都满足条件,即每一个fn都返回true,_.every()才返回true

分组 _.groupBy(obj, fn)

var scores = [20, 81, 75, 40, 91, 59, 77, 66, 72, 88, 99];
var groups = _.groupBy(scores, function (x) {
    if (x < 60) {
        return 'C';
    } else if (x < 80) {
        return 'B';
    } else {
        return 'A';
    }
});

// 结果:
// {
//   A: [81, 91, 88, 99],
//   B: [75, 77, 66, 72],
//   C: [20, 40, 59]
// }

打乱shuffle与随机取sample

_.shuffle(obj)
_.shuffle([1, 2, 3, 4, 5, 6]); // [3, 5, 4, 6, 2, 1]

_.sample(obj)
// 随机选1个:
_.sample([1, 2, 3, 4, 5, 6]); // 2
// 随机选3个:
_.sample([1, 2, 3, 4, 5, 6], 3); // [6, 1, 4]

Array工具类

zip与unzip与Object

// zip 一一对应,合并
var names = ['Adam', 'Lisa', 'Bart'];
var scores = [85, 92, 59];
_.zip(names, scores);
// [['Adam', 85], ['Lisa', 92], ['Bart', 59]]


// unzip 一一拆解
var namesAndScores = [['Adam', 85], ['Lisa', 92], ['Bart', 59]];
_.unzip(namesAndScores);
// [['Adam', 'Lisa', 'Bart'], [85, 92, 59]]


// 一一对应,合并为对象
var names = ['Adam', 'Lisa', 'Bart'];
var scores = [85, 92, 59];
_.object(names, scores);
// {Adam: 85, Lisa: 92, Bart: 59}

高阶函数

- _.bind(fn, newThis)   // 获取方法,传入原来方法的this才能保证方法被获取后成功调用
- _.partial(fn, constParam)        // 为一个函数创建偏函数,为fn固定第一个参数
- _.partial(fn, _, constParam)     // 为一个函数创建偏函数,_占位,为fn固定第二个参数

- _.memoize(fn)  // 缓存某个函数在某个参数下的计算结果
- _.once(fn)     // 返回一个只能被调用一次的函数
- _.delay(fn, time, param)  // 同setTimeOut

Object 工具类

 _.keys(obj)     // 返回一个object自身所有的key,但不包含从原型链继承下来的
 _.allKeys(obj)  // allKeys()除了object自身的key,还包含从原型链继承下来的:
 _.values(obj)   // 返回object自身但不包含原型链继承的所有值
 _.invert(obj)   // 把object的每个key-value来个交换,key变成value,value变成key
 _.extend(obj,obj,obj)  //把多个object的key-value合并到第一个object并返回
 _.extendOwn()    // 和extend()类似,但获取属性时忽略从原型链继承下来的属性
 _.clone()        // 复制obj,就不需要遍历每个key了
 _.isEqual()      // 内容层面的深度比较

像jQuery一样链式调用 .chain()

// 非链式调用
_.filter(_.map([1, 4, 9, 16, 25], Math.sqrt), x => x % 2 === 1);   // [1, 3, 5]


// 通过_.chain()实现链式调用
_.chain([1, 4, 9, 16, 25])
 .map(Math.sqrt)
 .filter(x => x % 2 === 1)
 .value();                 // [1, 3, 5]

上一篇下一篇

猜你喜欢

热点阅读