JavaScript复习笔记

js复习笔记

js复习笔记,内容来源自廖雪峰JavaScript教程,w3school,菜鸟教程

一.快速入门

1.基本语法

1)js是一种直译式脚本语言

2)js结束语句后的;可以省略不写,原因是执行js时浏览器相关引擎会自动加上

3)js的数据类型有Number(js不分整数和浮点数),字符串,布尔值,数组,对象(键值对的一种无序集合)

var person = {
    name: 'Bob',
    age: 20,
    tags: ['js', 'web', 'mobile'],
    city: 'Beijing',
    hasCar: true,
    zipcode: null
};
//获取方式
person.name  

4)strict模式——在这个模式下会强制用
var来声明变量,防止变量被自动声明为全局变量

5)js字符串——字符串的相关操作和其他语言差不多。符号``,可以来对字符串进行拼接

var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);

#操作字符串相关方法:

  • toUpperCase()把一个字符串全部变为大写
  • toLowerCase()把一个字符串全部变为小写
  • indexOf()会搜索指定字符串出现的位置
  • substring()返回指定索引区间的子串

6)js数组——js的Array可以包含任意数据类型,真是强大,arr.length可以获取数组长度,当然js数组不建议直接修改arr的大小,确保索引不会报错。多维数组懂得都懂,利益相关。

  • indexOf()来搜索一个指定元素的下标
  • slice()来截取arr的部分元素,返回一个新的arr,属于不完全的深拷贝
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C']
arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']

 

  • push()和pop(),跟数据结构的栈差不多,先进后出,push添加数据,pop返回数据
  • unshift()在arr的头部添加元素,shift()在arr的第一个元素删掉
  • sort()方法对arr排序(快排),Array的sort()方法默认把所有元素先转换为String再排序
  • reserve()方法反转数组
  • splice()方法可以从指定的索引开始删除若干个元素,然后也可以在该位置添加若干元素,开发时间轴记事本的删除功能就使用了splice()方法,很简便
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']

 

  • concat()方法可以实现数组拼接,返回的是一个新的arr
  • join()方法是可以用指定的符号对数组进行拼接(若原数组元素不是字符串,则自动转为字符串,老牛逼了)
var arr=['a',1,2,3,4];
arr.join('+');//'a+1+2+3+4'

 

7)js对象——是一种无序的集合数据类型,它由若干键值对组成。

var xiaoming = {
    name: '小明',
    birth: 1990,
    school: 'No.1 Middle School',
    height: 1.70,
    weight: 65,
    score: null
};

访问属性用 . 来操作,如果属性名包含特殊字符,则需用’'包起来,访问则需要[‘xxx’]这样来访问

对象的添加和删除属性方法:

var xiaoming = {
    name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性
xiaoming.age; // 18
delete xiaoming.age; // 删除age属性
xiaoming.age; // undefined
delete xiaoming['name']; // 删除name属性
xiaoming.name; // undefined
delete xiaoming.school; // 删除一个不存在的school属性也不会报错

通过in 判断某个对象是否有某一属性
,如果in判断一个属性存在,这个属性不一定是xiaoming的,它可能是xiaoming继承得到的,而判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法

'name' in xiaoming;
xiaoming.hasOwnProperty('name');

8)条件,循环不记

9)Map和Set——js的对象的键必须是字符串,所以需要引入Map

Map:

var m = new Map(); // 空Map
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:

  • 通过add(key)方法可以添加元素到Set中
  • 通过delete(key)方法可以删除元素

10)iterable类型(arr,map,set)可以用for … of循环遍历

var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍历Array
    console.log(x);
}
for (var x of s) { // 遍历Set
    console.log(x);
}
for (var x of m) { // 遍历Map
    console.log(x[0] + '=' + x[1]);
}
  • 更好的方式是直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数.
var a = ['A', 'B', 'C'];
a.forEach(function (element) {
    console.log(element);
});
/*
A
B
C
*/

 

var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (element) {
    console.log(element);
});
/*
x
y
z
*/

 

二.函数

1)函数定义如下:

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

 

其他不记

2)方法——给对象绑定一个函数,则称为这个对象的方法,其中一个大坑就是this指针。这里附上一个this指针的详解——click

    1. 检查 ’ . ’ 左边是谁调用 这个函数. 例如 xiaoming.age(); age函数里面有this, 然后 '. ’ 旁边是xiaoming , 那么this就是指向xiaoming了.这种叫做 Implicit Binding.
    1. 如果点旁边没有,那就检查有没有用到 bind, apply, call 这三种, 有的话就是调用此方法的对象. 这种叫做 explicit binding.
    1. 如果上面两个都没有就检查代码里面有没有用到new 这个keyword, 有的话那就是指向new旁边的函数对象. 这种叫做new binding
    1. 上面三个都没有, 检查是不是有arrow function, 有arrow function的话就是, 那么指向是arrow function的lexical binding 的对象. 就是她的parent. 这种叫做 lexical binding
    1. 全部都没有如果不是strict mode那就是window对象了… strict就是 error (undefined).

3)高阶函数——一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数

function add(x,y,f){
    return f(x)+f(y);
}
var x=add(-5,6,Math.abs);
console.log(x);//11

 

4)闭包——以函数作为返回值实现函数外部访问私有变量的,从而延迟函数的使用(函数套函数,外层函数传回里程函数,里程函数运行才能实现函数操作)

闭包功能:

  • 延迟函数使用——副作用:外层变量为定值
  • 实现类中私有变量的功能

5)箭头函数——箭头函数相当于匿名函数,并且简化了函数定义。

//1
x => x * x
//2
x => {
    if (x > 0) {
        return x * x;
    }
    else {
        return - x * x;
    }
}
  • 箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。
  • 箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj
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(); // 25

由于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);
    }
};
obj.getAge(2015); // 25

 

6)generator(生成器)——generator由function* 定义(注意多出的‘*’号),并且,除了return语句,还可以用yield返回多次。

function* foo(x) {
    yield x + 1;
    yield x + 2;
    return x + 3;
}
var a=foo(1);
a.next();//2
a.next();//3
a.next();//4
a.next();//undefined

 

  • next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。
  • 当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了

三.标准对象

  • Date

Date对象用来表示日期和时间

var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月
now.getDate(); // 24, 表示24号
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小时制
now.getMinutes(); // 49, 分钟
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒数
now.getTime(); // 1435146562875, 以number形式表示的时间戳

 

不过上面这个方式获取是从本机操作系统获取的,不一定准确,可以使用下面这种方法

var d = new Date(2015, 5, 19, 20, 15, 30, 123);
d; // Fri Jun 19 2015 20:15:30 GMT+0800 (CST)

 

这里也有个坑,就是js的月份范围是0-11,所以表示9月就要写8月

这里还有另外一种解析iso 8601格式字符串获取时间戳的方法,不过可以通过转化成一个Date

var d1 = Date.parse('2015-06-24T19:49:22.875+08:00');
var d = new Date(d1);
d; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
d.getMonth(); // 5

 

使用Date.parse()时传入的字符串使用实际月份01-12,转换为Date对象后getMonth()获取的月份值为0-11。

ps:时间戳是一个自增的整数,它表示从1970年1月1日零时整的GMT时区开始的那一刻,到现在的毫秒数。假设浏览器所在电脑的时间是准确的,那么世界上无论哪个时区的电脑,它们此刻产生的时间戳数字都是一样的,所以,时间戳可以精确地表示一个时刻,并且与时区无关

  • 正则——懂得都懂,语言互通
  • json

1>序列化

var xiaoming = {
    name: '小明',
    age: 14,
    gender: true,
    height: 1.65,
    grade: null,
    'middle-school': '\"W3C\" Middle School',
    skills: ['JavaScript', 'Java', 'Python', 'Lisp']
};
var s = JSON.stringify(xiaoming, ['name', 'skills'], '  ');
console.log(s);
//结果
{
  "name": "小明",
  "skills": [
    "JavaScript",
    "Java",
    "Python",
    "Lisp"
  ]
}

 

2>反序列化

用JSON.parse()把json格式的字符串变成一个JavaScript对象

JSON.parse('[1,2,3,true]'); // [1, 2, 3, true]
JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14}
JSON.parse('true'); // true
JSON.parse('123.45'); // 123.45

 

四.面向对象

  • js的面向对象和其他语言不大一样。JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程
  • JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已
1.创建对象
2.原型继承
2.class继承

五.浏览器

1.浏览器对象
  • window对象不仅仅是全局作用域,还代表浏览器窗口,有innerWidth和innerHeight属性,分别是浏览器窗口内部的宽度和高度,即是网页的净宽高,对应的,还有一个outerWidth和outerHeight属性,可以获取浏览器窗口的整个宽高。
  • navigator对象表示浏览器的信息,常用属性有:
  1. navigator.appName:浏览器名称;
  2. navigator.appVersion:浏览器版本;
  3. navigator.language:浏览器设置的语言;
  4. navigator.platform:操作系统类型;
  5. navigator.userAgent:浏览器设定的User-Agent字符串

上面这个navigator在博客旁边的ip签名档使用过

  • screen对象表示屏幕的信息:
  1. screen.width:屏幕宽度,以像素为单位;
  2. screen.height:屏幕高度,以像素为单位;
  3. screen.colorDepth:返回颜色位数,如8、16、24。
  • location对象表示当前页面的url信息
  1. 获取url的各个值,可以使用protocol方法(http/https),host方法(域名),port端口,pathname方法(路径名),search方法,hash方法
  2. 要加载一个新的页面——location.assign(),重新加载当前页面——location.reload()
  • document对象表示当前页面,html在浏览器是以DOM形式表示为树形结构的,document对象就是整个DOM树的根节点。
  1. document的title属性是从HTML文档中的读取的,但是可以动态改变
document.title = '努力学习JavaScript!';
此时浏览器窗口标题变成上面这个

 

  1. 查找DOM树的某个节点,可以根据ID和Tag Name来查找,getElementById()和getElementsByTagName()可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点
<dl id="drink-menu" style="border:solid 1px #ccc;padding:6px;">
    <dt>摩卡</dt>
    <dd>热摩卡咖啡</dd>
    <dt>酸奶</dt>
    <dd>北京老酸奶</dd>
    <dt>果汁</dt>
    <dd>鲜榨苹果汁</dd>
</dl>

var menu = document.getElementById('drink-menu');
var drinks = document.getElementsByTagName('dd');
var i, s;

s = '提供的饮料有:';
for (i=0; i<drinks.length; i++) {
    s = s + drinks[i].innerHTML + ',';
}
console.log(s);//提供的饮料有:热摩卡咖啡,北京老酸奶,鲜榨苹果汁,
  • document对象有一个cookie属性,可以获取当前页面的cookie,使用方法:
Cookie是什么?

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

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

document.cookie;

 

安全机制:服务器在设置Cookie时可以使用httpOnly,设定了httpOnly的Cookie将不能被JavaScript读取

2.操作DOM

我们操作DOM树,不外乎就四种,更新,遍历,添加,删除

  • 操作DOM树前先需要拿到相关节点,最常用的方法是document.getElementById()和document.getElementsByTagName(),以及CSS选择器document.getElementsByClassName(),这个学过爬虫就略知一二了,当然,还有一种方法是使用querySelector()和querySelectorAll()
// 通过querySelector获取ID为q1的节点:
var q1 = document.querySelector('#q1');

// 通过querySelectorAll获取q1节点内的符合条件的所有节点:
var ps = q1.querySelectorAll('div.highlighted > p');

 

  • 更新DOM,直接修改节点的文本,有两种方法
  1. 修改innerHTML属性,这个不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本为abc:
p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
// 设置HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p>...</p>的内部结构已修改

 

使用innerHTML需要注意是否有写入HTML,如果是通过网络拿到的字符串,需要对字符编码来避免XSS攻击
2. 是修改innerText或textContent属性,这样可以自动对字符串进行HTML编码,保证无法设置任何HTML标签

// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被自动编码,无法设置一个<script>节点:
// <p id="p-id">&lt;script&gt;alert("Hi")&lt;/script&gt;</p>

 

这两个的区别是:innerText不返回隐藏元素的文本,而textContent返回所有文本

  • 插入DOM
  1. 如果DOM节点是空的,可以通过innerHTML直接插入新的节点,但如果不是空的,则不能这样做,因为innerHTML会替换原来的所有节点
  2. 如果DOM节点不为空,可以使用appendChild(把一个字节点添加到父节点的最后一个节点)
<!-- HTML结构 -->
<p id="js">JavaScript</p>
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>
//插入
var
    js = document.getElementById('js'),
    list = document.getElementById('list');
list.appendChild(js);
//结果
<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
    <p id="js">JavaScript</p>
</div>

 

因为我们插入的js节点已经存在于当前的文档树,因此这个节点首先会从原先的位置删除,再插入到新的位置。

  1. 动态添加一个新节点
var
    list = document.getElementById('list'),
    haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell);
//输出
<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
    <p id="haskell">Haskell</p>
</div>

 

  1. 添加新的CSS定义
var d = document.createElement('style');
d.setAttribute('type', 'text/css');
d.innerHTML = 'p { color: red }';
document.getElementsByTagName('head')[0].appendChild(d);

 

  1. 将子节点插入到指定位置

使用parentElement.insertBefore(newElement, referenceElement);,子节点会插入到referenceElement之前

var
    list = document.getElementById('list'),
    ref = document.getElementById('python'),
    haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell, ref);
//输出
<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="haskell">Haskell</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>

  • 删除DOM

删除一个节点,需要获得它和它的父节点,然后调用父节点的removeChild把该节点删掉

// 拿到待删除节点:
var self = document.getElementById('to-be-removed');
// 拿到父节点:
var parent = self.parentElement;
// 删除:
var removed = parent.removeChild(self);
removed === self; // true

 

注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置

遍历一个父节点的所有子节点进行删除操作时,主要children属性是一个只读属性,会随着子节点的变化实时更新

<div id="parent">
    <p>First</p>
    <p>Second</p>
</div>

var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 浏览器报错

 

这里报错的原因是,你删了第一个节点,节点是数量就发生改变了,所以索引[1]就不存在了

3.操作表单

HTML表单的输入主要有如下几个控件

  • 文本框,对应的,用于输入文本
  • 口令框,对应的,用于输入口令;
  • 单选框,对应的,用于选择一项;
  • 复选框,对应的,用于选择多项;
  • 下拉框,对应的,用于选择一项;
  • 隐藏文本,对应的,用户不可见,但表单提交时会把隐藏文本发送到服务器;

1)获取值

我们可以通过一个节点的引用来调用value来获取输入值

// <input type="text" id="email">
var input = document.getElementById('email');
input.value; // '用户输入的值'

 

这里对于单选框和复选框,value属性返回的永远是HTML预设的值,但我们只需知道用户是否勾上了选项,所以要用checked判断

// <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
// <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
var mon = document.getElementById('monday');
var tue = document.getElementById('tuesday');
mon.value; // '1'
tue.value; // '2'
mon.checked; // true或者false
tue.checked; // true或者false

 

2)设置值

和获取值类型,直接设置value就行

// <input type="text" id="email">
var input = document.getElementById('email');
input.value = 'test@example.com'; // 文本框的内容已更新

 

3)HTML控件

常用的控件有date,datetime,datetime-local,color,它们都使用input标签

<input type="date" value="2015-07-01">
//这里是一个日期的选择控件
<input type="color" value="#ff0000">
//这里编译后的是,一个颜色的选择控件

 

date类型的input的value将保证是一个有效的YYYY-MM-DD格式的日期

4)提交表单

  • 方式一,通过元素的submit()方法提交一个表单,例如,响应一个的click事件,在js代码中提交表单
<!-- HTML -->
<form id="test-form">
    <input type="text" name="test">
    <button type="button" onclick="doSubmitForm()">Submit</button>
</form>

<script>
function doSubmitForm() {
    var form = document.getElementById('test-form');
    // 可以在此修改form的input...
    // 提交form:
    form.submit();
}
</script>

 

这种方式的缺点是扰乱了浏览器对form的正常提交。浏览器默认点击时提交表单,或者用户在最后一个输入框按回车键

  • 方式二,响应本身的onsubmit事件
<!-- 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;
}
</script>

 

如果这里是return false,浏览器不会提交这个表单,这种情况可以来判断用户输入有误,提示用户错误信息终止提交form

在检查和修改时,要充分利用来传递数据,比如我们可以用MD5加密来实现安全传输问题

<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
    <input type="text" id="username" name="username">
    <input type="password" id="password" name="password">
    <button type="submit">Submit</button>
</form>

<script>
function checkForm() {
    var pwd = document.getElementById('password');
    // 把用户输入的明文变为MD5:
    pwd.value = toMD5(pwd.value);
    // 继续下一步:
    return true;
}
</script>

 

这个代码看似没啥问题,但是用户输入口令提交后,口令框会变成32个 * ,因为MD5有32个字符,如果不改变用户的输入,可以通过实现

<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
    <input type="text" id="username" name="username">
    <input type="password" id="input-password">
    <input type="hidden" id="md5-password" name="password">
    <button type="submit">Submit</button>
</form>

<script>
function checkForm() {
    var input_pwd = document.getElementById('input-password');
    var md5_pwd = document.getElementById('md5-password');
    // 把用户输入的明文变为MD5:
    md5_pwd.value = toMD5(input_pwd.value);
    // 继续下一步:
    return true;
}
</script>

注意到id为md5-password的标记了name=“password”,而用户输入的id为input-password的没有name属性。没有name属性的的数据不会被提交

4.操作文件

在HTML表单中,可以上传文件的唯一控件就是。

注意:当一个表单包含时,表单的enctype必须指定为multipart/form-data,method必须指定为post,浏览器才能正确编码并以multipart/form-data格式发送表单的数据。

出于安全考虑,浏览器只允许用户点击来选择本地文件,用JavaScript对的value赋值是没有任何效果的。当用户选择了上传某个文件后,JavaScript也无法获得该文件的真实路径

var f = document.getElementById('test-file-upload');
var filename = f.value; // 'C:\fakepath\test.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') || filename.endsWith('.gif'))) {
    alert('Can only upload image file.');
    return false;
}

 

对文件扩展名进行检查,防止用户上传无效格式文件

  • File API

HTML5的File API提供了File和FileReader两个主要对象,可以获得文件信息并读取文件

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);
});

 

上面的代码演示了如何通过HTML5的File API读取文件内容。以DataURL的形式读取到的文件是一个字符串,类似于data:image/jpeg;base64,/9j/4AAQSk…(base64编码)…,常用于设置图像。如果需要服务器端处理,把字符串base64,后面的字符发送给服务器并用Base64解码就可以得到原始文件的二进制内容。

  • 回调

浏览器的js执行引擎在执行js代码时,总是以单线程模式执行,也就是说,任何时候,JavaScript代码都不可能同时有多于1个线程在执行。

在JavaScript中,执行多任务实际上都是异步调用

reader.readAsDataURL(file);

发起一个异步操作来读取文件内容。因为是异步操作,所以我们在JavaScript代码中就不知道什么时候操作结束,因此需要先设置一个回调函数

reader.onload = function(e) {
    // 当文件读取完成后,自动调用此函数:
};

 

当文件读取完成后,JavaScript引擎将自动调用我们设置的回调函数。执行回调函数时,文件已经读取完毕,所以我们可以在回调函数内部安全地获得文件内容。

5.AJAX
6.Promise
  • Promise().then().then…catch() 多任务串行执行.

情景化记忆:在一个任务链中,比如我要向上级部门一层层的往上提交申请,if(某种条件)承诺帮你resolve解决问题,else承诺reject你的请求. 他给出的resolve问题的办法只是个空头Promise,then到总经理那实现具体承诺,如果总经理还是给一个空头承诺(返回Promise实例),还得then到董事长那里… 任一一步做出的是reject的承诺,还有什么好说的,被拒绝了,后面的就不会再往上走了呀. 准备catch 拒绝通知吧blablabla

  • Promise.all([p1,p2,…])

多任务并行执行
都要成功才进入then,返回结果数组.

  • Promise.race([p1,p2,…])

多任务赛跑.
then()和catch(),谁先调用算谁的,其它任务中断.

更多内容

7.Canvas

Canvas是HTML5新增的组件,它就像一块幕布,可以用JavaScript在上面绘制各种图表、动画等。

之前没有Canvas前绘图用Flash插件实现,但是现在Flash逐步被淘汰

一个Canvas定义了一个指定尺寸的矩形框,在这个范围内我们可以随意绘制

<canvas id="test-canvas" width="300" height="200"></canvas>

 

getContext(‘2d’)方法让我们拿到一个CanvasRenderingContext2D对象,所有的绘图操作都需要通过这个对象完成。

var ctx=canvas.getContext('2d');

 

绘制3D,可以这么获取

var gl = canvas.getContext("webgl");

 

  • 绘制形状

Canvas的坐标以左上角为原点,水平向右为X轴,垂直向下为Y轴,以像素为单位,所以每个点都是非负整数

var
    canvas = document.getElementById('test-shape-canvas'),
    ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, 200, 200); // 擦除(0,0)位置大小为200x200的矩形,擦除的意思是把该区域变为透明
ctx.fillStyle = '#dddddd'; // 设置颜色
ctx.fillRect(10, 10, 130, 130); // 把(10,10)位置大小为130x130的矩形涂色
// 利用Path绘制复杂路径:
var path=new Path2D();
path.arc(75, 75, 50, 0, Math.PI*2, true);
path.moveTo(110,75);
path.arc(75, 75, 35, 0, Math.PI, false);
path.moveTo(65, 65);
path.arc(60, 65, 5, 0, Math.PI*2, true);
path.moveTo(95, 65);
path.arc(90, 65, 5, 0, Math.PI*2, true);
ctx.strokeStyle = '#0000ff';
ctx.stroke(path);

 

  • 绘制文本

绘制文本就是在指定的位置输出文本,可以设置文本的字体、样式、阴影等,与CSS完全一致

var
    canvas = document.getElementById('test-text-canvas'),
    ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = '#666666';
ctx.font = '24px Arial';
ctx.fillStyle = '#333333';
ctx.fillText('带阴影的文字', 20, 40);

 

Canvas功能很强大,还可以实现动画,缩放,滤镜和像素转化等操作

  • 通过创建一个不可见的Canvas来绘图,然后将最终绘制结果复制到页面的可见Canvas中;
  • 尽量使用整数坐标而不是浮点数;
  • 可以创建多个重叠的Canvas绘制不同的层,而不是在一个Canvas中绘制非常复杂的图;
  • 背景图片如果不变可以直接用标签并放到最底层。
点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像