Number:
javascript不区分整数和浮点数,统一用number表示。可以直接做四则运算。
{
124,
0.345,
1.23,
-99,
NaN,
Infinity //表示无限大,当数值超过了javascript的Number所能表示的最大值是,就显示Infinity
}
字符串是以单引号‘’或双引号“”括起来的任意文本。‘’或“”本身只是一种表示方式,不是字符串的一部分。
布尔值只有true或false
运算符==,它会自动转换数据类型再比较,很多时候会得到非常诡异的结果
运算符===,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较
因此,不要使用==比较,始终坚持使用===比较
另一个例外是NaN,这个特殊的Number与其他值都不相等,包括它自己:
NaN == NaN //false
唯一等判断NaN的方法是通过isNaN()函数:
isNaN(NaN); //true
最后要注意浮点数的相等比较:
1/3 == (1-2/3);//false
这不是javascript的设计缺陷,浮点数在运算工程中会产生误差,因为计算机无法精确表示
无限循环小数,要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否
小于某个阈值:
Math.abs(1/3 == (1-2/3)) < 0.0000001; //true
null 和 undefined
null 表示一个‘空’的值,它和0以及空字符串‘’不同,0是一个数值,''表示长度为0的字符串,而null表示‘空’
null 表示一个空的值,而undefined表示值未定义。大多数情况下,我们都应该用null。undefined 仅仅在判断函数参数是否传递的情况下有用。
数组
数组是一组按顺序排列的集合,集合的每个值称为元素。javascript的数组可以包括任意数据类型。
[1,2,4,56]
另一种创建数组的方法是通过Array()函数实现:
new Array(1,2,34)
然而,处于代码的可读性考虑,强烈建议直接使用[]
数组的元素可以通过索引来访问。索引的起始值为0
对象
javascript 的对象是一组由键-值组成的无序集合:
var person = {
name: 'xiaoxia',
age: '25',
city:'hz'
}
javascript对象的键都是字符串类型,值可以使任意数据类型。每个键又称为对象的属性
要获取一个对象的属性,我们用对象变量.属性名的方式
person.name; // xiaoxia
变量
在javascript中,使用等号=对变量进行赋值,可以把任意类型赋值给变量,同一个变量可以反复赋值,
而且可以是不同类型的变量,但是要注意只能用var申明一次
javascript严格模式:
'use strict';
避免不用var申明的变量会被视为全局变量的缺陷。
模板字符串
要把多个字符串连起来,可以用+号链接:
var name = '小明'
var age = 20
var message = name + age;
es6语法:
var message = '${name},${age}'
操作字符串的方法:
var s = 'hello xiaoxia';
s.length;//13
要获取字符串某个指定位置的字符,使用类似Array的下标操作,
s[0];//h
s[33];//undefined超出范围的索引不会报错,但一律返回undefined
特别注意的是:字符串是不可变的,如果对字符串的某个索引值赋值,不会有任何错误,但是,
也没有任何效果
toUpperCase//把一个字符串全部变为大写
var s = 'hexiaoxia';
s.toUpperCase();
toLowerCase//把一个字符串全部变为小写
s.toLowerCase();
indexOf()//会搜索指定字符串出现的位置
s.indexOf('xiao');//2
s.indexOf('Xiao');//返回-1,没有找到指定的子串
s.substring(7)//索引7开始到结束
s.substring(0,5)//从索引0开始到5(不包括5)
数组的方法
splice()改变了数组结构。
var arr = [1,24,4,5,7,8];
arr.length;
indexOf()//Array也可以通过indexOf()来搜索一个指定的元素的位置
arr.indexOf(24)//1
slice()//就是对应String的substring()版本,它截取Array的部分元素,然后
返回一个新的Array:
arr.slice(0,3);//1,24,4
如果不给slice()传递任何参数,它就会从头到尾截取所有元素,利用这一点,
我们可以很容易地复制一个Array:
var aCopy = arr.slice();
push和pop
push()向Array的末尾添加若干元素,pop()则把Array的最后一个元素删除掉
unshift和shift
如果要往Array的头部添加若干元素,使用unshift()方法,shift()方法则把Array的第一个元素删掉
sort()可以对当前Array进行排序,它会直接修改当前Array的元素位置,直接调用时,按照默认顺序排序
reverse
reverse()把整个Array的元素给掉个个,也就是反转
splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
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
concat()方法把当前的Array和另一个Array连接起来,并返回一个新的Array
join()方法是一个非常实用的方法,它把当前Array的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
Javascript 的对象是一种无序的集合数据类型。
访问属性是通过.操作符完成的,但这要求属性名必须是有效的变量名。如果属性名
包含特殊字符,就必须用‘’括起来:
var xiaoxia = {
name: 'xiaoxia',
'family': 'kkk'
};
xiaoxia['family'] // 'kkk'
javascript 对象的所有属性都是字符串,不过属性对应的值可以是任意数据类型
如果访问一个不存在的属性会返回undefined
由于javascript对象是动态类型,可以自由地给一个对象添加或删除属性
var xiaobin = {
name: 'xiaobin'
}
xiaobin.age = 21
delete xiaobin.age
xiaobin.age // undefined
要检测对象是否拥有某一属性,可以用in 操作符:
'name' in xiaobin //true
'age' in xiaobin //false
不过要小心,如果in判断一个属性存在,这个属性不一定是xiaobin的,它可能是xiaobin
继承得到的。
'toString' in xiaobin //true
要判断一个属性是否是xiaobin自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法:
xiaobin.hasOwnProperty('toString');//false
Javascript 把null,undefined,0,NaN和空字符串''视为false,其他值一概视为true
for...in
for循环的一个变体是for...in 循环,它可以把一个对象的所有属性依次循环出来
var o = {
name: 'xiaobin',
age: 21,
city:'beijing'
}
for(var key in o) {
console.log(key); // name,age,city
}
Map
Map是一组键值对的结构
初始化Map需要一个二维数组,或者直接初始化一个空Map
var m = new Map();
m.set('Xiaobin',21);
m.get('Xiaobin');//21
m.has('Xiaobin');
m.delete('Xiaobin');
m.get('Xiaobin');//undefined
函数:
函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。
因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
如果没有return 语句,函数执行完毕后会返回结果,
只是结果为undefined.function abs(x) { if (x >= 0) { return x; } else { return -x; }}
由于Javascript的函数也是一个对象,上述定义的abs()函数名abs可以视为指向该函数的变量
var abs = function(x) { for (var i=0; i= 0) {
return x;
} else {
return -x;
}
}
abs(4);//4
abs(-1);//-1
在这种方式下,function(x) {...} 是一个匿名函数,它没有函数名,但是,这个匿名函数赋值给了
变量abs,所以,通过abs就可以调用该函数。
为了避免函数的参数收到undefined,计算结果为NaN,可以对参数进行检查:
function abs(x) {
if(typeof x !== 'number') {
throw 'Not a number';
}
}
arguments
Javascript还有一个免费赠送的关键字arguments,它只在函数内部起作用,并且永远指向当前的调用者
传入的所有参数。arguments类似Array 但它不是Array:
for (var i=0; i < arguments.length; i++) { console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30 }
由于Javascript引擎在行末自动添加分号的机制,return语句需要注意了。
所以正确的多行写法是:
return {
}
全局作用域
不在任何函数内定义的变量就具有全局作用域。实际上,javascript默认一个全局对象window,
全局作用域的变量实际上被绑定到window的一个属性。
var course = 'Learn Javascript';
window.course ;//'Learn Javascript'
由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:
'use strict';
function foo() {
alert('foo');
}
foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用
名字空间:全局变量会绑定到window上,不同的javascript文件如果使用了相同的全局变量,
或者定义了相同名字的顶层函数,都会造成命名冲突,减少冲突的一个方法是把自己的所有
变量和函数全部绑定到一个全局变量中。
例如:
var MYAPP = {}
MYAPP.name = 'xiaobin';
MYAPP.version = 1.0;
MYAPP.foo = function() {
return 'foo';
}
方法:
在一个对象中绑定函数,称为这个对象的方法。
function getAge() {
var y = new Date().getFullYear();
return y-this.birth;
}
var xiaobin = {
name: 'xiaobin',
birth: 1996,
age: function() {
var y = new Date().getFullYear();
return y-this.birth;
},
age1: getAge
};
xiaobin.age();
一个方法内部,this是一个特殊变量,它始终指向当前对象,也就是xiaobin
这个变量。
如果以对象的方法形式调用,比如xiaobin.age(),该函数的this指向被调用的对象。
如果单独调用函数,比如getAge(),此时,该函数的this指向全局变量,也就是window.
apply
在一个独立的函数调用中,还是可以控制this的指向的,
要指定函数的this指向哪个对象,可以用函数本身的apply方法,第一个参数就是需要绑定的
this变量,第二个参数是Array,表示函数本身的参数。
用apply修复getAge()调用:
function getAge() {
var y = new Date().getFullYear();
return y-this.birth;
}
var xiaobin = {
name: 'xiaobin',
birth: 1996,
age:getAge
};
xiaobin.age();//21
getAge.apply(xiaobin,[]);//21,this指向xiaoming,参数为空
call() 与 apply()类似,唯一区别是:
apply()把参数打包成Array再传入;
call()把参数按顺序传入
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
高阶函数
既然变量可以指向函数,函数的参数能接收变量,那个一个函数就可以接收另一个函数作为参数,
这种函数就称之为高阶函数
map()方法定义在Javascript的Array中,我们调用Array的map()方法,传入我们自己的函数,就得到了一个
新的Array作为结果。
'use strict';
function pow(x) {
return x*x
}
var arr = [1,2,3,4,5,6,7,8];
var results = arr.map(pow);//1,4,9,16,25,36,49,64
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
filter()用于把Array的某些元素过滤掉,然后返回剩下的元素。和map()不同的是,
filter()把传入的函数依次作用于每个元素,然后根据返回值true还是false决定保留还是
丢弃该元素。
var arr = [1,3,4,5,6,7];
var r = arr.filter(function(x) {
return x % 2 !== 0;
});
//只保留基数
filter()接收的回调函数,可以有多个参数,通常仅使用第一个参数,表示Array的某个元素,回调函数还可以
接收另外两个参数,表示元素的位置和数组本身:
var r = arr.filter(function(element,index,self) {
console.log(element);//1,3,4,5,6,7
console.log(index);//0,1,2,3,4,5
console.log(self);//arr
return true;
});
利用filter可以巧妙的去除Array的重复元素:
var r = arr.filter(function(element,index,self) {
return self.indexOf(element) === index;
})
闭包:函数作为返回值。
typeof 操作获取对象的类型,它总是返回一个字符串
包装对象:
var n = new Number(124);//124
var b = new Boolean(true);//true
var s = new String('str');//'str'
虽然包装对象看上去和原来的值一模一样,显示出来也一模一样,但他们的类型已经变为object了。
如果我们在使用Number、Boolean和String时,没有写new会发生什么情况?
此时,Number()、Boolean和String()被当做普通函数,把任何类型的数据转换为number、boolean和string类型(注意不是其包装类型):
总结一下,有这么几条规则需要遵守:
不要使用new Number()、new Boolean()、new String()创建包装对象;
用parseInt()或parseFloat()来转换任意类型到number;
用String()来转换任意类型到string,或者直接调用某个对象的toString()方法;
通常不必把任意类型转换为boolean再判断,因为可以直接写if (myVar) {...};
typeof操作符可以判断出number、boolean、string、function和undefined;
判断Array要使用Array.isArray(arr);
判断null请使用myVar === null;
判断某个全局变量是否存在用typeof window.myVar === 'undefined';
函数内部判断某个变量是否存在用typeof myVar === 'undefined'。
任何对象都有toString()方法吗?null和undefined就没有!确实如此,这两个特殊值要除外,虽然null还伪装成了object类型。
Date对象用来表示日期和时间
var now = new Date();
第二种创建一个指定日期和时间的方法是解析一个符合ISO 8601格式的字符串:
var d = Date.parse('2015-06-24T19:49:22.875+08:00');
d; // 1435146562875
var d = new Date(1435146562875);
d.toLocaleString(); // '2015/6/24 下午7:49:22',本地时间(北京时区+8:00),显示的字符串与操作系统设定的格式有关
d.toUTCString(); // 'Wed, 24 Jun 2015 11:49:22 GMT',UTC时间,与本地时间相差8小时
JSON的字符串规定必须用双引号“”,Object的键必须用双引号“”
把任何Javascript对象变成JSON,就是把这个对象序列化成一个JSON格式的字符串,这样才能通过网络传递给其他计算机。
如果我们收到一个JSON格式的字符串,只需要把它反序列化成一个Javascript对象,就可以在Javascript中直接使用
这个对象了。
JSON.stringify();
JSON.stringify(xiaoming, ['name', 'skills'], ' ');
{
"name": "小明",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}
拿到一个JSON格式的字符串,JSON.parse()把它变成一个Javascript对象
JSON.parse('[1,2,3,true]'); // [1, 2, 3, true]
Javascript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程
var Student = {
name:'robot',
height:1.2,
run:function() {
console.log(this.name + 'is running...');
}
}
var xiaoming = {
name: '小明'
}
xiaoming._proto_=Student;
//xiaoming的原型指向了对象Student,看上去xiaoming仿佛是从Student继承下来的。
xiaoming.name;//'小明'
xiaoming.run();//小明is runing...
Javascript 的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,
所谓继承关系不过是把一个对象的原型指向另一个对象而已。
在编写Javascript代码时,不要直接用obj._proto_去改变一个对象的原型,并且低版本的IE
也无法使用_proto_。Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象,
但是新对象什么属性都没有,因此,我们可以编写一个函数来创建xiaobin:
var Student = {
name:'Robot',
height: 1.2,
run: function() {
console.log(this.name + 'is running...');
}
}
function createStudent(name) {
var s = Object.create(Student);
s.name = name;
return s;
}
var xiaobin = createStudent('小宾');
xiaobin.run();
xiaobin._proto_=== Student;//true;
var arr = [1,2,3];
arr --->Array.prototype ---> Object.prototype --->null
Array.prototype 定义了indexOf()、shift()等方法
function foo() {
return 0;
}
foo ---> Function.prototype --->Object.prototype --->null
构造函数
Javascript可以用一种构造函数的方法来创建对象,它的用法是,先定义一个
构造函数:
function Student(name) {
this.name = name;
this.hello = function() {
alert('Hello' + this.name + '!');
}
}
var xiaoming = new Student('小明');
xiaoming.name;//'小明'
xiaoming.hello();
如果不写new,Student就是一个普通函数,它返回undefined.
如果写new,它就变成一个构造函数,它绑定的this指向新创建的对象,并默认返回this,
也就是说,不需要在最后写return this
用new Student()创建的对象还从原型上获得了constructor属性,它指向函数Student本身:
xiaobin.constructor === Student.prototype.constructor;//true
Student.prototype.constructor === Student;//true
Object.getPrototypeOf(xiaobin) === Student.prototype;//true
xiaobin.instanceof Student;//true
通过new Student()创建了很多对象,这些对象的hello函数实际上只需要共享同一次函数就可以了,
这样可以节省很多内存。
修改代码如下:
function Student(name) {
this.name = name;
}
Student.prototype.hello = function() {
alert('hello' + this.name);
}
按照约定,构造函数首字母应当大写,而普通函数首字母应当小写
Javascript可以获取浏览器提供的很多对象,并进行操作。
window对象不但充当全局作用域,而且表示浏览器窗口。
window对象有innerWidth 和 innerHeight属性,可以获取浏览器窗口的内部宽度和高度,用于
显示网页的净宽高。
IE<=8 不支持
navigator 对象表示浏览器的信息,最常用的属性:
navigator.appName:浏览器名称;
navigator.appVersion:浏览器版本;
navigator.language:浏览器设置的语言;
navigaotor.userAgent:浏览器设定的User-Agent字符串
screen对象便是屏幕的信息,常用的属性是:
screen.width;屏幕宽度,以像素为单位
screen.height;
screen.colorDepth
http://www.example.com:8080/path/index.html?a=1&b=2#TOP
location对象表示当前页面的URL信息。
location.href;//获取一个完整的URL
locaton.protocal;//'http'
location.host;//'www.example.com'
location.pathname;//'/path/index.html'
location.search;//'?a=1&b=2'
location.hash;//'TOP'
加载一个新页面
location.reload()
document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,
document对象就是整个DOM树的根节点。
操作DOM
要改变HTML的结构,就需要通过Javascript来操作DOM
更新,遍历,添加,删除
最常用的方法是document.getElementById()和document.getElementsByTagName(),以及CSS选择器document.getElementsByClassName()
querySelector() 和 querySelectotAll()
// 通过querySelector获取ID为q1的节点:
var q1 = document.querySelector('#q1');
// 通过querySelectorAll获取q1节点内的符合条件的所有节点:
var ps = q1.querySelectorAll('div.highlighted > p');
低版本的IE<8不支持querySelector和querySelectorAll。IE8仅有限支持。
更新DOM:innerHTML,innerText
p.innerHTML = 'love'
插入DOM:appendChild(),insertBefore()
var js = document.getElementById('js'),
list = document.getElementById('list');
list.appendChild(js);
var list = document.getElementById('list'),
ref = document.getElementById('python'),
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell, ref);
删除DOM
要删除一个节点,首先要获得该节点本身以及它的父节点,然后,
调用父节点的removeChild把自己删掉
var self = document.getElementById('to-be-removed');
var parent = self.parentElement;
var removed = parent.removeChild(self);
removed === self //true
用Javascript操作表单和操作DOM是类似的,表单本身也是DOM树
操作文件:在HTML表单中,可以上传文件的唯一控件就是表单的enctype必须指定为multipart/form-data,method必须指定为post,浏览器才能正确编码并以multipart/form-data格式发送表单的数据
node.js在Node环境中,一个.js文件就称之为一个模块。
使用模块大大提高了代码的可维护性、避免函数名和变量名冲突。
module.exports = great;
把函数greet作为模块的输出暴露出去,这样其他模块就可以使用greet函数了;
//引用这个模块var greet = require('./hello');
Javascript有且仅有一个全局对象,在浏览器中,叫window对象,
在Node.js环境中,也有唯一的全局变量,叫global,这个对象的属性和方法也和浏览器环境的window不同。
判断Javascript执行环境
if(typeof(window) === 'undefined') {console.log('node.js');}
else{console.log('browser');}
fsNode.js内置的fs模块就是文件系统模块,负责读写文件,同时提供了异步和同步的方法。
js正则表达式
通常用于两个字符串方法:search() 和 replace();
search()用于检索字符串中指定的子字符串,或检索与正则表达式匹配的子字符串,
并返回子串的起始位置。
var str = 'visit you';
var n = str.search(/you/i);
//6
search方法可以使用字符串作为参数:
var str = 'visit you';
var n = str.search('you');
//6
replace()使用正则表达式且不区分大小写将字符串aaa替换为bbb
var text = str.replace(/aaa/,'bbb');
test()方法用于检测一个字符串是否匹配某个模式,如果字符串中含有
匹配的文本,则返回true,否则返回false.
exec()方法用于检索字符串中的正则表达式的匹配
/e/.exec("The best things in life are free!");
//e
^匹配一个输入或一行的开头,/^a/匹配'an A',而不匹配'An'
$匹配一个输入或一行的结尾,/a$/匹配'an a',而不匹配'an A'
*匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa
+匹配前面元字符1次或多次,/ba+/将匹配ba,baa,baaa
?匹配前面元字符0次或1次,/ba?/将匹配b,ba
(x|y)匹配x或y
{n}匹配n次以上
{n,m}匹配n-m次
[xyz]字符集,匹配这个集合中的任一一个字符
[^xyz]不匹配这个集合中的任何一个字符
[\b]匹配一个退格符
\b匹配一个单词的边界
\B匹配一个单词的非边界
\cX,X是一个控制符,匹配Ctrl-X
\d匹配一个字数字符,/d/ == /[0-9]/
\D匹配一个非字数字符,/d/ == /[^0-9]/
\n匹配一个换行符
\r匹配一个回车符
\s匹配一个空白字符,包括\n,\r,\f,\t,\v
\S匹配一个非空白字符,等于[^\n\r\f\t\v]
\t匹配一个制表符
\v匹配一个垂直制表符
\w匹配一个可以组成单词的字符,等于[a-zA-Z0-9]
\W匹配一个不可以组成单词的字符,等于[^a-zA-Z0-9]