廖雪峰JavaScript教程笔记

数据类型和变量

  • “==”会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
  • “===”不会自动转换数据类型。
  • NaN与所有其他值都不相等,包括它自己:NaN===NaN; //false
  • 唯一能判断NaN的方法是通过isNaN()函数。

‘use strict’

在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致错误。

字符串

\n表示换行
ES6 新增多行字符串表示法,用`…`表示:

1
2
3
`这是一个
多行
字符串`

字符串常见操作

  • length 获取字符串某个指定位置的字符,使用类似Array的下标操作,索引号从0开始
  • toUpperCase 把一个字符串全部变为大写
  • toLowerCase 把一个字符串全部变为小写
  • indexOf 搜索制定字符串出现的位置
  • substring 返回指定索引区间的字串
1
2
var s = 'hello,world!'
s.substring(0,5); //从索引0开始到5(不包括5),返回"hello".

数组

数组常见操作

  • length
  • indexOf
1
2
var arr = [10,20,'30','xyz'];
arr.index(20);//元素20的索引为1
  • slice 类似字符串的substring(),截取数组的部分元素,返回新数组
1
2
var arr = ['a','b','c','d','e'];
arr.slice(0,3);//从索引0开始,到索引3结束,但不包括索引3:['a','b','c']
  • push和pop push()向数组的末尾添加若干元素,pop()则把数组的最后一个元素删除。
1
2
3
var arr = [1,2];
arr.push('a','b');
arr;//[1,2,'a','b']
  • unshift和shift unshift()向数组头部添加若干元素,shift()则把数组第一个元素删除。
  • sort 对数组进行排序
  • reverse 把数组元素掉个,反转
  • splice 可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素
1
2
3
var arr = ['a','b','c','d'];
arr.splice(2,2,'google','twitter');
arr;//['a','b','google','twitter']
  • concat 把当前的数组和另一个数组连接起来
1
2
3
var arr=['a','b','c'];
var added=arr.concat([1,2,3]);
added;//['a','b','c',1,2,3]
  • join 把当前数组的每一个元素都用指定的字符串连接起来,然后返回连接后的字符串.
1
2
var arr = ['a','b','c',1,2,3];
arr.join('-');//['a-b-c-1-2-3']

对象

  • 对象访问方法:
1
2
object.property;
object['property'];
  • 删除对象属性
1
delete.object.property;
  • 检查object是否有用某一属性,可以用in操作符
    不过要小心,如果in判断一个属性存在,这个属性不一定是object的,它可能是继承得到的
1
'toString' in xiaoming;//true
  • 要判断一个属性是否是对象自身拥有的,而不是继承的,可以用hasOwnPorperty();
1
xiaoming.hasOwnPorperty('toString');//false

条件判断

条件判断的顺序非常重要

循环

*for…in 可以把一个对象的所有属性一次循环出来:

1
2
3
4
5
6
7
8
var o={
name:"Jack",
age:20,
city:'Beijing'
};
for (var key in o){
alert (key);//'name','age','city'
}
  • 要过滤掉对象继承的属性,用hasOwnProperty()
1
2
3
4
5
for (var key in o){
if(o.hasOwnProperty(key)){
alert(key);
}
}

由于Array也是对象,它的每个元素的索引被视为对象的属性,因此for…in可以直接循环出Array的索引:

1
2
3
4
5
var a=['a','b','c'];
for (var i in a){
alert(i);//'0','1','2' 注意得到的是字符串,不是数字
alert(a[i]);//'a','b','c'
}
  • while
  • do…while

Map和Set(ES6)

Map

Map是一组键值对的结构,具有极快的查询速度

1
2
var m= new Map([['Michael',95],['Bob',75],['Tracy',85]]);
m.get('Michael');//95

Map具有以下方法:

  • set();添加
  • has();是否存在
  • get();获取
  • delete();删除

Set

Set和Map类似,也是一组key的集合,但不储存value。

1
2
3
var s1=new Set();
var s2=nwe Set([1,2,3,3,'3']);
s2;//1,2,3,'3' key不能重复,重复的会被自动过滤

Set的方法:

  • add(key);添加
  • delete(key);删除

iterable(ES6)

Array,Map和Set都属于iterable类型,这类集合可以通过新的for…of循环来遍历。

  • for…in遍历的是属性名称,当给一个Array手动添加一个额外的属性后,它会遍历出属性名称,而不是索引号。
  • for…of只循环集合本身的元素。
1
2
3
4
5
var a=['a','b','c'];
a.name='hello';
for (var i in a){
alert(i);//'a','b','c'
}
  • 更好的方法是使用iterable内置的forEach()
1
2
3
4
5
6
7
var a=['a','b','c'];
a.forEach(function(element,index,array){
//element:指向当前元素的值value
//index:指向当前元素的索引key
//array:指向Array对象本身,同理指向Map和Set本身
alert(element);
});

函数

定义函数

arguments

只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。类似Array,但不是一个Array。
arguments最常用于判断传入参数的个数。

1
2
3
4
5
6
7
function foo(a,b.c){
if(arguments.length====2){
//实际拿到的参数是a和b
c=b; //把b值赋值给c
b=null;//b变为默认值,变成了可选参数
}
}

rest参数(ES6)

1
2
3
4
5
6
7
8
9
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 //若只有一位,b=undefined
Array[3,4,5] //若无多余参数,则为空数组,非undefined

变量作用域

  • 内部和外部函数变量名重名,则内部函数的变量将“屏蔽”外部函数的变量。
  • 变量提升。函数会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部。
    应严格遵守“在函数内部首先申明所有变量”这一规则
  • 全局作用域,全局对象window
  • 名字空间:减少相同的全局变量和顶层函数命名冲突,可以把所有的变量和函数绑定到一个全局变量中。
1
2
3
4
5
6
var MYAPP={};
MYAPP.name='myapp';
MYapp.version=1.0;
MYAPP.foo=function(){
...
};
  • 局部作用域。在for循环等语句块中是无法定义具有局部作用域的变量的。就是for中定义的变量,父函数可以访问。ES6引入的let关键字,可以申明一个块级作用域的变量。
1
2
3
4
5
6
7
function foo(){
var sum = 0;
for (let i=0;i<100;i++){
sum +=i;
}
i +=1;//出错,i不可访问
}
  • 常量。constlet都具有块级作用域

方法

单独调用函数,比如getAge(),该函数的this指向全局对象window,要保证this指向正确,必须用object.*()的形式调用!单独调用getAge()//NaN

在严格模式下,函数的this指向undefined。
修复办法:
用that捕获this。

  • apply:根据是否是严格模式,this指向undefined或window,可通过apply控制this指向。apply()第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。
1
getAge.apply(xiaoming,[]);
  • call与apply唯一的区别就是call把参数按顺序传入
1
Math.max.call(null,3,5,4);//5

对普通函数调用,通常把this绑定为null。

  • 装饰器。利用apply动态改变函数行为
1
return oldParseInt.apply(null,arguments);

高阶函数

一个函数接收另外一个函数作为参数,这种函数称为高阶函数。

  • map()方法定义在JavaScript的Array中
1
2
var arr=[1,2,3,4,5,6,7,8,9];
arr.map(String);//
  • Array的reduce()把结果继续和序列的下一个元素做累计计算
1
2
3
4
5
var arr = [1,3,5,7,9];
arr.reduce(function(x,y){
return x+y;
});
//25
  • filter()用于把Array的某些元素过滤掉,然后返回剩下的元素
1
2
3
4
5
6
var arr=['a','b','',null,undefined,'c',''];
var r = arr.filter(function(s){
return s && s.trim();
});
r;//['a','b','c']
//在JS中除了false、0、''、null、undefined都算true
  • Array的sort()方法,默认把所有元素先转换为String再排序,结果‘10’排在了‘2’的前面。

闭包

当一个函数返回一个函数后,其内部的局部变量被新函数引用。

返回必报时牢记一点:
返回函数不要引用任何循环变量,或者后续会发生变化的变量。
要避免这种情况,方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定的函数参数的值不变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function count(){
var arr=[];
for (var i=1;i<=3;i++){
arr.push((function(n){
return function(){
return n*n;
}
})(i));
}
return arr;
}
var resules=count();
var f1=results[0];
var f2=results[1];
var f3=results[2];
f1();//1
f2();//4
f3();//9

箭头函数

1
2
箭头前面的表示参数,后面的表示表达式
```(x,y)=>x*x

标准对象

typeof获取对象的类型,它总是返回一个字符串

1
typeof null;//'object'

规则:

  • 用String()来转换任意类型到string,或者直接调用某个对象的toString()方法
  • typeof操作符可以判断出number boolean string function undefined
  • 判断Array要使用Array.isArray(arr);
  • 判断null使用myVar === null;
  • 判断某个全局变量是否存在使用typeof window.myVar ===’undefined’;
  • 函数内部判断某个变量是否存在用typeof myVar===’underfined’;

Date

new Date()的方法

  • getFullYear()
  • getMonth()
  • getDate()
  • getDay()//1,表示星期一
  • getHours()//19,表示晚上7点
  • getMinutes(0)
  • getSeconds(0)
  • getMilliseconds()
  • getTime()//以number形式表示的时间戳

解析一个符号ISO 8601格式的字符串使用parse()

1
2
var d= Date.parse('2015-06-24T19:49:22 875+08:00');//注意:不是new Date().parse().
d://1435146562875(时间戳)

  • toLocalString()显示本地时间
  • toUTCString()显示UTC时间,比北京时间晚8小时

RegExp

创建正则表达式:

  • 直接通过/正则表达式/写出来:
1
re1=/abc\-001/;
  • 通过new RegExp(‘正则表达式‘)创建一个RegExp对象:
1
rel2 = new RegExp('/abc\\-001');

RegExp对象的test()方法用于测试给定的字符串是否符合条件

切分字符串

1
2
'a,b;; c d'.split(/[\s\,\;]+/);
//['a','b','c','d']

切记与Array的splice区分开

分组

1
2
var re=/^(\d{3})-(\d{3,8})$/;
re.exec('010-12345');//['010-12345','010','12345']

exec()在匹配成功后,返回一个Array,匹配失败时返回null。

贪婪匹配

正则表达式默认是贪婪匹配,就是尽可能多的匹配,要采用非贪婪匹配,加上即可。

全局搜索

全局匹配用g标志,i标志表示忽略大小写,m表示执行多行匹配。

JSON

JSON规定字符串必须是UTF-8,字符串必须是双引号,Object的键也必须用双引号。

序列化

使用JSON.stringify(object,范围(null或属性和方法名称),’ ‘(按缩进输出));

反序列化

JSON.parse(JSON格式的字符串,(可选参数,可以传入函数));

面向对象编程

JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
所有对象都是实例,所谓继承关系,不过是把一个对象的原型链指向另一个对象而已。

1
xiaoming.__proto__=Bird;//低版本的IE不支持__proto__,仅供演示

创建对象

原型链

1
2
3
var arr = [1,2,3]
//其原型链是
arr ----> Array.prototype ----> object.prototype ---->null

构造函数

用new Student()创建的对象还从原型prototype上获得了一个constructor属性,它指向函数Student本身:
instanceof 用于判断一个变量是否是某个对象的实例
getPrototypeOf();用来获取对象的原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//用creatStudent()函数封装所有的new操作。
function Student(props){
this.name=props.name||'匿名';
this.grade=props.grade||'匿名';
}
Student.prototype.hello=function(){
alert('Hello, '+ this.name+'!');
}
function creatStudent(props){
return new Student(props||{});
}
var xiaoming = creatStudent({
name:'小明';
});
xiaoming.grade;//1

原型继承

用inherits()函数实现原型链继承

1
2
3
4
5
6
function inherits(Child,Parent){
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}

小结:

  • 定义新的构造函数,并在内部用call()调用希望继承的构造函数,并绑定this;
  • 借助中间函数F实现原型链继承,可通过封装的inherits函数完成;
  • 继续在新的构造函数的原型上定义新方法。

class继承(ES6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student{
constructor(name){
this.name=name;
}
hello(){//注意没有使用function
alert('Hello, '+ this.name+'!');
}
}
//通过<i>`extends`</i>来实现继承
class PridentStudent extends Student {
constructor(name,grade){
super(name);//使用super调用父类的构造方法!
this.grade = grade;
}
myGrade(){
alert('I am at grade'+this.grade);
}
}

浏览器

浏览器对象

  • navigator.appName:浏览器名称
  • navigator.appVersion:浏览器版本
  • navigator.language:浏览器设置的语言
  • navigator.platform:操作系统类型
  • navigator.userAgent:浏览器设定的User-Agent字符串
    1
    2
    //navigator的信息很容易被修改,所以用短路运算符
    var width = window.innerWidth||document.body.clientWidth;

screen

  • screen.width:屏幕宽度
  • screen.height:屏幕高度
  • screen.colorDepth:返回颜色位数

location

location对象表示页面的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()//重新加载

操作DOM

注意getElementsByClassName和getElementsByTagName,Elements为复数,后面加索引号。

更新DOM

innerText或textContent(IE<9不支持)可以自动对字符串进行html编码 使用style修改css时一定要加引号

1
js.style.fontWeight='bold';

插入DOM

parentElement.insertBefore(newElement,referenceElement);

删除DOM

removeChild();
children是子元素,非值,要想获得值,必须.children.innerText;

操作表单

要获得input、text、password、hidden、select的值,直接使用.value即可。
单选框和复选框用checked判断。
HTML5新增控件date、datetime、datetime-local、color

提交表单

1
2
3
4
5
6
7
8
<form id='test' onsubmit='return checkForm()'>
<button type='submit'>Submit</button>
</form>
<script>
function checkForm(){
return true;
}
</script>

使用toMD5修改口令时,口令框的显示会从几个变为32个(因为MD5有32个字符)。
要想不改变用户的输入,可以利用

1
2
3
4
5
6
7
8
9
10
11
12
<form>
<input type='password' id='input_password'>
<input type='hidden' id='md5_password' name='password'>
</form>
<script>
function checkForm(){
var input_pwd=document.getElementById('input_password');
var md5_pwd=document.getElementById('md5_password');
md5_pwd.value=toMD5(input_pwd.value);
return true;
}
</script>

没有name属性的input数据不会被提交

操作文件

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

File API

AJAX

1
2
3
4
5
6
var request;
if(window.XMLHttpRequest){
request = new XMLHttpRequest;
}else{
request = new ActiveXOBject('Microsoft.XMLHTTP);
}

通过检测window对象是否拥有XMLHttpRequest属性确定浏览器是否支持,而不要根据navigator.userAgent来检测。

安全限制

请求外域的URL方式:

JSONP,只能用GET请求,并且要求返回JavaScript。
CORS(前提是浏览器支持HTML5)

首先检测Access-Control-Allow-Origin是否包含本域,如果是,则跨域请求成功,如果不是则失败。

Canvas

在使用canvas前,用canvas.getContext来检查浏览器是否支持。
优化方案:

  • 通过创建一个不可见的Canvas来绘图,然后将最终结果复制到页面的可见Canvas中;
  • 尽量使用整数坐标而不是浮点数;
  • 可以创建多个重叠的Canvas绘制不同的层,而不是在一个Canvas中绘制非常复杂的图;
  • 背景图片如果不变可以直接用<img>标签并放到最底层。
坚持原创技术分享,您的支持将鼓励我继续创作!