JavaScript是世界上最流行的脚本语言,因为你在电脑、手机、平板上浏览的所有的网页,以及无数基于HTML5的手机App,交互逻辑都是由JavaScript驱动的。
ECMAScript是一种语言标准,而JavaScript是网景公司对ECMAScript标准的一种实现。
引入javascript:
<!-- 第一种引入方法 --> <html> <head> <!-- 第一种引入方法:直接编写 --> <script> alert('Hello, world'); </script> <!-- 第二种引入方法:编写到一个单独的.js文件 --> <script src="..."> </script> </head> <body> ... </body> </html>
有些时候你会看到
<script type="text/javascript"> ... </script>
但这是没有必要的,因为默认的 type 就是JavaScript,所以不必显式地把 type 指定为
JavaScript。
JavaScript的语法和Java语言类似,每个语句以 ; 结束,语句块用 {…}
//下面的一行代码就是一个完整的赋值语句 var x = 1; //下面的一行代码是一个字符串,但仍然可以视为一个完整的语句 'Hello, world'; //JavaScript严格区分大小写,如果弄错了大小写,程序将报错或者运行不正常。 //语句块是一组语句的集合 if (2 > 1) { x = 1; y = 2; z = 3; }
Number
//JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型: 123; // 整数 123 0.456; // 浮点数 1.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5 -99; // 负数 NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示 Infinity; // Infinity表示无限大
字符串
字符串是以单引号 ‘ 或双引号 “ 括起来的任意文本,比如 ‘abc’ , “xyz” 等等
//如果 ' 本身也是一个字符,那就可以用 "" 括起来 //如果字符串内部既包含 ' 又包含 " 可以用转义字符 \ 来标识 'I\'m \"OK\"!'; //ASCII字符可以以 \x## 形式的十六进制表示,例如 '\x41'; // 完全等同于 'A' //以用 \u#### 表示一个Unicode字符 '\u4e2d\u6587'; // 完全等同于 '中文' 1
布尔值
运算符
&& //与运算 || //或运算 !//非运算 == //它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果 === //它不会自动转换数据类型,如果数据类型不一致,返回false,建议使用 NaN === NaN; // false 这个特殊的Number与所有其他值都不相等,包括它自己 isNaN(NaN); // true 唯一能判断 NaN 的方法是通过 isNaN() 函数 1 / 3 === (1 - 2 / 3); // false 浮点数在运算过程中会产生误差
null和undefifined
null 表示一个“空”的值,它和 0 以及空字符串 ‘’ 不同, 0 是一个数值, ‘’ 表示长度为0
的字符串,而 null 表示“空”。
数组
//JavaScript的数组可以包括任意数据类型。 var arr = [1, 2, 3.14, 'Hello', null, true]; //另一种创建数组的方法是通过 Array() 函数实现 var arr = new Array(1, 2, 3);
对象
var person = { name: 'Bob', age: 20, tags: ['js', 'web', 'mobile'], city: 'Beijing', hasCar: true, zipcode: null }; //进行调用 person.name; // 'Bob' person.zipcode; // null
变量
//可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量 var a = 123; // a的值是整数123 a = 'ABC'; // a变为字符串
如果一个变量没有通过 var 申明就被使用,那么该变量就自动被申明为全局变量
使用 var 申明的变量则不是全局变量,,同名变量在不同的函数体内互不冲突。
在strict模式强制通过 var 申明变量,否则将导致运行错误
//启用strict模式的方法是在JavaScript代码的第一行写上: 'use strict';
多行字符串
//最新的ES6标准新增了一种多行字符串的表示方法 `这是一个 多行 字符串`;
模板字符串
//要把多个字符串连接起来,可以用 + 号连接: var name = '小明'; var age = 20; var message = '你好, ' + name + ', 你今年' + age + '岁了!'; alert(message); //如果有很多变量需要连接,用 + 号就比较麻烦。 //ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量 var name = '小明'; var age = 20; var message = `你好, ${name}, 你今年${age}岁了!`; alert(message);
操作字符串
//字符串常见的操作如下: var s = 'Hello, world!'; s.length; // 13 s[0]; // 'H' s[6]; // ' ' s[13]; // undefined 超出范围的索引不会报错,但一律返回undefined 1234567 //字符串是不可变的,对字符串的某个索引赋值,不会有任何错误,但也没有任何效果 var s = 'Test'; s[0] = 'X'; alert(s); // s仍然为'Test
字符串常用方法,不会改变原有的字符串,而是返回一个新的
//toUpperCase() 把一个字符串全部变为大写 var s = 'Hello'; s.toUpperCase(); // 返回'HELLO' //toLowerCase() 把一个字符串全部变为小写 var lower = s.toLowerCase(); // 返回'hello'并赋值给变量lower lower; // 'hello' //indexOf() 会搜索指定字符串出现的位置 var s = 'hello, world'; s.indexOf('world'); // 返回7 s.indexOf('World'); // 没有找到指定的子串,返回-1 1 //substring() 返回指定索引区间的子串 s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello' s.substring(7); // 从索引7开始到结束,返回'world'
要取得 Array 的长度,直接访问 length 属性
var arr = [1, 2, 3.14, 'Hello', null, true]; arr.length; // 6
直接给 Array 的 length 赋一个新的值会导致 Array 大小的变化
var arr = [1, 2, 3]; arr.length; // 3 arr.length = 6; arr; // arr变为[1, 2, 3, undefined, undefined, undefined] arr.length = 2; arr; // arr变为[1, 2]
Array 可以通过索引把对应的元素修改为新的值,因此,对 Array 的索引进行赋值会直接修改这个 Array
var arr = ['A', 'B', 'C']; arr[1] = 99; arr; // arr现在变为['A', 99, 'C']
请注意,如果通过索引赋值时,索引超过了范围,同样会引起 Array 大小的变化:
var arr = [1, 2, 3]; arr[5] = 'x'; arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
常用方法
indexOf
//搜索指定元素的位置 var arr = [10, 20, '30', 'xyz']; arr.indexOf(10); // 元素10的索引为0 arr.indexOf(30); // 元素30没有找到,返回-1 arr.indexOf('30'); // 元素'30'的索引为2
slice
//截取数组的一部分,然后返回新的数组 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'] //如果不给slice传递任何参数,则其会全部截取,可用做复制的数组用途 var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; var aCopy = arr.slice(); aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G'] aCopy === arr; // false
push和 pop
//push() 向 Array 的末尾添加若干元素 //pop() 则把 Array 的最后一个元素删除掉 var arr = [1, 2]; arr.push('A', 'B'); // 返回Array新的长度: 4 arr; // [1, 2, 'A', 'B'] arr.pop(); // pop()返回'B' arr; // [1, 2, 'A'] arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次 arr; // [] arr.pop(); // 空数组继续pop不会报错,而是返回undefined arr; // []
unshift和shift
//同push和pop一样,只不过是从头部进行添加和删除 var arr = [1, 2]; arr.unshift('A', 'B'); // 返回Array新的长度: 4 arr; // ['A', 'B', 1, 2] arr.shift(); // 'A' arr; // ['B', 1, 2] arr.shift(); arr.shift(); arr.shift(); // 连续shift 3次 arr; // [] arr.shift(); // 空数组继续shift不会报错,而是返回undefined arr; // []
sort
//为数组元素进行排序 var arr = ['B', 'C', 'A']; arr.sort(); arr; // ['A', 'B', 'C']
reverse
//将整个数组次序进行反转 var arr = ['one', 'two', 'three']; arr.reverse(); arr; // ['three', 'two', 'one']
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
//将两个数组连接成新的数组,但没有修改原来的数组,而是返回一个新的数组 var arr = ['A', 'B', 'C']; var added = arr.concat([1, 2, 3]); added; // ['A', 'B', 'C', 1, 2, 3] arr; // ['A', 'B', 'C'] //concat() 方法可以接收任意个元素和Array,并且自动把Array拆开,然后全部添加到新的Array里 var arr = ['A', 'B', 'C']; arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
join
//将当前数组中的元素都用指定字符串连接起来 var arr = ['A', 'B', 'C', 1, 2, 3]; arr.join('-'); // 'A-B-C-1-2-3'
多维数组
var arr = [[1, 2, 3], [400, 500, 600], '-']; var x = arr[1][2]; console.log(x); // x应该为500
定义与获取属性
//定义一个对象 var 对象名 = { key: 'value', key: 'value', key: 'value' } //获取对象的属性 对象名.key
动态的添加和删除属性
//可通过直接赋值增加一个新的属性 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属性也不会报错
检验对象是否拥有某一个属性
'name' in xiaoming; // true 'grade' in xiaoming; // false //但是用in这个属性不一定是这个对象的,它可能是这个对象继承得到的 'toString' in xiaoming; // true toString定义在原型链的Object对象上 //要判断一个属性是否是 xiaoming 自身拥有的,而不是继承得到的,可以用 hasOwnProperty() 方法: xiaoming.hasOwnProperty('name'); // true xiaoming.hasOwnProperty('toString'); // false
if判断
for循环
for…in
var o = { name: 'Jack', age: 20, city: 'Beijing' }; //把一个对象的所有属性依次循环出来 for (var key in o) { if (o.hasOwnProperty(key)) { console.log(key); // 'name', 'age', 'city' } } //由于 Array 也是对象,而它的每个元素的索引被视为对象的属性,所以遍历出来是下标 var a = ['A', 'B', 'C']; for (var i in a) { console.log(i); // '0', '1', '2' 遍历出元素的下标 console.log(a[i]); // 'A', 'B', 'C' }
while
do…while
JavaScript的对象有个问题,键key必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。
为了解决这个问题,最新的ES6规范引入了新的数据类型 Map。
举个例子,假设要根据同学的名字查找对应的成绩:
//如果用 Array 实现,需要两个 Array //给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,Array越长,耗时越长。 var names = ['Michael', 'Bob', 'Tracy']; var scores = [95, 75, 85]; //使用Map var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]); m.get('Michael'); // 95
Map的常用方法
var m = new Map(); // 空Map m.set('Adam', 67); // 添加新的key-value m.has('Adam'); // 是否存在key 'Adam': true m.get('Adam'); // 67 m.delete('Adam'); // 删除key 'Adam' m.get('Adam'); // undefined //由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉 var m = new Map(); m.set('Adam', 67); m.set('Adam', 88); m.get('Adam'); // 88
Set 和 Map 类似,也是一组key的集合,但不存储value。由于key不能重复,在 Set 中,没有重复的key
//自动过滤相同的元素 var s = new Set([1, 2, 3, 3, '3']); s; // Set {1, 2, 3, "3"}
Set的常用方法
//通过 add(key) 方法可以添加元素到 Set 中,可以重复添加,但不会有效果 s.add(4); s; // Set {1, 2, 3, 4} s.add(4); s; // 仍然是 Set {1, 2, 3, 4} //通过 delete(key) 方法可以删除元素 var s = new Set([1, 2, 3]); s; // Set {1, 2, 3} s.delete(3); s; // Set {1, 2}
遍历Map 和 Set,无法像数组一样遍历下标
为了统一集合类型,ES6标准引入了新的 iterable 类型,Array,Map,Set
具有 iterable 类型的集合可以通过新的 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 方法,它接收一个函数,每次迭代就自动回调该函数。
//以 Array 为例: a.forEach(function (element, index, array) { // element: 指向当前元素的值 // index: 指向当前索引 // array: 指向Array对象本身 console.log(element + ', index = ' + index); }); //Set 没有索引,因此回调函数的前两个参数都是元素本身 var s = new Set(['A', 'B', 'C']); s.forEach(function (element, sameElement, set) { console.log(element); }); //Map 的回调函数参数依次为 value 、 key 和 map 本身 var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]); m.forEach(function (value, key, map) { console.log(value); });
定义方式
//1、定义函数方式一 function abs(x) { if (x >= 0) { return x; } else { return -x; } } //2、定义函数方式二:此方式下的函数为匿名函数,可通过变量来调用此函数 var abs = function (x) { if (x >= 0) { return x; } else { return -x; } };
调用函数
//调用函数 abs(10); // 返回10 abs(-9); // 返回9 //由于JavaScript允许传入任意个参数而不影响调用,写多的内部不需要 abs(10, 'blablabla'); // 返回10 abs(-9, 'haha', 'hehe', null); // 返回9 //传入的参数比定义的少也没有问题,缺失的将当成undefined接收,结果为NaN abs(); // 返回NaN //要避免收到 undefined ,可以对参数进行检查 function abs(x) { if (typeof x !== 'number') { throw 'Not a number'; }if (x >= 0) { return x; } else { return -x; } }
函数的参数arguments和rest参数
JavaScript还有一个免费赠送的关键字 arguments ,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数
//实际上 arguments 最常用于判断传入参数的个数 //由于JavaScript函数允许接收任意个参数,于是我们就不得不用 arguments 来获取所有参数 function foo(a, b) { var i, rest = []; if (arguments.length > 2) { for (i = 2; i<arguments.length; i++) { rest.push(arguments[i]); } } console.log('a = ' + a); console.log('b = ' + b); console.log(rest); }
ES6标准引入了rest参数,上面的函数可以改写为
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 []
如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量
不同函数内部的同名变量互相独立,互不影响
内部函数可以访问外部函数定义的变量,反过来则不行
变量提升
'use strict'; function foo() { var x = 'Hello, ' + y; console.log(x); var y = 'Bob'; } foo();//:Hello, undefined , 说明 y 的值为 undefined //是因为JavaScript引擎自动提升了变量 y 的声明,但不会提升变量 y 的赋值。
由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有
变量”这一规则。最常见的做法是用一个 var 申明函数内部用到的所有变量.
全局作用域
//不在任何函数内定义的变量就具有全局作用域。 //实际上,JavaScript默认有一个全局对象 window ,全局作用域的变量实际上被绑定到 window 的一个属性 var course = 'Learn JavaScript'; alert(course); // 'Learn JavaScript' alert(window.course); // 'Learn JavaScript'
不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
//减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。 //把自己的代码全部放入唯一的名字空间 MYAPP 中,会大大减少全局变量冲突的可能。 //许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。 // 唯一的全局变量MYAPP: var MYAPP = {}; // 其他变量: MYAPP.name = 'myapp'; MYAPP.version = 1.0; // 其他函数: MYAPP.foo = function () { return 'foo'; };
在一个对象中绑定函数,称为这个对象的方法。
var xiaoming = { name: '小明', birth: 1990, age: function () { var y = new Date().getFullYear(); return y - this.birth; // this 是一个特殊变量,它始终指向当前对象,也就是 xiaoming 这个变量 } }; xiaoming.age; // function xiaoming.age() xiaoming.age(); // 今年调用是25,明年调用就变成26了
若是拆开写:
function getAge() { var y = new Date().getFullYear(); return y - this.birth; //谁调用就指向谁 } var xiaoming = { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 25, 正常结果 getAge(); // NaN 因为无人调用,this指向全局对象window,而其没有这个方法
要指定函数的 this 指向哪个对象,可以用函数本身的 apply 方法,它接收两个参数,第一个参数
就是需要绑定的 this 变量,第二个参数是 Array ,表示函数本身的参数。
function getAge() { var y = new Date().getFullYear(); return y - this.birth; //谁调用就指向谁 } var xiaoming = { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 25, 正常结果 getAge(); // NaN 因为无人调用,this指向全局对象window,而其没有这个方法
为了区分对象的类型,我们用 typeof 操作符获取对象的类型,它总是返回一个字符串。
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'
//常用方法 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形式表示的时间戳
获取当前时间戳,可以用:
if (Date.now) { console.log(Date.now()); // 老版本IE没有now()方法 } else { console.log(new Date().getTime()); } //使用时间戳就无需关心时区,只需传递时间戳然后由本地解析成正确时间即可
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛。
采用完全独立于编程语言的文本格式来存储和表示数据。
JSON 和 JavaScript 对象的关系
//JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。 var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的 var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串
JSON 和 JavaScript 对象的互相转换
//要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法 var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'} //要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法 var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b":"World"}'
JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } }; var xiaoming = { name: '小明' }; //小明的原型是student xiaoming.__proto__ = Student;
class 从ES6开始正式被引入到JavaScript中。 class 的目的就是让定义类更简单。
//prototype function Student(name) { this.name = name; } // 现在要给这个Student新增一个方法 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();
class的继承
class PrimaryStudent extends Student { constructor(name, grade) { super(name); // 记得用super调用父类的构造方法! this.grade = grade; } myGrade() { alert('I am at grade ' + this.grade); } }
window对象不但充当全局作用域,而且表示浏览器窗口。
//window 对象有 innerWidth 和 innerHeight 属性,可以获取浏览器窗口的内部宽度和高度 console.log('window inner size: ' + window.innerWidth + ' x ' + window.innerHeight); //还有一个 outerWidth 和 outerHeight 属性,可以获取浏览器窗口的整个宽高
navigator 对象表示浏览器的信息
//常用属性 console.log('appName = ' + navigator.appName); //名称 console.log('appVersion = ' + navigator.appVersion);//版本 console.log('language = ' + navigator.language);//设置的语言 console.log('platform = ' + navigator.platform); //操作系统类型 console.log('userAgent = ' + navigator.userAgent);//浏览器设定的 User-Agent 字符串 /*请注意, navigator 的信息可以很容易地被用户修改,所以JavaScript读取的值不一定是正确的。 很多初学者为了针对不同浏览器编写不同的代码,喜欢用 if 判断浏览器版本*/ var width; if (getIEVersion(navigator.userAgent) < 9) { width = document.body.clientWidth; } else { width = window.innerWidth; } /*但这样既可能判断不准确,也很难维护代码。正确的方法是充分利用JavaScript对不存在属性返回 undefined 的特性,直接用短路运算符 || 计算*/ var width = window.innerWidth || document.body.clientWidth;
screen 对象表示屏幕的信息
//常用的属性有: /* screen.width:屏幕宽度,以像素为单位; screen.height:屏幕高度,以像素为单位; screen.colorDepth:返回颜色位数,如8、16、24 */ console.log('Screen size = ' + screen.width + ' x ' + screen.height);
location 对象表示当前页面的URL信息
location.href;//http://www.example.com:8080/path/index.html?a=1&b=2#TOP 1 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.reload();//重新加载当前页面 location.assign('https://baidu.com/'); // 重新加载新的页面
document 对象就是整个DOM树的根节点
//document 的 title 属性是从HTML文档中的 xxx 读取的,但是可以动态改变 document.title = '百度一下,你就知道'; //用 document 对象提供的 getElementById() 和 getElementsByTagName() 可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点 var menu = document.getElementById('code-menu'); var drinks = document.getElementsByTagName('dt'); //Cookie是由服务器发送的key-value标示符,JavaScript可以通过 document.cookie 读取到当前页面的Cookie document.cookie; // 'v=123; remember=true; prefer=zh' //为了确保安全,服务器端在设置Cookie时,应该始终坚持使用 httpOnly
history 对象保存了浏览器的历史记录
//Js可以调用 history 对象的 back() 或 forward () ,相当于用户点击了浏览器的“后退”或“前进”按钮 任何情况,你都不应该使用 history 这个对象了
由于HTML文档被浏览器解析后就是一棵DOM树,要改变HTML的结构,就需要通过JavaScript来操作DOM。
1、获得DOM
第一种方法
// 返回ID为'test'的节点: var test = document.getElementById('test'); // 先定位ID为'test-table'的节点,再返回其内部所有tr节点: var trs = document.getElementById('test-table').getElementsByTagName('tr'); // 先定位ID为'test-div'的节点,再返回其内部所有class包含red的节点: var reds = document.getElementById('test- div').getElementsByClassName('red'); // 获取节点test下的所有直属子节点: var cs = test.children; // 获取节点test下第一个、最后一个子节点: var first = test.firstElementChild; var last = test.lastElementChild;
第二种方法
// 通过querySelector获取ID为q1的节点: var q1 = document.querySelector('#q1'); // 通过querySelectorAll获取q1节点内的符合条件的所有节点: var ps = q1.querySelectorAll('div.highlighted > p');
2、更新DOM
修改文本
//修改文本一: innerHTML 属性 // 获取<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>的内部结构已修改 //修改文本二: innerText 属性 // 获取<p id="p-id">...</p> var p = document.getElementById('p-id'); // 设置文本: p.innerText = '<script>alert("Hi")</script>'; // HTML被自动编码,无法设置一个<script>节点: // <p id="p-id"><script>alert("Hi")</script></p>
修改CSS:DOM节点的 style 属性对应所有的CSS,可以直接获取或设置。
/*因为CSS允许 font-size 这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为 驼峰式命名 fontSize */ // 获取<p id="p-id">...</p> var p = document.getElementById('p-id'); // 设置CSS: p.style.color = '#ff0000'; p.style.fontSize = '20px'; p.style.paddingTop = '2em';
3、插入DOM
innerHTML 会直接替换掉原来的所有子节点。如果这个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>
//把 JavaScript 添加到的最后一项 var js = document.getElementById('js'), //获得将要插入的节点 list = document.getElementById('list'); //获得父节点 list.appendChild(js);//父节点.appendChild(需插入结点)
<!-- 插入结果--> <div id="list"> <p id="java">Java</p> <p id="python">Python</p> <p id="scheme">Scheme</p> <p id="js">JavaScript</p> </div>
就动态地给文档添加了新的CSS定义
var d = document.createElement('style'); d.setAttribute('type', 'text/css'); d.innerHTML = 'p { color: red }'; //head 头部标签 document.getElementsByTagName('head')[0].appendChild(d);
insertBefore,把一个子节点添加到指定节点之前
//子节点会插入到referenceElement 之前。 parentElement.insertBefore(newElement, referenceElement);
var list = document.getElementById('list'),//获得父节点parentElement ref = document.getElementById('python'), //获得指定结点 haskell = document.createElement('p');//将要插入的结点 //设置插入结点的属性 haskell.id = 'haskell'; haskell.innerText = 'Haskell'; list.insertBefore(haskell, ref);
<!-- 插入结果--> <div id="list"> <p id="java">Java</p> <p id="haskell">Haskell</p> <p id="python">Python</p> <p id="scheme">Scheme</p> </div>
4、删除DOM
要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的 removeChild 把自己删掉
// 拿到待删除节点: var self = document.getElementById('to-be-removed'); // 拿到父节点: var parent = self.parentElement; // 删除: var removed = parent.removeChild(self); removed === self; // true
用JavaScript操作表单和操作DOM是类似的,因为表单本身也是DOM树
varparent=document.getElementById('parent'); parent.removeChild(parent.children[0]); parent.removeChild(parent.children[1]);//<--浏览器报错 /*浏览器报错: parent.children[1] 不是一个有效的节点。 原因就在于,当 First 节点被删除后, parent.children 的节点数量已经从2变为了1,索引 [1] 已经不存在了。 因此,删除多个节点时,要注意 children 属性时刻都在变化*/
对于单选框和复选框, value 属性返回的永远是HTML预设的值,而我们需要获得的实际是用户是否“勾上了”选项,所以应该用 checked 判断
//<label><inputtype="radio"name="weekday"id="monday"value="1">Monday</label> //<label><inputtype="radio"name="weekday"id="tuesday"value="2">Tuesday</label> varmon=document.getElementById('monday'); vartue=document.getElementById('tuesday'); mon.value;//'1' 获取的是值 tue.value;//'2' mon.checked;//true或者false 检查是否被选中 tue.checked;//true或者false //设置 checked 为 true 或 false 即可
提交表单
<!--方式一是通过 <form> 元素的 submit() 方法提交一个表单,例如,响应一个 button 的 click 事件--> <!--HTML--> <form id="test-form"> <inputtype="text"name="test"> <button type="button" onclick="doSubmitForm()">Submit</button> </form> <script> //这种方式的缺点是扰乱了浏览器对form的正常提交。 function doSubmitForm(){ var form=document.getElementById('test-form'); //可以在此修改form的input... //提交form: form.submit(); } </script>
<!--第二种方式是响应 <form> 本身的onsubmit 事件,在提交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>
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> <!-- 引入MD5加密,然后会返回加密好的 --> <script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"> </script> <script> function checkForm() { var pwd = document.getElementById('password'); // 把用户输入的明文变为MD5: pwd.value = md5(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"> //将password隐藏起来 <input type="password" id="input-password"> <input type="hidden" id="md5-password" name="password"> <button type="submit">Submit</button> </form> <!-- 引入MD5加密,然后会返回加密好的 --> <script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"> </script> <script> function checkForm() { var input_pwd = document.getElementById('input-password'); var md5_pwd = document.getElementById('md5-password'); // 把用户输入的明文变为MD5: pwd.value = md5(pwd.value); md5_pwd.value = md5(input_pwd.value); // 继续下一步: return true; } </script>
jQuery,是JavaScript世界中使用最广泛的一个库。jQuery是一个 jquery-xxx.js 文件。
官网:https://jquery.com/
使用jQuery只需要在页面的 head 引入jQuery文件即可:
<head> <script src="//code.jquery.com/jquery-1.11.3.min.js"></script> ... </head>
//有了jQuery,就无需像之前那样原生的使用document.getElementById('test');来获取节点 //公式: $(selector).action() selector中就可以像CSS选择器一样书写 //按id来查找:查找<div id="abc"> var div = $('#abc'); //按tag查找 var ps = $('p'); // 返回所有<p>节点 ps.length; // 数一数页面有多少个<p>节点 //按class查找,注意在class名称前加一个 . : var a=$('.red');//所有节点包含`class="red"`都将返回 //例如: //<divclass="red">...</div> //<pclass="greenred">...</p> //按属性查找 var email=$('[name=email]');//找出<???name="email"> var passwordInput=$('[type=password]');//找出<???type="password"> var a=$('[items="AB"]');//找出<???items="AB"> var icons=$('[name^=icon]');//找出所有name属性值以icon开头的DOM //例如:name="icon-1",name="icon-2" var names=$('[name$=with]');//找出所有name属性值以with结尾的DOM //例如:name="startswith",name="endswith"
修改Text和HTML:text()和html() 方法
<!--HTML结构--> <ul id="test-ul"> <li class="js">JavaScript</li> <li name="book">Java&JavaScript</li> </ul>
$('#test-ul li[name=book]').text();//'Java&JavaScript' $('#test-ul li[name=book]').html();//'Java&JavaScript' //jQuery的API设计非常巧妙:无参数调用 text() 是获取文本,传入参数就变成设置文本,HTML也是类似操作 var j1=$('#test-ul li.js'); var j2=$('#test-ul li[name=book]'); j1.html('<span style="color:red">JavaScript</span>'); j2.text('JavaScript&ECMAScript'); //一个jQuery对象可以包含0个或任意个DOM对象,它的方法实际上会作用在对应的每个DOM节点上。 $('#test-ul li').text('JS');//两个节点都变成了JS
修改CSS
<!--jQuery对象有“批量操作”的特点,这用于修改CSS实在是太方便了--> <ul id="test-css"> <li class="langdy"><span>JavaScript</span></li> <li class="lang"><span>Java</span></li> <li class="langdy"><span>Python</span></li> <li class="lang"><span>Swift</span></li> <li class="langdy"><span>Scheme</span></li> </ul>
调用jQuery对象的 css(‘name’, ‘value’) 方法
$('#test-css li.dy>span').css('background-color', '#ffd351').css('color', 'red'); var div=$('#test-div'); div.css('color');//'#000033',获取CSS属性 div.css('color','#336699');//设置CSS属性 div.css('color','');//清除CSS属性 //css()方法将作用于DOM节点的 style 属性,具有最高优先级。如果要修改 class 属性,可以用jQuery提供的下列方法 var div=$('#test-div'); div.hasClass('highlight');//false,class是否包含highlight div.addClass('highlight');//添加highlight这个class div.removeClass('highlight');//删除highlight这个class
显示和隐藏DOM
要隐藏一个DOM,我们可以设置CSS的 display 属性为 none,利用 css() 方法就可以实现。
不过,要显示这个DOM就需要恢复原有的 display 属性,这就得先记下来原有的 display 属性到底是 block 还是 inline 还是别的值。
jQuery直接提供 show() 和 hide() 方法,我们不用关心它是如何修改 display 属性的
vara=$('a[target=_blank]'); 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是否有效
attr()和removeAttr() 方法用于操作DOM节点的属性
//<divid="test-div"name="Test"start="1">...</div> var div=$('#test-div'); div.attr('data');//undefined,属性不存在 div.attr('name');//'Test' div.attr('name','Hello');//div的name属性变为'Hello' div.removeAttr('name');//删除name属性 div.attr('name');//undefined
操作表单
jQuery对象统一提供 val() 方法获取和设置对应的 value 属性
/* <input id="test-input" name="email" value=""> <select id="test-select" name="city"> <option value="BJ" selected>Beijing</option> <option value="SH">Shanghai</option> <option value="SZ">Shenzhen</option> </select> <textarea id="test-textarea">Hello</textarea> */ var input = $('#test-input'), select = $('#test-select'), textarea = $('#test-textarea'); input.val(); // 'test' input.val('abc@example.com'); // 文本框的内容已变为abc@example.com select.val(); // 'BJ' select.val('SH'); // 选择框已变为Shanghai textarea.val(); // 'Hello' textarea.val('Hi'); // 文本区域已更新为'Hi'
添加DOM
<!-- HTML结构 --> <div id="list"> <p id="js">JavaScript</p> <p id="java">Python</p> <p id="python">Swift</p> </div>
新增一个结点
var ul = $('#test-div>ul');//首先要拿到 <ul> 节点 ul.append('<li><span>Haskell</span></li>');//调用 append() 传入HTML片段 //append() 把DOM添加到最后, prepend() 则把DOM添加到最前 //同级节点可以用 after() 或者 before() 方法 var js = $('#test-div>ul>li:first-child'); js.after('<li><span>Lua</span></li>');
删除DOM
var li = $('#test-div>ul>li'); li.remove(); // 所有<li>全被删除
jQuery能够绑定的事件主要包括:
鼠标事件
click: 鼠标单击时触发;
dblclick:鼠标双击时触发;
mouseenter:鼠标进入时触发;
mouseleave:鼠标移出时触发;
mousemove:鼠标在DOM内部移动时触发;
hover:鼠标进入和退出时触发两个函数,相当于mouseenter加上mouseleave
键盘事件
键盘事件仅作用在当前焦点的DOM上
其他事件
focus:当DOM获得焦点时触发;
blur:当DOM失去焦点时触发;
change:当 、 或 的内容改变时 触发;
submit:当 提交时触发;
ready:当页面被载入并且DOM树完成初始化后触发。
初始化事件
我们自己的初始化代码必须放到 document 对象的 ready 事件中,保证DOM已完成初始化
$(document).on('ready',function(){ $('#testForm).on('submit',function(){ alert('submit!'); }); }); //简化 $(document).ready(function () { // on('submit', function)也可以简化: $('#testForm).submit(function () { alert('submit!'); }); }); //更简化 $(function () { // init... });
事件参数
有些事件,如 mousemove 和 keypress ,我们需要获取鼠标位置和按键的值,否则监听这些事件就
没什么意义了。所有事件都会传入 Event 对象作为参数,可以从 Event 对象上获取到更多的信息:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> #testMouseMoveDiv{ width: 300px; height: 300px; border: 1px solid black; } </style> </head> <body> mousemove: <span id="testMouseMoveSpan"></span> <div id="testMouseMoveDiv"> 在此区域移动鼠标试试 </div> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> <script> $(function () { $('#testMouseMoveDiv').mousemove(function (e) { $('#testMouseMoveSpan').text('pageX = ' + e.pageX + ', pageY = ' + e.pageY); }); }); </script> </body> </html>