js基础入门

第0章 课前说明

0.1 学习方法及态度

没有学不会的知识,只有不努力的傻子!!!

学习的过程很痛苦,不学习的日子是苦难!!!

0.2 学前准备

1:拒绝一阳指和二指禅;
2:打字练习,及格线: 100/分

键盘指法

3:windows中的常用快捷键

0.3 开发工具

0.3.1 浏览器

浏览器是指可以显示网页服务器或者文件系统的HTML文件(标准通用标记语言的一个应用)内容,并让用户与这些文件交互的一种软件。

国内网民计算机上常见的网页浏览器有,QQ浏览器、Internet Explorer、Firefox、Safari,Opera、Google Chrome、百度浏览器、搜狗浏览器、猎豹浏览器、360浏览器、UC浏览器、傲游浏览器、世界之窗浏览器等,浏览器是最经常使用到的客户端程序。

常用的五大浏览器:chrome,firefox,Safari,ie,opera;

我们用的最多的则是 chrome(谷歌浏览器) 和 Firefox(火狐浏览器)

代码执行

0.3.2 编辑器

Sublime Text、VSCode、Atom、Brackets、WebStorm、Notepad++、HBuilder、Vim、记事本…

0.4 JavaScript语言的强大

http://impress.github.io/impress.js/
http://naotu.baidu.com/
http://echarts.baidu.com/index.html

总结:

摆正学习心态;编程基本功要练好;编程使用的工具要熟悉且顺手;我们要学的 JavaScript 很强大;

第1章 JavaScript介绍

1.1 JavaScript编程语言

JavaScript,简称JS,是一种客户端脚本语言,主要用来向HTML网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。

可以直接嵌入HTML页面,但写成单独的js文件有利于结构和行为的分离。

在绝大多数浏览器的支持下,可以在多种平台下运行(如WindowsLinux、Mac、Android、iOS等)

JS主要运行于客户端(浏览器),也可以运行在服务端(操作系统)

JavaScript 和 Java 是两种不同的编程语言:JavaScript主要用于客户端,Java用于服务端。

JavaScript现在的意义(应用场景)

JavaScript 发展到现在几乎无所不能。

  1. 网页特效 *
  2. 服务端开发(Node.js) *
  3. 命令行工具(Node.js)
  4. 桌面程序(Electron)
  5. App(Cordova)
  6. 控制硬件-物联网(Ruff)
  7. 游戏开发(cocos2d-js)

1.2 发展及历史

1994年Netscape公司推出免费版本浏览器 Netscape Navigator(网景浏览器1.0)
1995年微软公司发布 Internet Explorer 1.0。
1995年网景公司为适应市场变化,需要开发一门专门在浏览器运行的脚本语言,这个任务交给了布兰登,为了应付公司安排的任务,
他只用10天时间就提交了工作,并将这门语言命名为 LiveScript;
后来为了蹭sun公司java的热度,与sun公司合作,将其临时改名为“JavaScript”;
1996年8月,微软模仿JavaScript开发了一种相近的语言,取名为JScript,首先内置于IE 3.0
1997年7月,ECMA组织发布ECMAScript 1.0版;
此后,明争暗斗不断,1998年6月,ECMAScript 2.0版发布,1999年12月,ECMAScript 3.0版发布;
2007年10月,ECMAScript 4.0版草案发布,2008年7月中止ECMAScript 4.0的开发,并发布3.1版本;
会后不久,ECMAScript 3.1就改名为ECMAScript 5。
2011年6月,ECMAscript 5.1版发布,现在使用最为广泛的版本 版发布,现在使用最为广泛的版本;
2015年6月,ECMAScript 6正式发布,并且更名为“ECMAScript 2015”;
随后,ECMA组织决定,每年发布一个升级版本,以年号来代替版本号,如:ECMAScript 2016、ECMAScript 2017;

另外:
1996年,样式表标准CSS第一版发布;
1997年,DOM模式第一版正式应用,目前的通用版本是DOM3,下一代版本DOM 4正在拟定中。
1999年,IE5部署了XMLHttpRequest接口,允许JavaScript发出HTTP请求;
2001年,提出了JSON格式,用于取代XML格式。
2002年,Mozilla项目发布第一版Firefox。
2003年,苹果公司发布了Safari浏览器的第一版。
2006年,jQuery函数库诞生
2007年,Webkit引擎在iPhone手机中得到部署;
2008年,为Chrome浏览器而开发的V8编译器(解析引擎)诞生;
2009年,基于V8解析引擎的Node.js项目诞生,迎来前后端JS的霸权时代;
2009年,Google发布Chrome OS
2009年,Google发布Angular框架;
2013年,Mozilla基金会发布手机操作系统Firefox OS,该操作系统的整个用户界面都使用JavaScript;
2013年5月,Facebook发布UI框架库React;
2014年,尤雨溪发布开源前端开发库Vue.js;
2015年3月,Facebook公司发布了 React Native项目;

1.3 JavaScript和HTML、CSS

  1. HTML:提供网页的结构,提供网页中的内容

  2. CSS: 用来样式排版、美化网页

  3. JavaScript: 可以用来控制网页内容,给网页增加动态的效果

1.4 JavaScript的组成

ECMA 欧洲计算机制造联合会;
ECMAScript 是一套标准,定义了一种语言的标准,规定了基本语法、数据类型、关键字、具体API的设计规范等,解析引擎设计的参考标准,但与具体实现无关;

1496912475691

1.4.1 ECMAScript - JavaScript的核心

ECMAScript是一套语法标准,描述了JavaScript语言的基本语法和数据类型,是JavaScript的核心。 ES5 ES6

1.4.2 BOM - 浏览器对象模型

一套操作浏览器功能的API

通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等

1.4.3 DOM - 文档对象模型

一套操作页面元素的API

DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

1.5 JS学习概况

我们在学习JS时,需要学习的内容分为两部分,语言结构宿主环境提供的API;

语言结构部分主要时语言规则及内置对象;

而宿主环境的API,根据宿主环境不同而不同,以浏览器为例(js还可以运行在服务器/操作系统),最常见的三个类型:
浏览器控制类、DOM操作类、网络控制类;

总结:

JavaScript编程语言简称 JS,是一种嵌入式的脚本语言,应用范围及其广泛,由布兰登-艾奇开发,在20+年的发展中历经沧桑,学习 JS 分为语言规则及宿主环境两部分;

第2章 入门

2.1 如何写一段JS代码并运行

  • 写在行内
01.html: 
<input type="button" value="按钮" onclick="alert('Hello World')" />
  • 写在script标签中 *
02.html:
<head>
  <script>
    alert('Hello World!');
  </script>
</head>
  • 写在外部js文件中,在页面引入
03.html:
<script src="main.js"></script>
main.js:

alert('Hello World!');

注意点: 引用外部js文件的 script 标签中不可以再写JavaScript代码,即使写了也不会执行,没有作用

温馨提示:

下面开始进入 JS 基础语法的学习,非常枯燥,别睡着……

音乐很优美很动听,但学五线谱真的是乏味无聊痛苦不堪;

2.3 变量

2.3.1 什么是变量
  • 什么是变量

    变量是计算机内存中存储数据的标识符,根据变量名称可以获取到内存中存储的数据

  • 为什么要使用变量

    使用变量可以方便的获取或者修改内存中的数据

变量就是存储数据的容器;

2.3.2 如何使用变量
  • var声明变量
var age;
  • 变量的赋值
var age;
age = 18;
  • 同时声明多个变量
var age, name, sex;
age = 10;
name = 'zs';
  • 同时声明多个变量并赋值
var age = 10, name = 'zs';
2.3.3 变量的命名规则和规范
  • 规则 - 必须遵守的,不遵守会报错

    • 由字母、数字、下划线、$符号组成,且不能以数字开头

    • 区分大小写

    • 不能是关键字和保留字,例如:for、while。

  • 规范 - 建议遵守的,不遵守不会报错

    • 变量名必须有意义
    • 遵守驼峰命名法。(首字母小写,后面单词的首字母需要大写。例如:userName、userPassword)
  • 下面哪些变量名不合法

    a	    
    1
    age18
    18age
    name
    $
    $name
    _sex
    &sex
    theworld  
    theWorld
    

name变量名,本身不是保留字/关键字, 建议少用。 name在有的浏览器中,是自动声明过的。

2.3.4 案例
  1. 交换两个变量的值
var a = '1';
var b = '2';
//  借助第三个变量
var c = a;
a=b;
b=c;
console.log(a,b);
  1. 不使用临时变量,交换两个数值变量的值
//第二种方式
var num1 = 10;
var num2 = 20;
//计算的方式:累加,然后相减
num1 = num1 + num2;//num1的结果是30
num2 = num1 - num2;//num2的结果是10
num1 = num1 - num2;//num1的结果是20
console.log(num1);
console.log(num2);
2.3.5 代码调试(输出变量)
  1. alert 弹框 :浏览器页面弹框
var num1 = 10;
alert(num1);
  1. console.log() 浏览器console控制台
var num1 = 10;
var num2 = 20;
console.log(num1);
console.log(num1, num2);
  1. document.write() 浏览器页面中
var num1 = 10;
document.write(num1);

2.4 数据类型

2.4.1 简单数据类型

Number、String、Boolean、Undefined、Null

获取变量的类型

typeof

var age = 18;
console.log(typeof age);  // 'number'
Number类型
  • 数值字面量:数值的固定值的表示法(数值直接量)

    110 1024 60.5

  • 浮点数(小数)

  • 浮点数的精度问题

浮点数
	var n = 5e-324;   // 科学计数法  5乘以10的-324次方  
浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数
   var result = 0.1 + 0.2;    // 结果不是 0.3,而是:0.30000000000000004
   console.log(0.07 * 100);
   不要判断两个浮点数是否相等
  • 数值范围

    最小值:Number.MIN_VALUE// 这个值为: 5e-324  5乘以10的-324次方
    最大值:Number.MAX_VALUE// 这个值为: 1.7976931348623157e+308
    无穷大:Infinity
    无穷小:-Infinity
    
String类型

‘abc’ “abc” 单双引号引起来的一连串字符

  • 字符串字面量(直接量)

    ‘程序猿’,‘程序媛’, “黑马程序猿”

思考:如何打印以下字符串。
我是一个 “正直” 的人
我很喜欢 "黑马 ‘程序猿’ "

注: 转义后单双引号 只能 就近和转义后的单双引号进行配对

  • 转义符

1498289626813

注: \b 退格符 \f又叫走纸或进纸或 换页符

var s = '我很喜欢   \"黑马  \'程序猿\'  \"';
console.log(s);
  • 字符串长度

length属性用来获取字符串的长度

var str = '黑马程序猿 Hello World';
console.log(str.length);
  • 字符串拼接

字符串拼接使用 + 连接

console.log(11 + 11);
console.log('hello' + ' world');
console.log('100' + '100');
console.log('11' + 11);
console.log('male:' + true);
  1. 两边只要有一个是字符串,那么+就是字符串拼接功能
  2. 两边如果都是数字,那么就是算术功能。
Boolean类型
  • Boolean字面量: true和false,区分大小写
  • 计算机内部存储:true为1,false为0
Undefined和Null
  1. undefined表示一个声明了没有赋值的变量,变量只声明的时候值默认是undefined
  2. null表示一个空,变量的值如果想为null,必须手动设置

注: 关于undefined和null是一个面试中很容易被问到的问题

2.4.2 复杂数据类型

​ Object 对象:保存很多数据的一种数据类型

后面详解;

题外话

如何使用谷歌浏览器,快速的查看数据类型?

字符串的颜色是黑色的,数值类型是蓝色的,布尔类型也是蓝色的,undefined和null是灰色的

console.log('ss',2,null,undefined,true);

2.5 注释

被注释的内容是不执行的,不管什么内容都不会运行;

单行注释

用来描述下面一个或多行代码的作用

// 这是一个变量
var name = 'hm';

多行注释

用来注释多条代码

/*
var age = 18;
var name = 'zs';
console.log(name, age);
*/

总结:

JS代码的书写,变量的声明方式,数据类型,注释;

第3章 数据类型转换

3.1 转换成字符串类型

  • toString()

    var num = 5;
    console.log(num.toString());
    
  • String()

    var s = null;
    console.log(s.toString());
    console.log(String(s));
    
    // String()函数存在的意义:有些值没有toString(),
    // 这个时候可以使用String()。比如:undefined和null
    
  • 拼接字符串方式

    num + “”,当 + 两边一个操作符是字符串类型,一个操作符是其它类型的时候,会先把其它类型转换成字符串再进行字符串拼接,返回字符串

3.2 转换成数值类型

  • Number()

    var a = Number('1');
    var b = Number(1);
    var c = Number('c');
    var d = Number(null);
    var e = Number(undefined);
    
    console.log(a,b,c,d,e); // 1 1 NaN 0 NaN
    
    // Number()可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回NaN
    
  • parseInt()

    var a = parseInt('1.2df');
    var b = parseInt(1);
    var c = parseInt('c12');
    var d = parseInt(null);
    var e = parseInt(undefined);
    
    console.log(a,b,c,d,e); //1 1 NaN NaN NaN
    
    // 如果第一个字符是数字会解析,直到遇到非数字结束
    // 如果第一个字符不是数字或者符号就返回NaN
    
  • parseFloat()

    var a = parseFloat('1.2df');
    var b = parseFloat('1.3.4');
    var c = parseFloat('c12');
    var d = parseFloat(null);
    var e = parseFloat(undefined);
    
    console.log(a,b,c,d,e); //1.2 1.3 NaN NaN NaN
    
    // parseFloat() 把字符串转换成浮点数
    // parseFloat()和parseInt非常相似,
    // 不同之处在与parseFloat会解析第一个 . 遇到第二个.或者非数字结束
    // 如果解析的内容里只有整数,解析成整数
    
  • +,-,-0 等运算

    var str = '500';
    console.log(+str);		// 取正
    console.log(-str);		// 取负
    console.log(str - 0);   
    

3.3 转换成布尔类型

  • Boolean()
var a = Boolean('0');
var b = Boolean(0);
var c = Boolean('1');
var d = Boolean(null);
var e = Boolean(undefined);
var f = Boolean(NaN);

console.log(a,b,c,d,e,f); //true false true false false false

// 0、''(空字符串) 、null、 undefined 、NaN 会转换成false  其它都会转换成true

总结:

字符串、数值及布尔类型的数据类型转换

第4章 操作符

表达式:值和操作符,运算会有一个结果;

同时,表达式中的每个数值及部分表达式,又称为 子表达式

4.1 算术运算符

+ - * / %  取余(取模)

4.2 一元运算符 *

一元运算符:只有一个操作数的运算符,一元运算会直接修改原始变量的数据;

5 + 6 两个操作数的运算符 二元运算符

++ 自身加 (自增)

– 自身减 (自减)

  • 前置++

    var num1 = 5;
    ++ num1; 
    
    var num2 = 6;
    console.log(num1 + ++ num2); //13
    
  • 后置++

    var num1 = 5;
    num1 ++;    
    var num2 = 6 
    console.log(num1 + num2 ++); //12
    
  • 猜猜看

    var a = 1; 
    var b = ++a + ++a; 
    console.log(b); //5
    
    var a = 1; 
    var b = a++ + ++a; 
    console.log(b);//4
    
    var a = 1; 
    var b = a++ + a++; 
    // console.log(b);  // 3
    
    var a = 1; 
    var b = ++a + a++; 
    console.log(b);//4
    

    总结
    前置++:先加1,后参与运算
    后置++:先参与运算,后加1

    后置++ 运算的两个条件,满其一就会执行

    1:整个表达式结束;2表达式没结束但是又被使用了;

上面两个理解后,下面两个自通
前置-- :先减1,后参与运算
后置-- :先参与运算,后减1

4.3 逻辑运算符(布尔运算符) *

&& 与 左边为真则取右边,左边为假则取左边
|| 或 左边为真则取左边,左边为假则边右边
!  非  取反
var a = 1;
var b = 2;
var c = 0;

console.log(a || b); //1
console.log(b || a); //2
console.log(c && a); //0
console.log(a || c && b); //1

// JS逻辑运算中的逻辑或和逻辑与的运算结果:
// 决定整个表达式的子表达式的值

4.4 关系运算符(比较运算符)

<  >  >=  <= == != === !==
=====的区别:==只进行值得比较,===类型和值同时相等,则相等

var result = '55' == 55;  	// true
var result = '55' === 55; 	// false 值相等,类型不相等
var result = 55 === 55; 	// true

4.5 赋值运算符

注意与数学符号的差别;

= += -= *= /= %=

例如:
var num = 0;
num += 5;	//相当于  num = num + 5;

4.6 运算符的优先级 *

优先级从高到底
1. ()  优先级最高
2. 一元运算符  ++   --   !
3. 算数运算符  先*  /  %   后 +   -
4. 关系运算符  >   >=   <   <=
5. 相等运算符   ==   !=    ===    !==
6. 逻辑运算符 先&&   后||
7. 赋值运算符
// 练习1:
var s = 4 >= 6 || '人' != '阿凡达' && !(12 * 2 == 144) && true
console.log(s); //true

// 练习2:
var num = 10;
var f = 5 == num / 2 && (2 + 2 * num)
console.log(f.toString() === 22) //false

总结:

操作符的使用,基本数学运算,一元运算符自增自减及前置后置的区别,逻辑运算符及取值,关系比较运算符,赋值运算符,运算符优先级;

第5章 流程控制

程序的三种基本结构

顺序结构: 从上到下执行的代码就是顺序结构

程序默认就是由上到下顺序执行的;

分支结构:根据不同的情况及判断,执行对应代码;

循环结构:重复执行一段代码;

5.1 分支结构

if语句

语法结构

if (/* 条件表达式 */) {
  // 执行语句
}

if (/* 条件表达式 */){
  // 成立执行语句
} else {
  // 否则执行语句
}

if (/* 条件1 */){
  // 成立执行语句
} else if (/* 条件2 */){
  // 成立执行语句
} else if (/* 条件3 */){
  // 成立执行语句
} else {
  // 最后默认执行语句
}

案例

//获取两个数字中的最大值
var num1=100;
var num2=20;
if(num1>num2){
    console.log(num1);
}else{
    console.log(num2);
}
// 判断一个数是偶数还是奇数
var n = 10;
if(n%2==0){
    console.log('偶数');
}else{
    console.log('奇数');
}
    /*
    * 例子:
    * 获取考试的分数,如果成绩是在90(含)分以上的,则显示级别:A
    * 如果成绩是大于等于80的则:B
    * 如果成绩是大于等于70的则:C
    * 如果成绩是大于等于60的则:D
    * 如果成绩是小于60的则:E
    *
    * */


    var score = 91;
    if (score >= 90) {
        console.log("A");
    } else if (score >= 80) {
        console.log("B");
    } else if (score >= 70) {
        console.log("C");
    } else if (score >= 60) {
        console.log("D");
    } else {
        console.log("E");
    }

作业:判断一个年份是闰年还是平年

闰年:能被4整除,但不能被100整除的年份 或者 能被400整除的年份

var n = 2016;
if(n%4==0){
    if(n%100 !=0){
        console.log('闰年');
    }else if(n%400 ==0){
        console.log('闰年');
    }else{
        console.log('平年');
    }
}else{
    console.log('平年');
}
三元运算符
表达式1 ? 表达式2 : 表达式3
是对if……else语句的一种简化写法

案例:

// 是否年满18岁
var age = 18;
var s = age>=18?'Yes':'no';
console.log(s);
// 从两个数中找最大值
var a1 = 110;
var a2 = 19;
var s = a1>a2?a1:a2;
console.log(s);
switch语句

语法格式:

switch (expression) {
  case 常量1:
    语句;
    break;
  case 常量2:
    语句;
    break;case 常量n:
    语句;
    break;
  default:
    语句;
    break;
}

/*
* 执行过程:
* 获取表达式的值,和值1比较,相同则执行代码1,遇到break跳出整个语句,结束
* 如果和值1不匹配,则和值2比较,相同则执行代码2,遇到break跳出整个语句,结束
* 如果和值2不匹配,则和值3比较,相同则执行代码3,遇到break跳出整个语句,结束
* 如果和值3不匹配,则和值4比较,相同则执行代码4,遇到break跳出整个语句,结束
* 如果和之前的所有的值都不一样,则直接执行代码5,结束
*/
break可以省略,如果省略,代码会继续执行下一个case
switch 语句在比较值时使用的是全等操作符, 因此不会发生类型转换(例如,字符串'10' 不等于数值 10)
/* *
* 判断这个人的成绩的级别:
* 如果是A,则提示,分数在90分以上
* 如果是B,则提示,分数在80分以上
* 如果是C,则提示,分数在70分以上
* 如果是D,则提示,分数在60分以上
* 否则提示,不及格
* */

var jiBie="B";
switch (jiBie){
    case "A" : 
        console.log("分数在90分以上的");
        break;
    case "B" : 
        console.log("分数在80分以上的");
        break;
    case "C" : 
        console.log("分数在70分以上的");
        break;
    case "D" : 
        console.log("分数在60分以上的");
        break;
    default :
        console.log("不及格");
}

5.2 循环结构

在JS语言中,循环语句有三种,while、do…while、for循环。

5.2.1 while语句

基本语法:

// 当循环条件为true时,执行循环体,
// 当循环条件为false时,结束循环。
while (循环条件) {
  //循环体
}

案例1:计算1-100之间所有数的和

// 初始化变量
var i = 1;
var sum = 0;
// 判断条件
while (i <= 100) {
  // 循环体
  sum += i;
  // 自增
  i++;
}
console.log(sum);

案例2:打印100以内 7的倍数

var i = 1;
while(i<100){
    if(i%7==0){
        console.log(i);
    }
    i++;
}

案例3:打印100以内所有偶数

var i = 1;
while(i<=100){
    if(i%2==0){
        console.log(i);
    }
    i++;
}

案例4:打印100以内所有偶数的和

var i = 1;
var s = 0;
while(i<=100){
    if(i%2==0){
        s = s+i;
    }
    i++;
}
console.log(s);

作业:
打印100以内的奇数
打印100以内的奇数的和

5.2.2 do…while语句

do…while循环和while循环非常像,二者经常可以相互替代,

但是do…while的特点是不管条件成不成立,都会执行一次。

do {
  // 循环体;
} while (循环条件);

案例:计算1+2+3+4+……+99+100 的结果

// 初始化变量
var i = 0;
var sum = 1;
do {
  sum += i;//循环体
  i++;//自增
} while (i <= 100);//循环条件
5.2.3 for语句

while和do…while一般用来解决无法确认次数的循环。for循环一般在循环次数确定的时候比较方便

for循环语法:

// for循环的表达式之间用的是;号分隔的,千万不要写成,
for (初始化表达式1; 判断表达式2; 自增表达式3) {
  // 循环体4
}

执行顺序:1243 ---- 243 -----243(直到循环条件变成false)

  1. 初始化表达式
  2. 判断表达式
  3. 自增表达式
  4. 循环体
//打印1-100之间所有数
for(var i=1;i<=100;i++){
    console.log(i);
}

//求1-100之间所有数的和
var s = 0;
for(var i=0;i<=100;i++){
    s+=i;
}
console.log(s);

//求1-100之间所有偶数的和
var s = 0;
for(var i=1;i<=100;i++){
    if(i%2==0){
        s+=i;
    }
}
console.log(s);

//打印正方形
var start = '';
for (var i = 0; i < 10; i++) {
  for (var j = 0; j < 10; j++) {
    start += '* ';
  }
  start += '\n';
}
console.log(start);

//打印直角三角形
var start = '';
for (var i = 0; i < 10; i++) {
  for (var j = i; j < 10; j++) {
    start += '* ';
  }
  start += '\n';
}
console.log(start);

//打印9*9乘法表
var str = '';
for (var i = 1; i <= 9; i++) {
  for (var j = i; j <=9; j++) {
    str += i + ' * ' + j + ' = ' + i * j + '\t';
  }
  str += '\n';
}
console.log(str);
5.2.4 continue和break

break:立即跳出整个循环,即循环结束,开始执行循环后面的内容(直接跳到大括号)

continue:立即跳出当前循环,继续下一次循环(跳到i++的地方)

案例1:求1-100之间不能被7整除的整数的和(用continue)

var s = 0;
for(var i=0;i<100;i++){
    if(i%7==0){
        continue;
    }
    s+=i;
}
console.log(s);

案例2:求200-300之间所有的奇数的和(用continue)

var s = 0;
for(var i=200;i<=300;i++){
    if(i%2==0){
        continue;
    }else{
        s+=i;
    }
}
console.log(s);

案例3:求200-300之间第一个能被7整数的数(break)

for(var i=200;i<=300;i++){
    if(i%7==0){
        console.log(i);
        break;
    }
}

总结:

代码的执行流程分为顺序、分支和循环三种结构,顺序结构是默认的,判断结构主要有if-else和switch-case两种,循环结构有while、do-while、for三种,其中continue和break是跳出循环;

第6章 JS中特殊的对象-数组

之前学习的数据类型,只能存储一个值(比如:Number/String)。我们想在一个变量中存储多个值,应该如何存储?

所谓数组,就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中,那么这个集合我们就称之为数组。

6.1 数组的创建

// 字面量方式创建数组
var arr1 = []; //空数组
// 创建一个包含3个数值的数组,多个数组项以逗号隔开
var arr2 = [1, 3, 4]; 
// 创建一个包含2个字符串的数组
var arr3 = ['a', 'c']; 
console.log(arr1);
console.log(arr2);
console.log(arr3);

// 可以通过数组的length属性获取数组的长度
console.log(arr3.length);
// 可以设置length属性改变数组中元素的个数
arr3.length = 0;

console.log(arr3[0]);//undefined

数组的元素可以是任意类型的数据,因此,有时数组中的某个元素的值又是一个数组,而这样的数组被称为多维数组,如果数组中只有其他类型的数据,而没有另外的数组值,这样的数组被称为一维数组;

通常,数组被嵌套N层,则称为N维数组,最常见的就是二维数组、三维数组、四维数组,超过一维的数组都会被泛称为多维数组;

数组的维度值越大,复杂度就越高,开发中尽量避免产生高维度值的数组;

var arr1 = [a,b,c]; // 一维数组
var arr2 = [a,b,c,[d,e]]; // 二维数组
var arr3 = [a,b,c,[d,e,[f,g]]]; // 三维数组
var arr4 = [a,b,c,[d,e,[f,g,[h,t,y]]]]; // 四维数组

6.2 获取数组元素

// 格式:数组名[下标]	下标又称索引
// 下标从0开始
// 功能:获取数组对应下标的那个值,如果下标不存在,则返回undefined。
var arr = ['red',, 'green'];
arr[0];	// red
arr[1];	// undefined 下标位置没有数据
arr[2]; // green
arr[5]; // 这个数组的最大下标为2,因此返回undefined
// 获取多维数组的数据
var arr = ['路飞','娜美',['巴基','女帝',['佐助','乔巴']]];
console.log(arr[2][2][0]); //佐助

6.3 遍历数组

遍历:遍及所有,对数组的每一个元素都访问一次就叫遍历。

for循环数组遍历的基本语法:

for(var i = 0; i < arr.length; i++) {
	// 数组遍历的固定结构
}

for循环示例:

var arr1 = [1, 3, 4]; 

for(var i = 0;i<arr1.length;i++){
    console.log(arr1[i]);
}

whil循环示例:

var arr1 = [1, 3, 4]; 

var i = 0;
while(i<arr1.length){
    console.log(arr1[i]);
    i++;
}

6.4 为数组修改添加元素

// 格式:数组名[下标/索引] = 值;
// 如果下标有对应的值,会把原来的值覆盖,如果下标不存在,会给数组新增一个元素。
var arr = ["red", "green", "blue"];
// 把red替换成了yellow
arr[0] = "yellow";
// 给数组新增加了一个pink的值
arr[3] = "pink";

6.5 数组操作案例

案例1:求数组中的所有数的和

//求和
var arr = [10, 20, 30, 40, 50];
//定义变量存储和
var sum = 0;
for (var i = 0; i < arr.length; i++) {
    sum += arr[i];
}
console.log("和为:" + sum);

案例2:获取数组中的最大值

//最大值
var arr = [10, 20, 30, 40, 50, 60];
//假设这个变量中的值是最大的
var maxNum = arr[0];
//遍历数组
for (var i = 0; i < arr.length; i++) {
    //判断
    if (maxNum < arr[i]) {
        maxNum = arr[i];
    }
}
console.log("最大值是:" + maxNum);

案例3: 遍历出数组中所有的偶数

// 遍历出数组中所有的偶数
var arr = [1,2,3,4,5,6,7];
for(var i=0;i<arr.length;i++){
    //判断
    if(arr[i]%2==0){
        console.log(arr[i]);
    }
}

案例4:将数组转为字符串并以 | 分割

//把数组中的每个名字后面拼接一个|然后以字符串的方式输出
var names = ["卡卡西", "佐助", "凤姐", "鸣人", "黑崎一护"];
var str = "";//空的字符串,用来存储最后的拼接的结果的字符串
//不停的遍历数组的数据,并且拼接字符串
for (var i = 0; i < names.length - 1; i++) {
    str += names[i] + "|";//拼接字符串的方式
}
str += names[names.length - 1];
console.log(str);

总结:

数组就是多个数据的集合,有一维数组和多维数组之分,可以使用字面量方式创建数组,使用下标来获取数组元素数据,使用for或者while循环来遍历数组元素;

第7章 函数

把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数,起个名字(函数名),在后续开发中可以反复调用

函数的作用就是封装一段代码,将来可以重复使用

7.1 函数的声明及调用

7.1.1 声明
  • 关键字声明
function 函数名(){
  // 函数体
}
  • 表达式声明
var fn = function() {
  // 函数体
}
  • 特点:

    函数声明的时候,函数体并不会执行,只要当函数被调用的时候才会执行。
    一个函数一般都特定的用来干 一件 事情

7.1.2 调用
  • 调用函数的语法:
函数名();
  • 特点:

    函数体只有在调用的时候才会执行,调用需要()进行调用。
    可以调用多次(重复使用)

// 声明函数
function sayHi() {
  console.log("吃了没?");
}
// 调用函数
sayHi();

// 求1-100之间所有数的和
function getSum() {
  var sum = 0;
  for (var  i = 0; i < 100; i++) {
    sum += i;
  }
  console.log(sum);
}
// 一段代码可以多次调用
getSum();
getSum();
getSum();

7.2 参数

  • 为什么要有参数
function getSum() {
  var sum = 0;
  for (var i = 1; i <= 100; i++) {
    sum += i;
  }
  console.log();
}

// 虽然上面代码可以重复调用,但是只能计算1-100之间的值
// 如果想要计算n-m之间所有数的和,应该怎么办呢?
  • 语法:
// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部
// 带参数的函数声明
function 函数名(形参1, 形参2, 形参...){
  // 函数体
}

// 带参数的函数调用
函数名(实参1, 实参2, 实参3);
  • *形参和实参 **
  1. 形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。
  2. 实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。
function fn(a, b) {
  console.log(a + b);
}
var x = 5, y = 6;
fn(x,y); 
// x,y实参,有具体的值。
// 函数执行的时候会把x,y复制一份给函数内部的a和b,
// 函数内部的值是复制的新值,无法修改外部的x,y

JS 函数在调用时,允许传多个实参,就是实参个数可以比形参个数多;

7.3 函数的返回值

当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个值

返回值语法:

//声明一个带返回值的函数
function 函数名(形参1, 形参2, 形参...){
  //函数体
  return 返回值;
}

//可以通过变量来接收这个返回值
var 变量 = 函数名(实参1, 实参2, 实参3);

*返回值详解: **
如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined
如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值
如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined
函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。

7.4 函数相关的其它事情

7.4.1 匿名函数与自调用函数

匿名函数:没有名字的函数

匿名函数如何使用:

将匿名函数赋值给一个变量,这样就可以通过变量进行调用

var fun1 = function(){
    console.log(1);
}
fun1();

匿名函数如果没有任何变量来表示它,那么就不能直接调用来执行,因此可以通过匿名函数的自调用的方式来执行

(function () {
  alert(123);
})();

关于自执行函数(匿名函数自调用)的作用:防止全局变量污染。

7.4.2 函数本身也是值 *
function fn() {}
console.log(typeof fn);
  • 函数作为参数

因为函数也是一种值类型,可以把函数作为另一个函数的参数,在另一个函数中调用

function f1(f){
    f();
}
function f2(){
    console.log(2);
}
f1(f2); //2
  • 函数做为返回值

因为函数是一种类型,所以可以把函数可以作为返回值从函数内部返回。

function fn(b) {
  var a = 10;
  return function () {
    alert(a+b);
  }
}
var f = fn(5);
f();  //15

总结:

函数是一段代码的封装,可重复多次运行,函数的声明有表达式声明和关键字声明,使用 ·函数名()· 的方式进行调用,调用时传入的参数为实参,声明时的参数时形参,函数使用return返回值,函数可以是没有名字的匿名函数,函数本身也可以当做值使用;

第8章 作用域与JS代码的运行 *

作用域:变量可以起作用的范围和区域

8.1 全局变量和局部变量 *

  • 全局变量与全局作用域

    在任何地方都可以访问到的变量就是全局变量,全局变量所在的区域就是全局作用域

  • 局部变量

    只在固定的代码片段内可访问到的变量,最常见的例如函数内部的变量,就是局部变量。局部变量所在的区域就是局部作用域(函数作用域)

不使用var声明的变量是全局变量,不推荐使用。
变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁

8.2 变量提升

console.log(a); // undefined
var a = 2;
console.log(a); //   a is not defined
  • 变量提升

    定义变量的时候,变量的声明会被提升到作用域的最上面,变量的赋值不会提升。

  • 函数提升

    JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面

f();
function f(){
    console.log(12); //12
}
var f = 1;
function f(){
    console.log(12); //12
}
// 由于函数提升在前,所以被变量声明替换了;
// 执行阶段,变量被复制为1,不再是一个函数,
f(); // f is not a function

注:不管是普通变量还是函数,尽量不要出现重名;

8.3 JS代码的运行 *

console.log(s); //undefined
var s = 2;

JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。

JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析(编译)过程和代码执行过程

预解析过程:

  1. 语法检查,如果有错误,直接停止后续步骤不再运行。

  2. 把变量和函数的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值和调用。

  3. 先提升变量后提升函数,如果函数和变量同名,则被替换;

代码执行过程

变量的赋值,函数的调用,循环判断等,根据代码由上往下顺序执行;

var a = 25;
function abc (){
  alert(a);//undefined
  var a = 10;
}
abc();


// 如果变量和函数同名的话,函数优先做提升
console.log(a);
function a() {
  console.log('aaaaa');
}
var a = 1;
console.log(a);

// 1、----------------
var num = 10;
fun();
function fun() {
    console.log(num); //undefined
    var num = 20;
}

// 2、----------------
var a = 18;
f1();
function f1() {
    var b = 9;
    console.log(a); //undefined
    console.log(b); // 9
    var a = '123'; 
}

8.4 词法作用域

变量的作用域是在定义时决定而不是执行时决定的,也就是说词法作用域取决于编译阶段,通过静态分析就能确定,因此词法作用域也叫做静态作用域。

在 js 中词法作用域规则:

  • 函数允许访问函数外的数据.
  • 整个代码结构中只有函数可以限定作用域.
  • 作用域规则首先使用提升规则分析
  • 如果当前作用规则中有名字了, 就不考虑外面的名字
var num = 123;
function foo() {
  console.log( num );
}
foo();

if ( false ) {
    var num = 123;
}
console.log( num ); // undefiend

也就是说:

函数内部可以访问函数外部的变量,但是函数外部不可以访问函数内部的变量;

函数内部如果有变量,则优先使用内部的变量,如果函数内部没有,才会使用函数外部的变量;

8.5 作用域链 *

只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。

将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。
var a = 1;
function fn1(){
    function fn2(){
        function fn3(){
            console.log(a);
        }
        fn3();
    }
    fn2();
}
fn1();

var a = 1;
function fn1(){
    var a = 2;
    function fn2(){
        var a = 3;
        function fn3(){
            console.log(a);
        }
        fn3();
    }
    fn2();
}
fn1();

总结:

函数内部是JS代码的局部作用域,函数外部是全局作用域,JS 代码的运行分为与解析阶段和执行阶段,变量的声明实在与解析阶段的,所以变量存在提升,而变量只在自己的作用域中起作用,但是自己作用域是可以访问上级作用域的;

第9章 对象(Object)

9.1 什么是对象

万物皆对象

现实生活中:万物皆对象,对象是一个具体的事物,一个具体的事物就会有行为和特征。
举例: 一部车,一个手机
车是一类事物,门口停的那辆车才是对象
	特征:红色、四个轮子
	行为:驾驶、刹车

9.2 JavaScript中的对象

JavaScript中的对象其实就是生活中对象的一个抽象
JavaScript的对象是无序属性的集合。
其属性可以包含基本值、对象、数组或函数。
对象就是一组没有顺序的值。
我们可以把JavaScript中的对象想象成键值对,其中值可以是数据和函数。
对象的行为和特征
	特征---属性
	行为---方法

事物的特征在对象中用属性来表示。

事物的行为在对象中用方法来表示。

属性和方法统称为对象的成员。

9.3 如何得到一个对象

  • 字面量方式创建对象

    var obj1 = {};//得到一个空对象
    var obj2 = {name:'张三',age:18};//得到拥有两个属性的对象
    //得到拥有两个属性和一个方法的对象
    var obj3 = {
        name:'张三',
        age:18,
        fei:function(){
            console.log('你上天啊!');
        }
    }
    
  • new Object() 创建对象 (内置构造函数)

    var person = new Object();
    
    person.name = 'lisi';
    person.age = 35;
    person.sayHi = function(){
        console.log('Hello,everyBody');
    }
    
  • 自定义构造函数创建对象

    function Person(name,age,job){
          this.name = name;
          this.age = age;
          this.job = job;
          this.sayHi = function(){
            console.log('Hello,everyBody');
          }
    }
    var p1 = new Person('张三', 22, 'actor');
    

new关键字和构造函数

构造函数 ,是一种特殊的函数,又叫做函数构造器。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。

  1. 构造函数用于创建一类对象,首字母通常大写。

  2. 构造函数要和new一起使用才有意义。

  3. new 关键字也读做实例化。实例化构造函数,得到一个对象。

9.4 this 的指向

JavaScript中的this指向问题,比较复杂,有时候会让人难以捉摸,随着学习的深入,我们会不断接触this,

在学习过程中,我们可以不断总结,最终搞清楚this在何种情况下指向何处……

目前,我们只需要记住以下两点就可以了:

1: 函数如果在某个对象下,this就指向这个对象

2: 函数如果被直接调用,this指向window对象

var o1 = {
    name: '山治',
    f: function () {
        console.log(this.name);
    }
}
o1.f(); // 山治
function f(){
    console.log(this);
}
f();
console.log(window);

9.5 对象的使用

  • 方法及属性的使用
对象.方法名()//调用对象的方法
对象.属性;   //获取对象的属性
  • 遍历对象的属性

通过for…in语法可以遍历一个对象

var obj1 = {
    name:'路飞',
    age : 17,
    sex : '男',
}

for(var k in obj1){
    console.log(k);
    console.log(obj1[k]);
}

注意:使用for …in语法,同样可以遍历数组

注意:如果属性名或方法名,是一个变量,则使用对象[变量名] 语法

  • 删除对象的属性
var obj1 = {
    name:'路飞',
    age : 17,
    sex : '男',
}

console.log(obj1.age); //17
delete obj1.age;  //删除对象中指定的属性
console.log(obj1.age); // undefined

总结:

创建对象有三种方式,字面量、new内置构造函数及自定义构造函数;对象中有属性及方法,this指向当前对象,使用 . (点) 语法调用属性及方法;

第10章 标准库对象(内置对象)

JavaScript 提供了很多个内置对象:Math/Array/Number/String/Boolean…

对象只是带有属性方法的特殊数据类型。

我们在学习时其实就是要记住对象的每个属性和方法怎么使用,代表什么含义;

技术问题,遇到分歧,去哪里查找资料:

火狐开发者社区–MDN

微软开发者社区–MSDN

10.1 Math对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math

10.1.1 常用属性和方法

Math 是一个内置对象, 它具有数学常数和函数的属性和方法。不是一个函数对象。

与其它全局对象不同的是, Math 不是一个构造函数.  Math 的所有属性和方法都是静态的.

跟数学相关的运算直接使用Math中的成员即可

console.log(Math.PI); //圆周率3.141592653589793
Math.random();//介于 0 和 1 之间的伪随机数。

Math.ceil(6.6);//获取大于或等于提供数值的最小整数--向上取整
Math.floor(8.8);//获取小于或等于提供数值的最大整数--向下取整
Math.round(9.9);//四舍五入
Math.max(10,20,15);//取多个值中的最大值
Math.min(10,20,15);//取多个值中的最小值
Math.pow(10,2);//返回x的y次幂
Math.sqrt(100);//求平方根

10.1.2 案例
  • 求10-20之间的随机数

    Math.floor(Math.random() * (max - min)) + min; 
    
10.1.3 属性方法对照表

Math对象的属性(常量)

属性(常量)描述
Math.E 常量数学常数 e。这是欧拉数,自然对数的底。
Math.LN2 常量2 的自然对数。
Math.LN10 常量10 的自然对数。
Math.LOG2E 常量以 2 为底 e 的对数。
Math.LOG10E 常量以 10 为底 e 的对数。
Math.PI 常量Pi。这是圆的周长与直径的比值。
Math.SQRT1_2 常量0.5 的平方根,或相当于 1 除以 2 的平方根。
Math.SQRT2 常量2 的平方根。

Math对象的方法(函数)

方法(函数)描述
Math.abs 函数返回数字的绝对值。
Math.acos 函数返回数字的反余弦值。
Math.acosh 函数返回数字的双曲反余弦值(或反双曲余弦值)。
Math.asin 函数返回数字的反正弦值。
Math.asinh 函数返回数字的反双曲正弦。
Math.atan 函数返回数字的反正切值。
Math.atan2 函数将与 X 轴的角度(以弧度为单位)返回到由 y 和 x 坐标表示的点。
Math.atanh 函数返回数字的反双曲正切。
Math.ceil 函数返回大于或等于提供的数值表达式的最小整数。
Math.cos 函数返回数字的余弦值。
Math.cosh 函数返回数字的双曲余弦。
Math.exp 函数返回 e(自然对数的底)的乘幂数。
Math.expm1 函数返回 e(自然对数的底)的乘幂数减去 1 的结果。
Math.floor 函数返回小于或等于提供的数值表达式的最大整数。
Math.hypot 函数返回参数平方和的平方根。
Math.imul 函数返回被视为 32 位带符号整数的两个数字的积。
Math.log 函数返回数字的自然对数。
Math.log1p 函数返回 1 加上一个数字的的自然对数。
Math.log10 函数返回数字以 10 为底的对数。
Math.log2 函数返回数字以 2 为底的对数。
Math.max 函数返回提供的两个数值表达式中的较大值。
Math.min 函数返回提供的两个数字中的较小值。
Math.pow 函数返回基表达式的指定乘幂数的值。
Math.random 函数返回介于 0 和 1 之间的伪随机数。
Math.round 函数返回舍入到最近整数的指定数值表达式。
Math.sign 函数返回数字符号,它指示数字为正数、负数还是 0。
Math.sin 函数返回数字的正弦值。
Math.sinh 函数返回数字的反双曲正弦。
Math.sqrt 函数返回数字的平方根。
Math.tan 函数返回数字的正切值。
Math.tanh 函数返回数字的双曲正切。
Math.trunc 函数返回数字的整数部分,删除任何小数数字。

10.2 Date对象(构造函数)

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date

10.2.1 常用属性和方法

创建 Date 实例用来处理日期和时间。Date 对象基于1970年1月1日(世界标准时间)起的毫秒数。

// 获取当前时间,UTC世界时间,距1970年1月1日(世界标准时间)起的毫秒数
var now = new Date();
console.log(now.getTime());	// 获取距1970年1月1日(世界标准时间)起的毫秒数
console.log(now.valueOf());	// valueOf用于获取对象的原始值,与getTime()方法相同

Date构造函数的参数
1. 毫秒数 1498099000356		new Date(1498099000356)
2. 日期格式字符串  '2015-5-1'	 new Date('2015-5-1')
3. 年、月、日……				  new Date(2015, 4, 1)   // 月份从0开始
  • 获取日期的毫秒形式
var now = new Date();
console.log(now.getTime());
// valueOf用于获取对象的原始值
console.log(now.valueOf());

// HTML5中提供的方法,有兼容性问题
var now = Date.now();

// 不支持HTML5的浏览器,可以用下面这种方式
var now = + new Date();	// 隐式调用 Date对象的valueOf() 
  • 日期格式化方法
toString()		// 转换成字符串
valueOf()		// 获取毫秒值
// 下面格式化日期的方法,在不同浏览器可能表现不一致,一般不用
toDateString()
toTimeString()
toLocaleDateString()
toLocaleTimeString()
  • 获取日期指定部分
getTime()  	  // 返回毫秒数和valueOf()结果一样,valueOf()内部调用的getTime()
getSeconds()  // 返回0-59
getMinutes()  // 返回0-59
getHours()    // 返回0-23
getDay()      // 返回星期几 0周日   6周6
getDate()     // 返回当前月的第几天
getMonth()    // 返回月份,***从0开始***
getFullYear() //返回4位的年份  如 2016
10.2.2 案例
  • 案例1:写一个函数,格式化日期对象,返回yyyy-MM-dd HH:mm:ss的形式
function formatDate(d) {
  //如果date不是日期对象,返回
  if (!date instanceof Date) {
    return;
  }
  var year = d.getFullYear(),
      month = d.getMonth() + 1, 
      date = d.getDate(), 
      hour = d.getHours(), 
      minute = d.getMinutes(), 
      second = d.getSeconds();
  month = month < 10 ? '0' + month : month;
  date = date < 10 ? '0' + date : date;
  hour = hour < 10 ? '0' + hour : hour;
  minute = minute < 10 ? '0' + minute:minute;
  second = second < 10 ? '0' + second:second;
  return year + '-' + month + '-' + date + ' ' + hour + ':' + minute + ':' + second;
}
  • 计算时间差,返回相差的天/时/分/秒
function getInterval(start, end) {
  var day, hour, minute, second, interval;
  interval = end - start;
  interval /= 1000;
  day = Math.round(interval / 60 /60 / 24);
  hour = Math.round(interval / 60 /60 % 24);
  minute = Math.round(interval / 60 % 60);
  second = Math.round(interval % 60);
  return {
    day: day,
    hour: hour,
    minute: minute,
    second: second
  }
}
10.2.3 方法对照表

Date对象 的方法。

方法描述
getDate 方法使用当地时间返回一个月某天的值。
getDay 方法使用当地时间返回一个星期某天的值。
getFullYear 方法使用当地时间返回年份值。
getHours 方法使用当地时间返回小时值。
getMilliseconds 方法使用当地时间返回毫秒值。
getMinutes 方法使用当地时间返回分钟值。
getMonth 方法使用当地时间返回月份值。
getSeconds 方法使用当地时间返回秒值。
getTime 方法Date 对象中的时间值返回为自 1970 年 1 月 1 日午夜起经过的毫秒数。
getTimezoneOffset 方法返回主机的时间与协调通用时间 (UTC) 之间的分钟差值。
getUTCDate 方法使用 UTC 返回一个月某天的值。
getUTCDay 方法使用 UTC 返回一个星期某天的值。
getUTCFullYear 方法使用 UTC 返回年份值。
getUTCHours 方法使用 UTC 返回小时值。
getUTCMilliseconds 方法使用 UTC 返回毫秒值。
getUTCMinutes 方法使用 UTC 返回分钟值。
getUTCMonth 方法使用 UTC 返回月份值。
getUTCSeconds 方法使用 UTC 返回秒值。
getVarDate 方法Date 对象中的 VT_DATE 值返回。
getYear 方法返回年份值。
hasOwnProperty 方法返回一个布尔值,该值指示一个对象是否具有指定名称的属性。
isPrototypeOf 方法返回一个布尔值,该值指示对象是否存在于另一个对象的原型链中。
propertyIsEnumerable 方法返回一个布尔值,该值指示指定属性是否为对象的一部分以及该属性是否是可枚举的。
setDate 方法使用当地时间设置一个月中某一日的数值。
setFullYear 方法使用当地时间设置年份值。
setHours 方法使用当地时间设置小时值。
setMilliseconds 方法使用当地时间设置毫秒值。
setMinutes 方法使用当地时间设置分钟值。
setMonth 方法使用当地时间设置月份值。
setSeconds 方法使用当地时间设置秒值。
setTime 方法设置 Date 对象中的日期和时间值。
setUTCDate 方法使用 UTC 设置一个月中某一日的数值。
setUTCFullYear 方法使用 UTC 设置年份值。
setUTCHours 方法使用 UTC 设置小时值。
setUTCMilliseconds 方法使用 UTC 设置毫秒值。
setUTCMinutes 方法使用 UTC 设置分钟值。
setUTCMonth 方法使用 UTC 设置月份值。
setUTCSeconds 方法使用 UTC 设置秒值。
setYear 方法使用当地时间设置年份值。
toDateString 方法以字符串值的形式返回一个日期。
toGMTString 方法返回使用格林尼治标准时间 (GMT) 转换为字符串的日期。
toISOString 方法以字符串值的形式返回采用 ISO 格式的日期。
toJSON 方法用于在 JSON 序列化之前转换目标类型的数据。
toLocaleDateString 方法将一个日期以字符串值的形式返回,该字符串应适合于宿主环境的当前区域设置。
toLocaleString 方法返回使用当前区域设置转换为字符串的对象。
toLocaleTimeString 方法以字符串值的形式返回一个时间,此字符串值应与宿主环境的当前区域设置相适应。
toString 方法返回表示对象的字符串。
toTimeString 方法以字符串值形式返回时间。
toUTCString 方法返回使用 UTC 转换为字符串的日期。
valueOf 方法返回指定对象的原始值。

10.3 Array对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array

10.3.1 常用属性和方法

length属性: 返回数组的成员数量。

var arr = ['a', 'b'];
console.log(arr.length) // 2

常用方法

  • push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

    var a = [];
    a.push(1) // 1
    a.push('a') // 2
    a.push(true, {}) // 4
    console.log(a); //[1, 'a', true, {}]
    
  • pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组

    var a = ['a', 'b', 'c'];
    a.pop() // 'c'
    console.log(a);// ['a', 'b']
    
  • slice方法用于提取原数组的一部分,返回一个新数组,原数组不变。

    它的第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。 如果省略第二个参数,则一直返回到原数组的最后一个成员。

    var a = ['a', 'b', 'c'];
    a.pop() // 'c'
    console.log(a);// ['a', 'b']
    
  • join方法用于将数组元素以指定字符拼接为字符串,返回一个字符串,原数组不变。

    var a = ['a','b','c','d','e'];
    console.log(a.join('-')) // 'a-b-c-d-e'
    
  • 返回数组的字符串表示形式。

    var arr = [1, 2, 3, 4];
    console.log(arr.toString()); //1,2,3,4
    
10.3.2 方法和属性对照表

Array 对象的属性。

属性描述
length 属性返回一个整数值,此整数比数组中所定义的最高位元素大 1,是实际元素个数。

Array 对象的方法。

方法描述
concat 方法(数组)返回由两个数组组合而成的新数组。
entries 方法返回包含数组的键/值对的迭代器。
every 方法检查定义的回调函数是否为数组中的所有元素返回 true
fill 方法使用指定值填充数组。
filter 方法对数组的每个元素调用定义的回调函数,并返回回调函数为其返回 true 的值的数组。
findIndex 方法返回满足回调函数中指定的测试条件的第一个数组元素的索引值。
forEach 方法为数组中的每个元素调用定义的回调函数。
hasOwnProperty 方法返回一个布尔值,该值指示某个对象是否具有指定名称的属性。
indexOf 方法(数组)返回某个值在数组中的第一个匹配项的索引。
isPrototypeOf 方法返回一个布尔值,该值指示某个对象是否存在于另一个对象的原型链中。
join 方法返回由一个数组的所有元素串联而成的 String 对象。
keys 方法返回包含数组的索引值的迭代器。
lastIndexOf 方法(数组)返回指定值在数组中的最后一个匹配项的索引。
map 方法对数组的每个元素调用定义的回调函数并返回包含结果的数组。
pop 方法从数组中移除最后一个元素并将该元素返回。
propertyIsEnumerable 方法返回一个布尔值,该值指示指定属性是否为对象的一部分且是否可枚举。
push 方法将新元素追加到一个数组中,并返回数组的新长度。
reduce 方法通过对数组中的所有元素调用定义的回调函数来累积单个结果。 回调函数的返回值是累积的结果,并且作为对回调函数的下一个调用中的参数提供。
reduceRight 方法通过对数组中的所有元素调用定义的回调函数来按降序顺序累积单个结果。 回调函数的返回值是累积的结果,并且作为对回调函数的下一个调用中的参数提供。
reverse 方法将元素顺序被反转的 Array 对象返回。
shift 方法从数组中移除第一个元素并将返回该元素。
slice 方法(数组)返回一个数组中的一部分。
some 方法检查定义的回调函数是否为数组的任何元素返回 true
sort 方法返回一个元素已经进行了排序的 Array 对象。
splice 方法从一个数组中移除元素,如有必要,在所移除元素的位置上插入新元素,并返回所移除的元素。
toLocaleString 方法返回使用当前区域设置的字符串。
toString 方法返回数组的字符串表示形式。
unshift 方法在数组的开头插入新元素。
valueOf 方法获取对数组的引用。
values 方法返回包含数组的值的迭代器。

10.4 String对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String

10.4.1 常用属性和方法
var s = 'JavaScript';
// length属性返回字符串的长度。
var i = s.length; //返回参数在字符串中第一次出现的位置
var i = s.indexOf('b'); //从原字符串取出子字符串并返回,不改变原字符串
var i = s.substr(2,4);//从下标第二个开始截取4个长度的字符串
//toLowerCase方法用于将一个字符串全部转为小写 
//toUpperCase则是全部转为大写
var i = s.toLowerCase();
var i = s.toUpperCase();

// 用于替换匹配的子字符串,只替换第一个匹配 
var i = s.replace('a','b');
console.log(i);
10.4.2 方法和属性对照表

String 对象的属性

属性描述
constructor 属性指定用于创建对象的函数。
length 属性(字符串)返回 String 对象的长度。
prototype 属性为对象的类返回原型的引用。

String 对象的方法。

方法描述
anchor 方法将具有 NAME 特性的 HTML 定位点放置在文本两侧。
big 方法将 HTML 标记放置在文本两侧。
blink 方法将 HTML 标记放置在文本两侧。
bold 方法将 HTML 标记放置在文本两侧。
charAt 方法返回指定索引处的字符。
charCodeAt 方法返回指定字符的 Unicode 编码。
codePointAt 方法返回一个 Unicode UTF-16 字符的码位。
concat 方法(字符串)返回由提供的两个字符串串联而成的字符串。
EndsWith 方法返回一个布尔值,该值指示字符串或子字符串是否以传入字符串结尾。
includes 方法返回一个布尔值,该值指示传入字符串是否包含在字符串对象中。
fixed 方法将 HTML 标记放置在文本两侧。
fontcolor 方法将具有 COLOR 特性的 HTML 标记放置在文本两侧。
fontsize 方法将具有 SIZE 特性的 HTML 标记放置在文本两侧。
hasOwnProperty 方法返回一个布尔值,该值指示某个对象是否具有指定名称的属性。
indexOf 方法(字符串)返回字符串内第一次出现子字符串的字符位置。
isPrototypeOf 方法返回一个布尔值,该值指示某个对象是否存在于另一个对象的原型链中。
italics 方法将 HTML 标记放置在文本两侧。
lastIndexOf 方法(字符串)返回字符串内子字符串的最后一个匹配项。
link 方法将具有 HREF 特性的 HTML 定位点放置在文本两侧。
localeCompare 方法返回一个值,该值指示两个字符串在当前区域设置中是否相等。
match 方法通过使用提供的正则表达式对象来搜索字符串并以数组形式返回结果。
normalize 方法返回指定字符串的 Unicode 范式。
propertyIsEnumerable 方法返回一个布尔值,该值指示指定属性是否为对象的一部分且是否可枚举。
repeat 方法返回一个新的字符串对象,它的值等于重复了指定次数的原始字符串。
replace 方法使用正则表达式替换字符串中的文本并返回结果。
search 方法返回正则表达式搜索中第一个子字符串匹配项的位置。
slice 方法(字符串)返回字符串的片段。
small 方法将 HTML 标记放置在文本两侧。
split 方法返回一个字符串拆分为若干子字符串时所产生的字符串数组。
StartsWith 方法返回一个布尔值,该值指示字符串或子字符串是否以传入字符串开头。
strike 方法将 HTML 标记放置在文本两侧。
sub 方法将 HTML 标记放置在文本两侧。
substr 方法返回一个从指定位置开始且具有指定长度的子字符串。
substring 方法返回 String 对象中指定位置处的子字符串。
sup 方法将 HTML 标记放置在文本两侧。
toLocaleLowerCase 方法返回一个字符串,其中所有字母字符都转换为小写形式,并将考虑主机环境的当前区域设置。
toLocaleString 方法返回使用当前区域设置转换为字符串的对象。
toLocaleUpperCase 方法返回一个字符串,其中所有字母字符都转换为大写形式,并将考虑主机环境的当前区域设置。
toLowerCase 方法返回一个字符串,其中所有字母字符都转换为小写形式。
toString 方法返回字符串。
toUpperCase 方法返回一个字符串,其中所有字母字符都转换为大写形式。
trim 方法返回已移除前导空格、尾随空格和行终止符的字符串。
valueOf 方法返回字符串。

10.5 包装对象

对象是 JavaScript 语言最主要的数据类型,三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”。

所谓“包装对象”,就是分别与数值、字符串、布尔值相对应的NumberStringBoolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。

var v1 = new Number(123);
var v2 = new String('abc');
var v3 = new Boolean(true);

typeof v1 // "object"
typeof v2 // "object"
typeof v3 // "object"

v1 === 123 // false
v2 === 'abc' // false
v3 === true // false

包装对象的最大目的,首先是使得 JavaScript 的对象涵盖所有的值,其次使得原始类型的值可以方便地调用某些方法。

原始类型的值,可以自动当作对象调用,即调用各种对象的方法和参数。

**这时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,在使用后立刻销毁实例。**

比如,字符串可以调用length属性,返回字符串的长度。

'abc'.length // 3

上面代码中,abc是一个字符串,本身不是对象,不能调用length属性。

JavaScript 引擎自动将其转为包装对象,在这个对象上调用length属性。

调用结束后,这个临时对象就会被销毁。这就叫原始类型与实例对象的自动转换。

注意:JS的内置对象还有很多,我们只不过是学习了比较常用的几个而已;

在后面的学习中,我们还有讲解使用其他类型的内置对象;

可以查看狐火和微软开发者社区,获取更多知识……

课外知识:

JS代码规范&编程风格

  • 缩进:

    空格和tab键都可以,尽量保持一致,使用一种;

    两个空格和四个空格都行,尽量保持一致就行,但是使用4个空格的多一些;

  • 分号:

    尽量不要忘记,每一行的结束都要加分号

    while 与 for 循环后面不要加分号

    if else、switch等分支语句后面不要加分号

    关键字声明函数,后面不要加分号

    表达式声明函数,函数后面加分号

  • 区块:

    两种写法

if(){
    
}

if()
{
    
}

​ 理论上两种都可以,但是尽量使用第一种,因为js会在行尾自动添加分号,有时会出现意外情况;

  • 圆括号

    函数的声明和调用、表达式运算

    1:函数调用时,函数名和括号之间没有空格

    2:函数声明时,函数名和括号之间没有空格

    3:参与表达式运算时,括号的前面和后面,都要加空格

  • 变量的声明

    console.log(x);
    var x = 10;
    
    //等价于
    
    var x;
    console.log(x);
    x = 10;
    

    为了避免此种情况的出现,建议将所有在本作用域下声明的变量都提到最前面声明并赋值;

  • 自增自减运算

    因为 ++ 在前和++ 在后的运算结果不同,所以,尽量使用 +=1 -=1 替代,

    提高代码的可读性;

    你的团队中一定有搞不明白++在前和在后的区别的二傻子;生活不易,请善待他们;

  • 赋值 =

    赋值前后加空格;

变量命名和代码缩进 规范,是一个程序员必备的基本编程素质;

让别人给你调试BUG的第一前提条件就是 缩进要规范

JS基础-浏览器API

传智播客 & 黑马程序员

第0章 API介绍

HTML:用来存储网页内容;

CSS:用来定义这些内容的显示样式;

JavaScript:用来创造丰富的页面效果或者网页应用。

0.1 API 介绍

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

  • 任何开发语言都可以有自己的API
  • API的特征输入和输出(I/O)
  • API的使用方法

0.2 Web API 接口的概念

浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)

此处的Web API特指浏览器给JS提供的API(一组方法),Web API在后面的课程中有其它含义

前面我们说过,浏览器的API一共提供了三种类型;

分别是 浏览器操控类(BOM)、页面文档操控类(DOM)、网络控制类;

但实际上,浏览器提供的API并不只有这三类,而是有很多类:

文档对象模型、设备API、通信API、数据管理API、特权API、已认证应用程序的私有API;

第1章 文档对象模型 (DOM)

1.1 基本概念

DOM是JavaScript操作网页的接口,全称为“文档对象模型”(Document Object Model)。 它的作用是将网页转为一个JavaScript对象,从而可以用脚本进行各种操作(增删改查)。

浏览器会根据DOM模型,将结构化文档(比如HTML和XML)解析成一系列的节点, 再由这些节点组成一个树状结构(DOM Tree)。 所有的节点和最终的树状结构,都有规范的对外接口。

JavaScript是一门编程语言,而DOM是浏览器对HTML文档结构化后的一个模型;

严格地说,DOM不属于JavaScript,但是我们最常用的就是使用JavaScript操作DOM;

20180526-215645

1.2 节点的概念

DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。

每个节点都可以看作是文档树的一片叶子。

最顶层的节点就是document节点,它代表了整个文档;是文档的根节点。

每张网页都有自己的document节点,window.document属性就指向这个节点的。

只要浏览器开始载入HTML文档,这个节点对象就存在了,可以直接调用。

每一个HTML标签元素,在DOM树上都会转化成一个Element节点对象;

文档里面最高一层一般是HTML标签,其他HTML标签节点都是它的下级。

除了根节点以外,其他节点对于周围的节点都存在三种关系:

父节点关系(parentNode):直接的那个上级节点 
子节点关系(childNodes):直接的下级节点 
同级节点关系(sibling):拥有同一个父节点的节点

常用dom操作:

查找页面的标签元素

标签增加、修改、删除等操作

标签的属性相关操作

给标签元素绑定事件(设置当什么什么时候,做什么什么事情)

1.3 查找节点

上一节我们知道,整个文档的节点就是document节点,那么想要具体找到某个节点,
我们可以使用document提供的一系列方法:

<div>
	<p id="p1">1111111111</p>
	<i>2222222222</i>
	<p class='p'>1111111111</p>
	<i>2222222222</i>
	<p class="p">1111111111</p>
	<i>2222222222</i>
	<div id="p">
	    <p name="p">3333333333</p>
	</div>
</div>

getElementsByTagName()
返回所有指定HTML标签的元素,返回值是一个类似数组的HTMLCollection对象;匹配失败,返回[]
参数是想要获取节点的具体节点名称,就是 标签名;

var p = document.getElementsByTagName('p');
//标签节点.style.样式名 = '样式值'  可以给标签节点设置css样式
p[3].style.background = 'red';

getElementsByClassName()
返回所有class名字符合指定条件的元素,返回值是一个类似数组的HTMLCollection对象;匹配失败,返回[]
参数为 标签的class属性的值

var p = document.getElementsByClassName('p');
p[1].style.background = 'yellow';

getElementsByName()
选择拥有name属性的HTML元素,返回值是一个类似数组的HTMLCollection对象;匹配失败,返回[]
参数为 标签的name属性的值;
注意,使用时,最好选择原生具有name属性的元素;

var p = document.getElementsByName('p');
p[0].style.background = 'yellow';

getElementById()
返回匹配指定id属性的元素节点;没有发现匹配的节点,则返回null
参数为 标签的id属性的值,参数大小写敏感;

var p = document.getElementById('p');
p.style.background = 'yellow';

querySelector()、querySelectorAll()
document.querySelector方法接受一个CSS选择器作为参数,返回匹配该选择器的元素节点;
如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null;

document.querySelectorAll方法与querySelector用法类似,
区别是返回一个类似数组的HTMLCollection对象,包含所有匹配给定选择器的节点。

var p = document.querySelector('.p');
p.style.background = 'yellow';
var p = document.querySelectorAll('.p');
p[1].style.background = 'yellow';

多个参数值,使用,(英文逗号)隔开,而querySelector()返回第一个选中的节点;

var p = document.querySelectorAll('i,.p');
for(var i=0;i<p.length;i++){
    p[i].style.background = 'yellow';
}

这两个方法都支持复杂的CSS选择器。

//选中 id 属性值为p1的元素
// var p = document.querySelectorAll('[id="p1"]');
//选中div元素的class属相值为p的元素
// var p = document.querySelectorAll('div.p');
//选中所有的p标签,但是class值为p的除外
var p = document.querySelectorAll('p:not(.p)');

for(var i=0;i<p.length;i++){
    p[i].style.background = 'yellow';
}

但是,它们不支持CSS伪元素的选择器(比如:first-line和:first-letter)
和伪类的选择器(比如:link和:visited),即无法选中伪元素和伪类。

第2章 事件

2.1 什么是事件

一种 触发—响应 的机制;

用户的行为 + 浏览器感知(捕获)到用户的行为 + 事件处理程序

事件三要素:

  • 事件源:(被)触发事件的元素
  • 事件类型:事件的触发方式(例如鼠标点击或键盘点击)
  • 事件处理程序:事件触发后要执行的代码(函数形式)

2.2 事件绑定

  • 行内方式绑定(元素属性)
<body>
    <input type="button" value="按钮" id="btn" onclick="alert(2)">
</body>

<body>
    <input type="button" value="按钮" id="btn" onclick="f()">
</body>
<script>
    function f(){
        console.log(3);
    }
</script>

onclick 其实就是html元素的一个属性,而属性的值需要是 一段可执行的JS代码

  • 动态绑定 (节点对象属性)
<body>
    <input type="button" value="按钮" id="btn">
</body>
<script>
    var btn = document.getElementById('btn');
    btn.onclick = function(){
        alert(4);
    }
</script>

获取节点对象,然后 修改 节点对象属性 onclick 的值,值是一个 匿名函数 即可;

以上两种事件绑定方式,需要在事件名称前加 on ;

  • 事件监听(节点对象方法)
<body>
    <input type="button" value="按钮" id="btn">
</body>
<script>
    var btn = document.getElementById('btn');
    btn.addEventListener('click',function(){
        alert(5);
    });
</script>

每一个节点对象都提供了 addEventListener 方法,这个方法可以给选中的节点添加指定类型的事件及事件处理程序;

  • 移除事件监听
<body>
    <input type="button" value="按钮" id="btn">
</body>
<script>
    function f(){
        alert(5);
    }
    var btn = document.getElementById('btn');
    btn.addEventListener('click',f);
    btn.removeEventListener('click',f);
</script>

注意:
removeEventListener方法移除的监听函数,
必须与对应的addEventListener方法的参数完全一致,
而且必须在同一个元素节点,否则无效。

2.3 三种事件绑定比较

this关键字
在JavaScript中,每一个函数的内部都存在一个this关键字,其随着运行环境的不同,其指向也是不同的。

<body>
    <p id="t">ttttt</p>
</body>
<script>
    var d = document.getElementById('t');
    d.onclick = function(){
		//this指代本对象 就是 d 
        console.log(this);
    }
</script>

将上述代码的动态绑定,改为行内绑定:

<body>
    <p id="t" onclick="f()">ttttt</p>
</body>
<script>
    function f(){
        console.log(this); //window对象
    }
</script>

由此可知:
行内绑定,其事件处理程序内部的this指向了全局的window对象。
动态绑定,其事件处理程序内部的this指向了当前正在操作的dom对象。

需求:同一个元素的同一个事件,绑定多个处理函数:

<body>
    <!--行内绑定,谁在前谁执行-->
    <p id="t"  onclick="t()" onclick="f()">ttttt</p>
</body>
<script>
    function f(){
        alert(1);
    }
    function t(){
        alert(2);
    }
</script>
<body>
    <p id="t" >ttttt</p>
</body>
<script>
    //动态绑定,后边的执行函数会将前面的覆盖掉
    var d = document.getElementById('t');
    d.onclick = function(){
        alert(1);
    }
    d.onclick = function (){
        alert(2);
    }
</script>
<body>
    <p id="t" >ttttt</p>
</body>
<script>
    var d = document.getElementById('t');
    function f1() {
        console.log(this);
    }
    d.addEventListener('click', f1, false);
    d.addEventListener('click', function(){console.log('f2');}, false);
</script>

总结:
第一种 “HTML标签的on-属性”,违反了HTML与JavaScript代码相分离的原则;处理函数中 this 指向的window对象;
第二种 “Element节点的事件属性” 的缺点是,同一元素同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。但是处理函数中的 this 指向的选中的对象;
第三种:addEventListener方法可以针对同一个元素的同一个事件,添加多个监听处理函数。处理函数中的 this 指向的也是选中的元素;

2.4 事件类型

事件类型一览表:

https://developer.mozilla.org/zh-CN/docs/Web/Events

2.3.1 页面事件(资源事件)
事件名称何时触发
load一个资源及其相关资源已完成加载。
<body onload="f1()" >
</body>
<script>
    function f1(){
        alert('f1');
    }
</script>
2.3.2 焦点事件
事件名称何时触发
focus元素获得焦点
blur元素失去焦点
<body>
    <input type="text" id="t" value="请输入用户名" onfocus="f1()" onblur="f2()">
</body>
<script>
    function f1(){
        document.getElementById('t').value = '';
    }
    function f2(){
        var v = document.getElementById('t').value;
        alert(v);
    }
</script>
2.3.3 鼠标事件
事件名称何时触发
mouseenter指针移到有事件监听的元素内
mouseover指针移到有事件监听的元素或者它的子元素内
mousemove指针在元素内移动时持续触发
mousedown在元素上按下任意鼠标按钮
mouseup在元素上释放任意鼠标按键
click在元素上按下并释放任意鼠标按键
dblclick在元素上双击鼠标按钮
contextmenu右键点击 (右键菜单显示前).
mouseleave指针移出元素范围外(不冒泡)
mouseout指针移出元素,或者移到它的子元素上
select文本被选中(input标签、textarea标签)
copy元素内容被拷贝时
 <body>
    <div id="d" style="width:200px;height:200px;border:1px solid red">
        来啊
    </div>
</body>
<script>
    var d = document.getElementById('d');
    //当鼠标悬浮时触发
    d.onmouseover = function(){
        console.log('来了?');
    }
	//当鼠标离开时触发
    d.onmouseout = function(){
        console.log('不要啊');
    }
	//当鼠标按下时触发
    d.onmousedown = function(){
        console.log('用力啊');            
    }
	//当鼠标弹起时触发
    d.onmouseup = function(){
        console.log('再来');            
    }
	//当鼠标移动时触发
    d.onmousemove = function(){
        console.log('别乱动');            
    }
    
    //当点击右键时
    d.oncontextmenu = function(){
        console.log('你想干什么?');
        return false;
    }
	
    // 当复制内容时
    d.oncopy = function(){
        console.log('你敢复制我?');
        return false;
    }
    
</script>
2.3.4 键盘事件
事件名称何时触发
keydown按下任意按键
keypress除 Shift, Fn, CapsLock 外任意键被按住. (连续触发)
keyup释放任意按键
<body>
    <input type="text"  value="" id="t">
</body>
<script>
    var d = document.getElementById('t');
    //当键盘按下时触发
    d.onkeydown = function(){
        console.log('推到?');
    }
    //当键盘按下时触发
    d.onkeypress = function(){
        console.log('撩起2?');
    }
    //当键盘弹起时触发
    d.onkeyup = function(){
        console.log('撩起?');
    }
</script>
2.3.5 form表单事件
Event NameFired When
reset点击重置按钮时 (<input type=’reset’ value=’重置’ />
submit点击提交按钮
<body>
    <form id="f" action="1.2.5.php">
        姓名:<input type="text" name="" value=""> <br>
        <input type="submit" name="" value="提交">
        <input type="reset" name="" value="重置">
    </form>
</body>
<script>
    var d = document.getElementById('f');
	//当表单提交时触发
    d.onsubmit = function(){
        alert('t');
    }
	//当表单重置时触发
    d.onreset = function(){
        alert('re');
    }
</script>
2.3.6 内容变化事件

change: 当内容改变且失去焦点时触发 (存储事件)
input : 当内容改变时触发 (值变化事件)

<body>
    <input type="text" id="t" value="">
</body>
<script>
    var d = document.getElementById('t');
    //当内容改变且失去焦点时触发
    d.onchange = function(){
        console.log('t');
    }
    //当内容改变时触发
    d.oninput = function(){
        console.log('in');
    }
</script>

2.4 事件的传播

三个包裹着的DIV,都绑定了点击事件,问:
当点击 div1 时,会发生什么现象?

<head>
    <title></title>
    <meta charset="UTF-8">
    <style>
        div{padding: 40px}
        #div3{width: 300px;height: 300px;background-color: red}
        #div2{width: 200px;height: 200px;background-color: yellow}
        #div1{width: 100px;height: 100px;background-color: blue}
    </style>
</head>
<body>
    <div id="div3">3
        <div id="div2">2
            <div id="div1">1</div>
        </div>
    </div>
</body>
<script>
    var d1 = document.getElementById('div1');
    var d2 = document.getElementById('div2');
    var d3 = document.getElementById('div3');

    d1.onclick = function(){
        alert('1');
    }
    d2.onclick = function(){
        alert('2');
    }
    d3.onclick = function(){
        alert('3');
    }
</script>

20180528-183307

当点击div1时,触发 事件1,但是,紧跟着,事件2和事件3也被触发了;

这种现象,我们称为 事件冒泡

在JS中当一个事件发生以后,它会在不同的DOM节点之间传播。
这种传播分成三个阶段:
第一阶段:从window对象传导到目标节点,称为 捕获阶段
第二阶段:在目标节点上触发,称为 目标阶段
第三阶段:从目标节点传导回window对象,称为 冒泡阶段

20180528-183442

事件传播的最上层对象是window;
事件的传播顺序,在捕获阶段依次为window、document、html、body、div;
在冒泡阶段依次为div、body、html、document、window。

注意: 三种事件绑定方式全部 默认 监听冒泡阶段事件;

2.5 改变事件触发的阶段

想让事件监听在捕获阶段,只能通过 addEventListener 方法的进行设置:

<script>
    var d1 = document.getElementById('div1');
    var d2 = document.getElementById('div2');
    var d3 = document.getElementById('div3');
    d1.addEventListener('click',function(){
        alert('m1');
    });//目标阶段触发

    d2.addEventListener('click',function(){
        alert('b2');
    },true);//捕获阶段触发

    d3.addEventListener('click',function(){
        alert('b3');
    },true);//捕获阶段触发


    d1.addEventListener('click',function(){
        alert('mm1');
    });//目标阶段触发

    d2.addEventListener('click',function(){
        alert('p2');
    });//冒泡阶段触发
	d3.addEventListener('click',function(){
        alert('p3');
    },false);//冒泡阶段触发
</script>

2.6 案例

为选中的的元素绑定事件
<body>
<input type="button" value="按钮" id="btn" />
<script>
  //根据id获取元素
  document.getElementById("btn").onclick=function () {
      alert("哈哈,我又变帅了");
  };
</script>
</body>
一次性事件案例(下载按钮点一次则失效)
<body>
    <input type="button" id="btn" value="下载">
</body>
<script>
    var btn = document.getElementById('btn');
    function f(){
        alert(123);
        btn.removeEventListener('click',f);
    }
    
    btn.addEventListener('click',f);
</script>
点击每个图片弹出对话框
<body>
<img src="images/1-small.jpg" alt="" />
<img src="images/2-small.jpg" alt="" />
<img src="images/3-small.jpg" alt="" />
<script>
  //点击每个图片都可以弹出对话框
  //根据标签名字获取元素,分别注册点击事件,分别添加事件处理函数
  var imgObjs=document.getElementsByTagName("img");
  //遍历
  for(var i=0;i<imgObjs.length;i++){
    //为每个图片元素注册点击事件,添加事件处理函数
    imgObjs[i].onclick=function () {
      alert("啊,我被点击了");
    };

  }
</script>

</body>

第3章 节点操作

页面元素节点的操作,都离不开DOM对象

3.1 节点操作-增删改

document.createElement()
用来生成网页元素节点,参数为元素的标签名;

document.createTextNode()
用来生成文本节点,参数为所要生成的文本节点的内容;

node.appendChild()
接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点;

node.hasChildNodes()
返回一个布尔值,表示当前节点是否有子节点

node.removeChild()
接受一个子节点作为参数,用于从当前节点移除该子节点

node.cloneNode()
用于克隆一个选中的节点。
它接受一个布尔值作为参数,表示是否同时克隆子节点,默认是false,即不克隆子节点。
注意: 不会克隆绑定到该元素上的事件;

node.innerHTML
返回该元素包含的 HTML 代码。该属性可读写,常用来设置某个节点的内容;(不属于W3C DOM规范)

node.innerText

返回该元素包含的内容。该属性可读写

<body>
    <div id="d">
        <span>111</span>
    </div>
</body>
<script>
    //创建元素节点
    var p = document.createElement('p');
    //创建文本节点
    var t = document.createTextNode('女娲');
    //添加节点
    p.appendChild(t);
    var d = document.querySelector('#d')
    d.appendChild(p);

    //判断是否有子节点
    if(d.hasChildNodes('span')){
        var s = document.querySelector('span');
        //删除子节点
        d.removeChild(s);
    }

    // 克隆一个节点
    var c = d.cloneNode(true);
    d.appendChild(c);

	//操作选中元素的HTML代码,有值则是设置,无值则是获取
    alert(d.innerHTML);
    
    console.log(document.getElementById('d').innerText);
    console.log(document.getElementById('d').innerHTML);
</script>

案例:

点击按钮创建img节点,添加到body中

<body>
    <input type="button" value="我要图" id="btn">
</body>
<script>
    var btn = document.getElementById('btn');
    btn.onclick = function(){
        // var img = document.createElement('img');
        // img.src = '/img/c2.jpg';
        // document.getElementsByTagName('body')[0].appendChild(img);
        
        // 直接
        document.getElementsByTagName('body')[0].innerHTML += "<img src='/img/c3.jpg'>";
    }
</script>

动态创建文本框

<body>
    <input type="button" value="++" id="btn">
    <div id="bo"></div>
</body>
<script>
    var btn = document.getElementById('btn');
    btn.onclick = function () {
        // document.getElementById('bo').innerHTML += '<input type="text">';
        
        var inp = document.createElement('input');
        document.getElementById('bo').appendChild(inp);
    }
</script>

3.2 节点属性

3.2.1 原生属性

HTML元素节点的标准属性(即在标准中定义的属性),会自动成为元素节点对象的属性

<body>
    <div id="d" a="b" class="a b c d e"></div>
</body>
<script>

    var d = document.querySelector('#d');
    //获取原有属性值
    console.log(d.id);
    //修改原有属性值
    d.id = 'ff';
    console.log(d.a); // undefined
    //特殊:获取class类名,需使用className属性
  	console.log(d.className);
    d.className += ' hello';
</script>
3.2.2 属性操作的标准方法

node.getAttribute()
返回当前元素节点的指定属性。如果指定属性不存在,则返回null;

node.setAttribute()
为当前元素节点新增属性。如果同名属性已存在

<body>
    <div id="d"></div>
</body>
<script>
    var d = document.querySelector('#d');
    //设置属性,有则修改,无则添加,可设置非标准属性
    d.setAttribute('id','ffdd');
    d.setAttribute('aa','kk');
    //获取属性值,可获取非标准属性
    console.log(d.getAttribute('aa'));
</script>

node.hasAttribute()
返回一个布尔值,表示当前元素节点是否包含指定属性

node.removeAttribute()
从当前元素节点移除属性

//如果有id属性
if(d.hasAttribute('id')){
    //删除id属性
    d.removeAttribute('id');
}

3.3 节点操作-层级关系

node.nextElementSibling
返回紧跟在当前节点后面的第一个同级Element节点,如果当前节点后面没有同级节点,则返回null;

node.previousElementSibling
返回紧跟在当前节点前面的第一个同级Element节点,如果当前节点前面没有同级节点,则返回null;

node.parentElement
返回当前节点的父级Element节点;

node.childNodes
返回一个NodeList集合,成员包括当前节点的所有子节点(注意空格回车也算)。

node.firstChild

返回树中节点的第一个子节点,如果节点是无子节点,则返回 null。

node.lastChild

返回该节点的最后一个子节点,如果该节点没有子节点则返回null

<body>
    <div id="d1">
        <p id="p1">11111</p>
        <p id="p2">222</p>
        <p id="p3">33333</p>
        <p id="p4">4444</p>
    </div>
    <div id="d2">
        <p id="p5">55555</p>
        <p id="p6">66666</p>
    </div>
</body>
<script>
    var p2 = document.querySelector('#p2');
    //下一个兄弟节点
    p2.nextElementSibling.style.background = 'red';
    //上一个兄弟节点
    p2.previousElementSibling.style.background = 'red';
    //父级节点
    p2.parentElement.style.background = 'red';

    var d1 = document.querySelector('#d1');
	//所有子节点列表
    d1.childNodes[3].style.background = 'red';
</script>

3.4 CSS样式操作

每个DOM对象都有style属性,我们可以直接操作,用来读写 行内CSS样式
之前,我们已经简单的使用过JS控制元素的CSS样式;
在具体使用的时候还有一些需要重点注意的细节:

  1. 名字需要改写,将横杠从CSS属性名中去除,然后将横杠后的第一个字母大写:

    比如background-color写成backgroundColor

  2. 属性值都是字符串,设置时必须包括单位:

    比如,div.style.width的值不能写为100,而要写为100px

<body>
    <div id="d1" style="width:400px;height: 200px;border: 1px solid red"></div>
</body>
<script>
    var d1 = document.querySelector('#d1');
    d1.onclick = function(){
        //赋值则是设置
        d1.style.backgroundColor = 'red';
        //不赋值则是获取
        alert(d1.style.width);
    }
</script>

以上代码中,我们获取的CSS样式,均是行内样式;

如果将 样式表写在 style 标签内,我们将无法获取和修改;

getComputedStyle()
接受一个节点对象,返回该节点对象最终样式信息的对象,所谓“最终样式信息”,指的是各种CSS规则叠加后的结果。

注意: getComputedStyle() 是window对象下的方法,不是DOM对象

<style>
    #d1{
        width: 200px;height: 200px;
        border: 1px solid red;
    }
</style>

<body>
    <div id="d1" ></div>
</body>

<script>
    var d1 = document.querySelector('#d1');
    d1.onclick = function(){
        //获取不到
        console.log(d1.style.width);
        //获取计算后的样式
        console.log(getComputedStyle(d1).width);
    }
</script>

点击变大小案例:

<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
    #d1{
        width: 200px;height: 200px;
        border: 1px solid red;
    }
</style>
</head>
<body>
    <div id="d1" ></div>
</body>
<script>
    var d1 = document.querySelector('#d1');
    d1.onclick = function(){
        var w = parseInt(getComputedStyle(d1).width);
        var h = parseInt(getComputedStyle(d1).height);
        d1.style.width = w+10+'px';
		d1.style.height = h+10+'px';
    }
</script>

其他方法和属性:

document.documentURI 返回文档的 URL。

node.replaceChild(newChild, oldChild) 用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。

node.insertBefore() : parentElement.insertBefore(newElement,referenceElement);

3.5 案例(节点属性)

网页开关灯效果实现(类名操作)
<style>
    .cls {
      background-color: black;
    }
  </style>
</head>
<body>
<input type="button" value="开/关灯" id="btn"/>

<script>
  document.getElementById("btn").onclick = function () {
    //document.body.className="cls";
//console.log(document.body.className);
    //判断body标签是否应用了cls类样式,同时设置body标签的类样式
    document.body.className = document.body.className == "cls" ? "" : "cls";
  };
</script>
点击按钮显示一个图片
<body>
<input type="button" value="显示图片" id="btn"/>
<img src="" alt="美女" id="im" width="300" height="400" />
<script>
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick=function () {
    //根据id获取图片标签,设置src属性即可
    var imgObj=document.getElementById("im");
    //设置路径src属性
    imgObj.src="images/liuyan.jpg";
  };
</script>
</body>
案例点击按钮修改p标签内容
<body>
<input type="button" value="设置p的内容" id="btn"/>
<p id="p1">这是一个p</p>
<script>
  //点击按钮,设置p的内容
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick = function () {
    //var pObj = document.getElementById("p1");
    document.getElementById("p1").innerText = "哦,这是p啊";
  };
</script>

</body>
点击按钮设置a标签的地址和热点文字
<input type="button" value="显示效果" id="btn"/>
<a href="http://www.baidu.com" id="ak">百度</a>
<script>
  //案例:点击按钮修改a的地址和热点文字
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick=function () {
    var aObj=document.getElementById("ak");
    aObj.href="http://www.itcast.cn";
    aObj.innerText="传智播客";
  };
</script>
</body>
点击按钮设置所有的p的内容
<body>
<input type="button" value="改变内容" id="btn"/>
<p>红烧榴莲</p>
<p>清蒸臭豆腐</p>
<p>油炸大蒜</p>
<p>爆炒助教</p>
<p>凉拌班主任</p>
<script>

  //document.getElementsByTagName("标签的名字");

  //点击按钮,修改所有的p的内容
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick = function () {
    //获取所有的p标签---根据标签名字来获取---伪数组
    var pObjs = document.getElementsByTagName("p");
    //循环遍历这个伪数组
    for (var i = 0; i < pObjs.length; i++) {
      pObjs[i].innerText = "我们都是p";
    }
  };

</script>

</body>
点击按钮修改图片的alt和title
<body>
<input type="button" value="显示效果" id="btn"/>
<img id="im" src="images/cangjingkong.jpg" alt="" title=""/>
<script>
  //点击按钮,修改图片的宽和高,alt和title属性值
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick = function () {
    //根据id获取图片标签
    var imgObj = document.getElementById("im");
    //设置属性
    imgObj.width = "500";
    imgObj.height = "600";
    imgObj.alt = "好漂亮";
    imgObj.title = "美女";
  };
</script>
</body>
点击按钮修改按钮的值
<body>
<input type="button" value="按钮" id="btn"/>
<script>
  //案例:点击按钮修改按钮的value属性值
  //根据id获取按钮,注册点击事件,添加事件处理函数
//  document.getElementById("btn").οnclick=function () {
//    document.getElementById("btn").value="改变吧";
//  };

  //在某个元素的自己的事件中,this就是当前的这个元素

  document.getElementById("btn").onclick=function () {
    //当前对象
    this.value="改变吧";
  };
</script>

</body>
点击图片修改自身的宽和高
<body>
<img src="images/boduo.jpg" alt="" id="im" />
<script>
  //点击图片,修改自身的宽和高
  //根据id获取图片,注册点击事件,添加事件处理函数
  document.getElementById("im").onclick=function () {
    this.width="300";
    this.height="400";
  };
</script>

</body>
点击按钮修改所有的文本框的值
<body>
<input type="button" value="显示效果" id="btn"/><br/>
<input type="text" value=""/><br/>
<input type="text" value=""/><br/>
<input type="text" value=""/><br/>
<input type="text" value=""/><br/>
<input type="text" value=""/><br/>
<script>
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick=function () {
    //根据标签名字获取文本框,所有的input标签
    var inputs=document.getElementsByTagName("input");
    for(var i=0;i<inputs.length;i++){
      //判断当前input是不是文本框
      if(inputs[i].type=="text"){
        inputs[i].value="我是文本框";
      }
    }

  };
</script>
排他功能
<body>
<input type="button" value="没怀孕"/>
<input type="button" value="没怀孕"/>
<input type="button" value="没怀孕"/>
<input type="button" value="没怀孕"/>
<input type="button" value="没怀孕"/>
<script>

  //获取所有的按钮
  var btnObjs = document.getElementsByTagName("input");
  //循环,为每个按钮注册点击事件,添加事件处理函数
  for (var i = 0; i < btnObjs.length; i++) {
    //每个按钮注册点击事件
    btnObjs[i].onclick = function () {
      //把所有的按钮的value值还原
      for (var j = 0; j < btnObjs.length; j++) {
        btnObjs[j].value = "没怀孕";
      }
      //设置当前的这个按钮的value
      this.value = "怀孕了";
    };
  }
</script>
点击按钮禁用文本框
<input type="button" value="禁用文本框" id="btn"/>
<input type="text" value="" id="txt"/>
<script src="common.js"></script>
<script>
  //点击按钮禁用这个文本框
 document.getElementById("btn").onclick = function () {
    document.getElementById("txt").disabled = true;
  };
</script>
鹦鹉学舌
<body>
    <input type="text" id="t1" > <br>
    <input type="text" id="t2" >
</body>
<script>
    document.getElementById('t1').oninput = function(){
        document.getElementById('t2').value = this.value;
    }
</script>
全选和全不选
<style>
    table {
        border-collapse: collapse;
        border-spacing: 0;
        border: 1px solid #c0c0c0;
        width: 500px;
    }

    th,
    td {
        border: 1px solid #d0d0d0;
        color: #404060;
        padding: 10px;
    }
</style>

<body>
    <table>
        <thead>
            <tr>
                <th>
                    <input type="checkbox" id="th" />
                </th>
                <th>菜名</th>
                <th>饭店</th>
            </tr>
        </thead>
        <tbody id="tb">
            <tr>
                <td>
                    <input type="checkbox" />
                </td>
                <td>红烧肉</td>
                <td>田老师</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" />
                </td>
                <td>西红柿鸡蛋</td>
                <td>田老师</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" />
                </td>
                <td>油炸榴莲</td>
                <td>田老师</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" />
                </td>
                <td>清蒸助教</td>
                <td>田老师</td>
            </tr>
        </tbody>
    </table>
</body>
<script>
    document.getElementById('th').onclick = function () {
        var tb = document.getElementsByTagName('input');
        if (this.checked == true) {
            // console.log(tb);
            for (var i = 0; i < tb.length; i++) {
                tb[i].checked = true;
            }
        } else {
            for (var i = 0; i < tb.length; i++) {
                tb[i].checked = false;
            }
        }
    }
</script>

作业,实现反选

表格鼠标悬浮高亮(接上一个案例)
var t = document.getElementsByTagName('tbody')[0];
t.onmouseover = function(e){
    e.target.parentElement.style.background = '#F5F5F5';
}
t.onmouseout = function(e){
    e.target.parentElement.style.background = '#fff';
}

3.6 案例(样式操作)

节点方式隔行变色
<body>
    <input type="button" value="隔行变色" id="btn" />
    <ul id="uu">
        <li>雪花啤酒</li>
        <li>金士百啤酒</li>
        <li>青岛啤酒</li>
        <li>燕京啤酒</li>
        <li>百威啤酒</li>
        <li>哈尔滨啤酒</li>
        <li>乐宝啤酒</li>
        <li>崂山啤酒</li>
    </ul>
</body>
<script>
    //点击按钮,所有的li隔行变色---奇红偶黄
    document.getElementById("btn").onclick = function () {
        var count = 0;
        //要获取ul中所有的子节点
        var nodes = document.getElementById("uu").childNodes;
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            //判断这个节点是不是li
            if (node.nodeType == "1" && node.nodeName == "LI") {
                node.style.backgroundColor = count % 2 == 0 ? "red" : "yellow";
                count++;//记录li标签的个数
            }
        }
    };
</script>
验证密码的长度改变背景颜色
<input type="text" value="" id="txt"/>
<script>
  //根据id获取文本框---失去焦点的事件
  document.getElementById("txt").onblur=function () {
    //判断文本框中输入的内容长度是否在6到10个之间,如果是这样的,则背景颜色为绿色
    if(this.value.length>=6&&this.value.length<=10){
      this.style.backgroundColor="green";
    }else{
      this.style.backgroundColor="red";
    }
  };
</script>
div的高亮显示
<style>
    div {
        width: 200px;
        height: 200px;
        background-color: green;
        float: left;
        margin-right: 20px;
        cursor: pointer;
        border: 2px solid green;
    }
</style>

<body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>

    <script>
        //根据标签名字获取所有的div
        var divObjs = document.getElementsByTagName("div");
        //循环遍历
        for (var i = 0; i < divObjs.length; i++) {
            //为每个div添加鼠标进入事件
            divObjs[i].onmouseover = mouseoverHandle;
            //为每个div添加鼠标离开事件
            divObjs[i].onmouseout = mouseoutHandle;
        }
        function mouseoverHandle() {
            this.style.border = "2px solid red";
        }
        function mouseoutHandle() {
            //希望这个样式属性的值还原成默认的时候,值就是""空的字符串
            this.style.border = "";
        }
    </script>
鼠标点哪图片飞到哪里

思路:给整个dom绑定鼠标点击事件,获取点击位置后修改图片位置

<style>
    img {
        position: absolute;
        width: 50px;
        height: 50px;
    }
</style>

<body>
    <img src="/img/c3.jpg" alt="">
</body>
<script>
    var im = document.getElementsByTagName('img')[0];
    document.onclick = function (e) {
        im.style.left = e.clientX + 'px';
        im.style.top = e.clientY + 'px';
    }
</script>
跟着鼠标飞的天使

思路:点击图片后,给整个dom绑定鼠标移动事件,让图片跟随

<style>
    img {
        position: absolute;
        width: 50px;
        height: 50px;
    }
</style>

<body>
    <img src="/img/c3.jpg" alt="">
</body>
<script>
    var im = document.getElementsByTagName('img')[0];
    im.onclick = function () {
        document.onmousemove = function (e) {
            im.style.left = e.clientX + 'px';
            im.style.top = e.clientY + 'px';
        }
    }
</script>
实时获取鼠标在div内的坐标
<body>
    <div id="div3">3
       <p id="p"></p>
    </div>

</body>
<script>
    var p = document.getElementById('p');
    document.getElementById('div3').onmousemove = function(e){
        p.innerHTML = e.clientX+','+e.clientY;
        p.style.top = e.clientY+"px";
        p.style.left = e.clientX+"px";
    }
</script>
点击按钮设置div的宽和高及背景颜色
<head>
  <meta charset="UTF-8">
  <title>title</title>
  <style>
    div{
      width: 100px;
      height: 50px;
      background-color: yellow;
    }
  </style>
</head>
<body>
<input type="button" value="显示效果" id="btn"/>
<div id="dv"></div>
<script>

  //点击按钮,设置div的宽和高,及背景颜色
  //根据id获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick=function () {
    //获取div
    var dvObj=document.getElementById("dv");
    dvObj.style.width="300px";
    dvObj.style.height="200px";
    //css中的属性如果是多个单词连接的,在js代码DOM操作中多个单词中间的-干掉,后面单词的首字母变大写
    dvObj.style.backgroundColor="pink";
  };

</script>
点击按钮隐藏div
  <style>
    div{
      width: 200px;
      height: 100px;
      background-color: orangered;
    }
  </style>
</head>
<body>
<input type="button" value="隐藏" id="btn"/>
<input type="button" value="显示" id="btn2"/>
<div id="dv"></div>
<script src="common.js"></script>
<script>
  //点击按钮隐藏div
  document.getElementById("btn").onclick=function () {
    //获取div,隐藏
    document.getElementById("dv").style.display="none";
  };
  document.getElementById("btn2").onclick=function () {
    document.getElementById("dv").style.display="block";
  };
</script>
点击按钮改变列表的背景颜色
<input type="button" value="改变颜色" id="btn"/>
<ul id="uu">
  <li>乔峰</li>
  <li>卡卡西</li>
  <li>佐助</li>
  <li>自来也</li>
  <li>纲手</li>
  <li>雏田</li>
  <li>露琪亚</li>
</ul>
<script src="common.js"></script>
<script>
  //获取按钮,注册点击事件,添加事件处理函数
  document.getElementById("btn").onclick=function () {
    document.getElementById("uu").style.backgroundColor="pink";
  };
</script>
点击按钮列表隔行变色
<input type="button" value="隔行变色" id="btn"/>
<ul id="uu">
  <li>五菱宏光</li>
  <li>路虎</li>
  <li>兰博基尼</li>
  <li>布加迪威龙</li>
  <li>玛莎拉蒂</li>
  <li>奥拓</li>
  <li>拖拉机</li>
  <li>桑塔纳</li>
</ul>
<script>
  //点击按钮,li隔行变色:奇红偶黄
  //获取按钮,添加点击事件
  document.getElementById("btn").onclick = function () {
    //获取id为uu的ul中所有的li
    var list = getElementsByTagName("li");
    //遍历
    for (var i = 0; i < list.length; i++) {
//      if (i % 2 == 0) {
//        list[i].style.backgroundColor = "red";
//      } else {
//        list[i].style.backgroundColor = "yellow";
//      }

      list[i].style.backgroundColor = i % 2 == 0 ? "red" : "yellow";
    }
  };
</script>

3.7 案例(节点操作)

动态可编辑表格
<style>
    table {
        border-collapse: collapse;
        border-spacing: 0;
        border: 1px solid #c0c0c0;
        width: 500px;
    }

    th,
    td {
        border: 1px solid #d0d0d0;
        color: #404060;
        padding: 10px;
    }
</style>

<body>
    <input type="button" value="添加一行" id="addrow">
    <table>
        <thead>
            <tr>
                <td>菜名</td>
                <td>饭店</td>
                <td>厨师</td>
            </tr>
        </thead>
        <tbody id="tb"></tbody>
    </table>
</body>
<script>
    var tbs = document.getElementsByTagName('table')[0];
    tbs.onclick = function (e) {
        var clicks = e.target;
        if (clicks.nodeName == 'TD') {
            // var inp = document.createElement('input');
            // inp.value = clicks.innerText;
            // console.log(clicks.parentElement);
            clicks.innerHTML = '<input value=' + clicks.innerText + '>';
            tbs.getElementsByTagName('input')[0].onblur = function () {
                clicks.innerHTML = this.value;
            }
        }
    }

    var addrow = document.getElementById('addrow');
    addrow.onclick = function(){
        var tr = document.createElement('tr');
        for(var i = 0; i<3;i++){
            tr.appendChild(document.createElement('td'));
        }
        document.getElementById('tb').appendChild(tr);
    }

</script>
无刷新评论
<body>
    <div id="cont">
        <div>
            <p>name:</p>
            <p>我是评论内容</p>
            <hr>
        </div>
    </div>

    <div>
        昵称:
        <input type="text" value="" id="userName" /><br/><br/>
        <textarea name="" id="tt" cols="103" rows="10"></textarea><br/>
        <input type="button" value="评论一下" id="btn" /><br/>
    </div>
</body>
<script>
    var un = document.getElementById('userName');
    var tt = document.getElementById('tt');

    document.getElementById('btn').onclick = function(){
        var d = document.createElement('div');
        d.innerHTML = '<p>'+un.value+':</p>';
        d.innerHTML += '<p>'+tt.value+'</p>';
        d.innerHTML += '<hr>';
        document.getElementById('cont').appendChild(d);
        un.value = '';
        tt.value = '';
    }

</script>

第4章 事件对象

4.1 概述

事件的触发,大部分情况下是用户的一种行为,也就是说,我们并不能确定用户什么时间触发;

而且,由于事件的传播机制,我们甚至不能确定事件具体触发在哪个节点;这是一件很不爽的事情;

如何解决呢?

事件发生以后,系统会调用我们写好的事件处理程序

系统会在调用处理程序时,将事件发生时有关事件的一切信息,封装成一个对象,

作为参数传给监听函数(事件处理程序),我们把这个对象称为 事件对象
有关事件发生的一切信息,都包含在这个事件对象中;

根据事件类型的不同,事件对象中包含的信息也有所不同;
如点击事件中,包含鼠标点击的横纵坐标位置,键盘事件中,包含键盘的键值等;

<body>
    <div id="div">
        <p>pppp</p>
    </div>
    <input type="text" value="" id="i">
</body>
<script>
var d = document.getElementById('div');
//鼠标事件
d.addEventListener('click',function(e){
    console.log(e);
});

var i = document.getElementById('i');
//键盘事件
i.addEventListener('keydown',k);
function k(e){
    console.log(e);
}
</script>

20180528-183455

4.2 事件对象中的常用属性及方法

4.2.1属性

event.bubbles:属性返回一个布尔值,表示当前事件是否会冒泡;
event.eventPhase:返回一个整数值,表示事件流在传播阶段的位置

0:事件目前没有发生。
1:事件目前处于捕获阶段。
2:事件到达目标节点。
3:事件处于冒泡阶段。

event.type:返回一个字符串,表示事件类型,大小写敏感;
event.timeStamp:返回一个毫秒时间戳,表示事件发生的时间;

clientX、clientY :获取鼠标事件触发的坐标

<body>
    <div id="d">
        <p id="p">sdf</p>
    </div>
</body>
<script>
    var p = document.getElementById('p');
    p.onclick = function(e){
        //当前事件是否会冒泡
        console.log(e.bubbles);
        //事件目前所处的节点
        console.log(e.eventPhase);
        //事件类型
        console.log(e.type);
        //事件发生的时间戳
        console.log(e.timeStamp);
    }
</script>
4.2.2 事件代理/委托

event.target:对事件起源目标的引用,属性返回触发事件的那个节点。
event.currentTarget:属性返回事件当前所在的节点,即正在执行的监听函数所绑定的那个节点。
作为比较,target属性返回事件发生的节点。

var d = document.getElementById('d');
d.onclick = function(e){
    //返回事件节点
    console.log(e.currentTarget);
    //返回触发节点
    console.log(e.target);
}

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父
节点上,由父节点的监听函数统一处理多个子元素的事件。
这种方法叫做事件的代理也叫 事件委托 也有人称为 事件代理

<head>
    <title></title>
    <meta charset="UTF-8">
    <style>
        div{padding: 40px}
        #div3{width: 300px;height: 300px;border: 1px solid red;}
        #div2{width: 200px;height: 200px;border: 1px solid red;}
        #div1{width: 100px;height: 100px;border: 1px solid red}
    </style>
</head>
<body>
    <div id="div3">3
        <div id="div2">2
            <div id="div1">1</div>
        </div>
    </div>
</body>
<script>
    var d = document.getElementById('div3');
    d.onclick = function(e){
        e.target.style.background = 'red';
    }
</script>
4.3.3 阻止浏览器默认行为&阻止事件传播

event.preventDefault()
方法取消浏览器对当前事件的默认行为,
比如点击链接后,浏览器跳转到指定页面,或者按一下空格键,页面向下滚动一段距离。

event.stopPropagation()
方法阻止事件在DOM中继续传播,防止再触发定义在别的节点上的监听函数

<body>
    <div id="div2">2
        <div id="div1">1
            <a id="a" href="http://qq.com">王者喝农药</a>
        </div>
    </div>
</body>
<script>
    var d2 = document.getElementById('div2');
    var d1 = document.getElementById('div1');
    var a = document.getElementById('a');
    d2.onclick = function(e){
       alert('d2');
    }
    d1.onclick = function(e){
       alert('d1');
    }
    a.onclick = function(e){
        //阻止事件传播
        // e.stopPropagation();
       alert('a');
       //阻止浏览器默认行为
       e.preventDefault();
    }
</script>

4.3 案例

阻止超链接默认跳转
<body>
<!--第1种写法-->
<a href="http://www.baidu.com" onclick="alert('哈哈'); return false;">百度</a>


<!--第2种写法-->

<script>
  function f1() {
    alert("嘎嘎");
    return false;
  }
</script>
<a href="http://www.baidu.com" onclick="return f1();">百度</a>

<!--第3种写法-->
<a href="http://www.baidu.com" id="ak">百度</a>
<script>
  document.getElementById("ak").onclick=function () {
    alert("哈哈");
    return false;
  };
</script>
点击小图显示大图
<body>
<a id="ak" href="images/1.jpg"><img src="images/1-small.jpg" alt="" id="im"></a>
<script>
  //点击小图片,显示大图---修改了这个图片标签的src的属性值
  //根据id获取小图,注册点击事件,添加事件处理函数
  document.getElementById("im").onclick=function () {
    //根据id获取超链接
    var aObj=document.getElementById("ak");
    this.src=aObj.href;
    //阻止超链接的默认跳转事件
    return false;
  };
</script>

第5章 浏览器对象模型

5.1 介绍

浏览器对象模型(Browser Object Model)–英文简称 BOM,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。我们使用JavaScript与浏览器交互的所有内容,均来自 浏览器对象模型

浏览器对象模型的具体实例化对象就是 window 对象;

window 对象下有很多属性和方法,我们前面学过的DOM对象,就是window对象的一个属性,只不过这个属性的值又是一个对象,因此也成为window对象的子对象;

https://developer.mozilla.org/zh-CN/docs/Web/API/Window

5.2 对话框

window.alert() : 显示一个警告对话框,上面显示有指定的文本内容以及一个"确定"按钮。

window.prompt() : 显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字.

var s = window.prompt('你觉得很幸运吗?','是的');
console.log(s);

window.confirm() :方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框 。

5.3 页面加载事件

onload

window.onload = function () {
  // 当页面加载完成执行
  // 当页面完全加载所有内容(包括图像、脚本文件、CSS 文件等)执行
}

5.4 浏览器控制台

window.console : 返回console对象的引用,该对象提供了对浏览器调试控制台的访问。

Console.clear() : 清空控制台。

Console.error() : 打印一条错误信息

Console.table() : 将数组或对象数据在控制台以表格形式打印

Console.log() : 打印字符串,使用方法比较类似C的printf、PHP的echo等格式输出

5.5 定时器

setTimeout()和clearTimeout()

在指定的毫秒数到达之后执行指定的函数,只执行一次

// 创建一个定时器,1000毫秒后执行,返回定时器的标示
var timerId = window.setTimeout(function () {
  console.log('Hello World');
}, 1000);

// 取消定时器的执行
window.clearTimeout(timerId);
setInterval()和clearInterval()

定时调用的函数,可以按照给定的时间(单位毫秒)周期调用函数

// 创建一个定时器,每隔1秒调用一次
var timerId = window.setInterval(function () {
  var date = new Date();
  console.log(date.toLocaleTimeString());
}, 1000);

window.clearInterval(timerId);

5.6 案例

一起来摇摆
<style>
    #div{
        position: absolute;
    }
</style>
<body>
    <input type="button" value="摇起来" id="btn1">
    <div id="div">
        <img src="/img/c2.jpg" style="width: 50px;height: 50px;" alt="">
        <img src="/img/c3.jpg" style="width: 50px;height: 50px;" alt="">
    </div>
</body>
<script>
    var b1 = document.getElementById('btn1');
    var b2 = document.getElementById('btn2');

    b1.onclick = function(){
        
        if(this.value != '停止'){
            this.value = '停止';
            var d = document.getElementById('div');
            c = window.setInterval(function(){
                d.style.left = parseInt(Math.random() * 100 +1) + 'px'
                d.style.top = parseInt(Math.random() * 100 +1) + 'px'
            },50);
        }else{
            this.value = '摇起来';
            clearInterval(c);
        }
    }
</script>   
眼看着图片就这个飞走了
<style>
    #div{
        position: absolute;
    }
</style>
<body>
    <input type="button" value="摇起来" id="btn1">
    <div id="div">
        <img src="/img/c2.jpg" style="width: 50px;height: 50px;" alt="">
    </div>
</body>
<script>
    var b1 = document.getElementById('btn1');
    b1.onclick = function(){
        var d = document.getElementById('div');
        window.setInterval(function(){
            var s = parseInt(getComputedStyle(d)['top']);
            var l = parseInt(getComputedStyle(d)['left']);
            // console.log(l);
            d.style.top = s-1+'px';
            d.style.left = l+5+'px';
        },50)
    }
</script> 

5.7 location对象

https://developer.mozilla.org/zh-CN/docs/Web/API/Location

window.location 只读属性,返回一个  Location对象,其中包含有关文档当前位置的信息;

URL

统一资源定位符 (Uniform Resource Locator, URL)

URL的组成:

scheme://host:port/path?query#fragment
scheme:通信协议
	常用的http,ftp,maito等
host:主机
	服务器(计算机)域名系统 (DNS) 主机名或 IP 地址。
port:端口号
	整数,可选,省略时使用方案的默认端口,如http的默认端口为80。
path:路径
	由零或多个'/'符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。
query:查询
	可选,用于给动态网页传递参数,可有多个参数,用'&'符号隔开,每个参数的名和值用'='符号隔开。例如:name=zs
fragment:信息片断
	字符串,锚点.

console.log(location);

20180601-211004

页面跳转

location.href = 'http://qq.com'

5.8 history对象

https://developer.mozilla.org/zh-CN/docs/Web/API/History

history.back() : 前往上一页, 用户可点击浏览器左上角的返回按钮模拟此方法

history.forward() : 在浏览器历史记录里前往下一页,用户可点击浏览器左上角的前进按钮模拟此方法

history.go() : 通过当前页面的相对位置从浏览器历史记录( 会话记录 )加载页面。比如:参数为-1的时候为上一页,参数为1的时候为下一页.

5.9 navigator对象

https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator

userAgent : 通过userAgent可以判断用户浏览器的类型

platform : 通过platform可以判断浏览器所在的系统平台类型.

geolocation : 位置定位对象;

参考: https://developer.mozilla.org/zh-CN/docs/Web/Reference/API


第0章 课程回顾

0.1 JS语法基础-复习

  • 变量:变量的声明及命名规则

  • 数据类型:数值、字符串、布尔、undefined、null、对象

  • 运算符:+ - * \ == != < > ?: && ||

  • 流程控制结构: if else 、 switch case 、while 、for ;

  • 数组及函数:声明、访问、调用……

  • 作用域:全局作用域、局部作用域、作用链

  • 对象:声明、调用、属性、方法

  • 内置对象:Math、Date、Array、String

  • JS代码执行流程

0.2 JS-web-api-复习

API:浏览器对象模型

  • 定时器(setTimeout()、setInterval())
  • location对象、history对象、navigator对象
  • 事件:鼠标、键盘、表单、绑定、事件传播、事件对象(事件发生瞬间的一切信息,都包含在事件对象中)

DOM:文档对象模型

DOM对象、节点对象、节点属性、获取节点元素、节点元素的增删改查

createElement、childNodes、removeChild、parentNode、nextSibling、firstChild、innerHTML……

0.3 JS高级课程介绍

avaScript-plus-naot

0.4 案例:交换变量的值

临时变量、加减运算、数组方式、对象方式

0.5 数组遍历

for()循环 for in索引遍历 数组.forEach方法 for of 值遍历(ES6)

0.6 数据在内存中的存储

内存地址分区:

基本类型:非对象 string number boolean undefined null 数据直接存储在栈区

var a = 1;
变量栈区堆区
a1
var a = 1;
var b = a;
变量栈区堆区
a1
b1
var a = 1;
var b = a;
b = 2;
变量栈区堆区
a1
b2

引用类型:对象(array object function) 堆区存数据, 栈区存数据在堆区的地址

var obj = {"age":40, "sex":"男"};
变量栈区堆区堆区地址
obj00000001(堆区地址){“age”:40, “sex”:“男”}00000001
var obj = {"age":40, "sex":"男"};
var obj2 = obj;
变量栈区堆区堆区地址
obj00000001{“age”:40, “sex”:“男”}00000001
obj200000001
var obj = {"age":40, "sex":"男"};
var obj2 = obj;
obj2.age = 30;
变量栈区堆区堆区地址
obj00000001{“age”:30, “sex”:“男”}00000001
obj200000001

传值方式:

值传递 :基本数据类型,直接将变量放在栈区的值,复制一份,传给另外一个变量。

引用传递:对象类型,将变量放在堆区的值的地址,传给另外一个变量。

js中,对象(object , array, function), 传递过程中,都使用引用传递。

第1章 JS面向对象编程

xdxk

学习目标

  • 初步理解对象是什么及面向对象编程的概念
  • 能够自己创建一个对象

1.1 面向对象介绍

什么是对象?

Everything is object (万物皆对象), JS语言中将一切都视为 对象

016082302454244

  • 对象是对概念的具体化体现:

一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。

当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

  • 编程中对象是一个容器,封装了属性(property)和方法(method)

属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。

也可以将其简单理解为:数据集或功能集

ECMAScript 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数
严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都
映射到一个值。

1.2 面向对象编程

面向过程:以前写js代码,都是面向过程。

面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。

c06c066867f1d

面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。
它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

典型问题:将大象关进冰箱分几步

面向过程式:

开门(冰箱)

放进(冰箱,大象)

关门(冰箱)

面向对象式:

两个对象:大象、冰箱

冰箱.开门()

冰箱.放进(大象)

冰箱.关门()

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。
因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

面向对象与面向过程:

  • 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
  • 面向对象就是找一个对象,指挥得结果
  • 面向对象将执行者转变成指挥者
  • 面向对象不是面向过程的替代,而是面向过程的封装

面向对象的特性:

  • 封装性:对象中的属性、方法,对外提供一组方法(操作数据的接口),使用时无需关心内部具体实现。
  • 继承性:将同一类对象,公共的属性方法,提取到一个单独公共对象中,具体的子对象可以使用这个公共对象中的成员
  • [多态性]:动物的叫声为例,同一类的对象,有相同的方法(动物会叫), 但是每个具体的对象,方法实现的效果不一样(每个动物叫声不一样)

扩展阅读:

1.3 创建对象

JavaScript 语言(ES5)的对象体系,不基于“类” 创建对象,是基于构造函数(constructor)和原型链(prototype)。

简单方式创建对象

我们可以直接通过 new Object() 创建:

var person = new Object()
person.name = 'Jack'
person.age = 18

person.sayName = function () {
    console.log(this.name)
}

字面量方式创建对象

每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建:

var person = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?

var person1 = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

var person2 = {
  name: 'Mike',
  age: 16,
  sayName: function () {
    console.log(this.name)
  }
}

通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。

简单方式的改进:工厂函数

我们可以写一个函数,解决代码重复问题:

function createPerson (name, age) {
  return {
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
}

然后生成实例对象:

var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)

这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,
但是这依然没有脱离 使用 字面量方式创建对象 的本质;

第2章 构造函数

学习目标

  • 构造函数语法
  • 分析构造函数
  • 构造函数和实例对象的关系
    • 实例的 constructor 属性
    • instanceof 操作符
  • 普通函数调用和构造函数调用的区别
  • 构造函数的返回值
  • 构造函数的问题

2.1 构造函数

JavaScript 语言使用构造函数作为对象的模板。
所谓 ”构造函数”,就是一个普通的函数,只不过我们专门用它来生成对象(new 构造函数),这样使用的函数,就是构造函数;

它提供模板,描述对象的基本结构。
一个构造函数,可以生成多个对象,这些对象都有相同的结构。

function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike

解析 构造函数代码 的执行

在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。
这是为什么呢?

我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处:

  • 没有显式的创建对象(没有使用字面量)
  • 直接将属性和方法赋给了 this
  • 没有 return 语句
  • 函数名使用的是大写的 Person

而要创建 Person 实例,则必须使用 new 操作符。

以这种方式调用构造函数会经历以下 5 个步骤:

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。先记住,后面讲
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 执行构造函数内部的代码。
  5. 返回新对象
function Person (name, age) {
  // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
  // 然后让内部的 this 指向新创建的对象
  // 接下来所有针对 this 的操作实际上操作的就是刚创建的这个对象

  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }

  // 在函数的结尾处会将 this 返回,也就是这个新对象
}

构造函数和实例对象的关系

构造函数是根据具体的事物抽象出来的抽象模板

实例对象是根据抽象的构造函数模板得到的具体实例对象

实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的;

每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数

反推出来,每一个对象都有其构造函数

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

因此,我们可以通过实例对象的 constructor 属性判断实例和构造函数之间的关系

注意:这种方式不严谨,推荐使用 instanceof 操作符,后面学原型会解释为什么

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true

constructor 既可以判断也可以获取

instanceof 只能用于判断

2.2 构造函数存在的问题

以构造函数为模板,创建对象,对象的属性和方法都可以在构造函数内部定义;

function Cat(name, color) {
  this.name = name;
  this.color = color;
  this.say = function () {
    console.log('hello'+this.name,this.color);
  };
}
var cat1 = new Cat('猫', '白色'); 
var cat2 = new Cat('猫', '黑色'); 
cat1.say();
cat2.say();

在该示例中,从表面上看好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象, namesay 都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

对于这种问题我们可以把需要共享的函数定义到构造函数外部:

function say(){
    console.log('hello'+this.name,this.color);
}

function Cat(name, color) {
  this.name = name;
  this.color = color;
  this.say = say;
}
var cat1 = new Cat('猫', '白色'); 
var cat2 = new Cat('猫', '黑色'); 
cat1.say();
cat2.say();

这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局变量(函数名)冲突的问题。

你肯定想到了可以把多个函数放到一个对象中用来避免全局变量(函数名)冲突的问题:

var s = {
    sayhello:function (){
        console.log('hello'+this.name,this.color);
    },
    saycolor:function(){
        console.log('hello'+this.color);
    }
}

function Cat(name, color) {
  this.name = name;
  this.color = color;
  this.sayhello = s.sayhello;
  this.saycolor = s.saycolor;
}
var cat1 = new Cat('猫', '白色'); 
var cat2 = new Cat('猫', '黑色'); 
cat1.sayhello();
cat2.saycolor();

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
但是代码看起来还是那么的格格不入,那有没有更好的方式呢?

小结

  • 构造函数语法
  • 分析构造函数
  • 构造函数和实例对象的关系
    • 实例的 constructor 属性
    • instanceof 操作符
  • 构造函数的问题

第3章 原型

学习目标

  • 使用 prototype 原型对象解决构造函数的问题
  • 理解什么是原型(原型对象)
  • 构造函数、prototype 原型对象、实例对象 三者之间的关系
  • 实例对象读写原型对象
  • 属性成员搜索原则:原型链
  • 原型对象的简写形式
  • 原生对象的原型
  • 原型对象的问题及使用建议

3.1 构造函数的 prototype属性

JavaScript 的每个对象都继承另一个父级对象,父级对象称为 **原型 ** (prototype)对象。

原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享
通过构造函数生成实例对象时,会自动为实例对象分配原型对象。
而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象

null没有自己的原型对象。

这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在构造函数的 prototype 属性上,

也就是实例对象的原型对象上。

function Cat(color) {
  this.color = color;
}

Cat.prototype.name = "猫";
Cat.prototype.sayhello = function(){
    console.log('hello'+this.name,this.color);
}
Cat.prototype.saycolor = function (){
    console.log('hello'+this.color);
}

var cat1 = new Cat('白色'); 
var cat2 = new Cat('黑色'); 
cat1.sayhello();
cat2.saycolor();

这时所有实例的 name 属性和 sayhello()saycolor 方法,
其实都是同一个内存地址,指向构造函数的 prototype 属性,因此就提高了运行效率节省了内存空间。

3.2 构造函数、实例、原型三者之间的关系

1022041

构造函数的prototyp属性,就是由这个构造函数new出来的所有实例对象的 原型对象

前面已经讲过,每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数

对象._proto_ (两边都是两个下划线):获取对象的原型对象;

console.log(cat1.__proto__ == Cat.prototype); // true

注意:ES6标准规定,__proto__属性只有浏览器环境下才需要部署,其他环境可以不部署,因此不建议使用

3.3 原型对象的获取及修改

上节可以看到,想要获取一个实例对象的原型对象,有两种方式:

1:通过实例对象的构造函数的prototype属性获取: 实例对象.constructor.prototype

2:通过实例对象的 _proto_ 属性获取: 实例对象.__proto__

而这两种方式,我们都不建议使用:

obj.constructor.prototype在手动改变原型对象时,可能会失效。

function P() {};
var p1 = new P();

function C() {};
// 修改构造函数的prototype属性的值为p1
C.prototype = p1; //也就是说,此后所有有C构造函数得到的对象的原型对象都是p1;

var c1 = new C();

console.log(c1.constructor.prototype === p1) // false

推荐设置获取实例对象的原型的方式

Object.getPrototypeOf(实例对象) 方法返回一个对象的原型对象。

这是获取原型对象的标准方法。

function Cat(name, color) {
    this.name = name;
}
var cat1 = new Cat('猫'); //获取cat1对象的原型对象
var s = Object.getPrototypeOf(cat1); 
console.log(s);

Object.setPrototypeOf(实例对象,原型对象) 为现有对象设置原型对象
第一个是实例对象,第二个是要设置成为实例对象的原型对象的对象
这是设置原型对象的标准方法。

function Cat(name) {
    this.name = name;
}
var ob = {p:'波斯'};
var cat1 = new Cat('猫'); 
//设置cat1的原型对象为ob 
Object.setPrototypeOf(cat1,ob); 
console.log(cat1.p);//cat1的原型对象中有p属性 
console.log(Object.getPrototypeOf(cat1));
console.log(cat1.__proto__);
//注意:如果对象的原型被改变,不会影响构造函数获取的原型的结果
console.log(Cat.prototype == cat1.__proto__); //false

以上的两种方法,都是在ES6新标准中添加的;

重要图示

20180604-20014

3.4 原型及原型链

所有对象都有原型对象;

function Cat(name, color) {
    this.name = name;
 }

var cat1 = new Cat('猫');

console.log(cat1.__proto__.__proto__.__proto__);

而原型对象中的属性和方法,都可以被实例对象直接使用;

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性

  • 搜索首先从对象实例本身开始

  • 如果在实例中找到了具有给定名字的属性,则返回该属性的值

  • 如果没有找到,则继续搜索原型对象,在原型对象中查找具有给定名字的属性

  • 如果在原型对象中找到了这个属性,则返回该属性的值

  • 如果还是找不到,就到原型的原型去找,依次类推。

  • 如果直到最顶层的Object.prototype还是找不到,则返回undefined。

而这正是多个对象实例共享原型所保存的属性和方法的基本原理。

对象的属性和方法,有可能是定义在自身内,也有可能是定义在它的原型对象上。
由于原型本身也是对象,又有自己的原型,所以形成了一条 原型链(prototype chain)。

3.5 更简单的原型语法

我们注意到,前面例子中每添加一个属性和方法就要敲一遍 构造函数.prototype
为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象:

function Person (name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  type: 'human',
  sayHello: function () {
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

在该示例中,我们将 Person.prototype 重置到了一个新的对象。
这样做的好处就是为 Person.prototype 添加成员简单了,但是也会带来一个问题,那就是原型对象丢失了 constructor 成员(构造函数)。

所以,我们为了保持 constructor 的指向正确,建议的写法是:

function Person (name, age) {
  this.name = name
  this.age = age
}

Person.prototype = {
  // 将这个对象的构造函数指向Person
  //constructor: Person, // => 手动将 constructor 指向正确的构造函数
  type: 'human',
  sayHello: function () {
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

var p = new Person();

3.6 原生对象的原型

所有构造函数都有prototype属性;

  • Object.prototype
  • Function.prototype
  • Array.prototype
  • String.prototype
  • Number.prototype
  • Date.prototype
  • ……

为内置对象扩展原型方法:

例:

var ar = [1,5,23,15,5];
//获取数组中小于10的数
function f(){
    var minarr = [];
    this.forEach(function(v,k){
        if(v<10){
            minarr.push(v);
        }
    })
    return minarr;
}

Object.getPrototypeOf(ar).min10 = f;
console.log(ar.min10());//[1, 5, 5]

// 其他数组对象也具有相应的方法
var a = [1,2,34,7];
console.log(a.min10()); //[1, 2, 7]

这种技术被称为猴子补丁,并且会破坏封装。尽管一些流行的框架(如 Prototype.js)在使用该技术,但仍然没有足够好的理由使用附加的非标准方法来混入内置原型。

3.7 原型对象的问题及使用建议

性能问题:

在原型链上查找属性时是比较消耗资源的,对性能有副作用,这在性能要求苛刻的情况下很重要。

另外,试图访问不存在的属性时会遍历整个原型链

//声明构造函数Man
function Man(name){
    this.name = name;
    this.p = function(){
      console.log(this.name+'跑');
  }
}

var m = new Man('张三');

console.log(m.hasOwnProperty('name')); // true
console.log(m.hasOwnProperty('age')); //false

hasOwnProperty 是 JavaScript 中唯一处理属性并且不会遍历原型链的方法。

注意:检查属性是否undefined还不够。该属性可能存在,但其值恰好设置为undefined

//声明构造函数Man
function Man(name){
    this.name = name;
    this.n = undefined;
    this.p = function(){
      console.log(this.name+'跑');
  }
}

var m = new Man('张三');

if(m.n == undefined){
    console.log('没有n属性')
}

console.log(m.hasOwnProperty('name')); // true
console.log(m.hasOwnProperty('name')); // true
console.log(m.hasOwnProperty('n')); //true

第4章 继承

学习目标

  • 理解什么是继承
  • 原型继承

4.1 什么是继承

  • 现实生活中的继承
  • 程序中的继承

所谓的继承,其实就是在子类(子对象)能够使用父类(父对象)中的属性及方法;

赋予后辈调用祖辈资源的权限,就是继承;

4.2 原型链继承

//声明构造函数Run
function Run(){
  this.p = function(){
      console.log(this.name+'跑');
  }
}
//声明构造函数Man
function Man(name){
    this.name = name;
}
//设置构造函数Man的原型为Run,实现继承
Man.prototype = new Run();

var m = new Man('张三');

m.p();

但是,并不建议使用原型链继承,而且JS 中不止有原型链继承,还有其他的继承方式,后面会讲到;

第5章 函数进阶

学习目标

  • 函数的声明调用参数等基本语法
  • 理解作用域
  • 闭包
  • this的指向和使用
  • 改变this指向的方法

5.1 函数的声明及调用

关键字声明

function f1(){
    console.log('f1');
}

表达式声明

var f2 = function(){
    console.log('f2');
}

这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression)

构造函数方式声明

var add = new Function(
  'x',
  'y',
  'console.log( x + y )'
);
add(1,2);

上面代码与下面的代码等同;

// 等同于
function add(x, y) {
  console.log( x + y )
}
add(1,2);

因此,我们只关注前两种即可,后一种只做了解,不建议使用,这种声明函数的方式非常不直观,几乎无人使用;

那么,关键字声明表达式声明 有什么区别?

  • 关键字声明必须有名字
  • 关键字声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
  • 函数表达式类似于变量赋值
  • 函数表达式没有函数名字
  • 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用
// 先调用后声明
f1();
function f1(){
    console.log('f1');
}
//由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了
//因此可以正常调用

而表达式方式:

f();
var f = function (){};
// TypeError: undefined is not a function

上面的代码等同于下面的形式。

var f;
f();
f = function () {};

上面代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。

因此,如果同时采用function命令和表达式声明同一个函数,最后总是采用表达式的定义。

var f = function () {
  console.log('1');
}

function f() {
  console.log('2');
}

f() // 1

5.2 第一等公民

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为 第一等公民

函数作为参数

function eat (callback) {
  setTimeout(function () {
    console.log('吃完了')
    callback()
  }, 1000)
}

eat(function () {
  console.log('去唱歌')
})

函数作为返回值

function f1(){
    var s = 1;
    function f2(){
        console.log(s);
    }
    return f2;
}
var f = f1();
f();// 1

JS中一切皆对象,函数也是对象,后面还会讲到

5.3 参数及返回值

5.3.1 参数

形参和实参

// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部
// 带参数的函数声明
function 函数名(形参1, 形参2, 形参...){
  // 函数体
}

// 带参数的函数调用
函数名(实参1, 实参2, 实参3);

解释:

  1. 形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。
  2. 实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。
function fn(a, b) {
  console.log(a + b);
}
var x = 5, y = 6;
fn(x,y); 
//x,y实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b,函数内部的值是复制的新值,无法修改外部的x,y
var f = function (one) {
  console.log(one);
}
f(1, 2, 3)

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。

**arguments对象 **— 函数的实参参数集合

var f = function (one) {
  console.log(arguments);
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

f(1, 2, 3)
5.3.2 返回值

当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个返回值

//声明一个带返回值的函数
function 函数名(形参1, 形参2, 形参...){
  //函数体
  return 返回值;
}

//可以通过变量来接收这个返回值
var 变量 = 函数名(实参1, 实参2, 实参3);

函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。

返回值详解
如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined
如果函数使用 return语句,那么跟在return后面的值,就成了函数的返回值
如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined ,

​ 函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。

return后没有任何内容,可以用来调试代码。

5.3.3 递归

执行代码,查看执行结果:

function fn1 () {
  console.log(111)
  fn2()
  console.log('fn1')
}

function fn2 () {
  console.log(222)
  fn3()
  console.log('fn2')
}

function fn3 () {
  console.log(333)
  fn4()
  console.log('fn3')
}

function fn4 () {
  console.log(444)
  console.log('fn4')
}

fn1()
/*
** 执行结果为: 
111
222
333
444
fn4
fn3
fn2
fn1
*/

最简单的一句话介绍递归:函数内部自己调用自己

递归案例:

计算 1+2+3+…+100 的结果

function sum(n){
    if(n>1){
        return n + sum(n-1);
    }else{
        return 1; 
    }
}
var jieguo = sum(5);
console.log(jieguo);

递归必须要有判断条件,不加判断会死;

5.4 作用域

作用域(scope)指的是变量存在的范围。在 ES5 的规范中,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域(局部作用域),变量只在函数内部。

函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。

var v = 1;

function f() {
  console.log(v);
}

f()
// 1

在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。

function f(){
  var v = 1;
}

v // ReferenceError: v is not defined

注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。

5.4.1 作用域链

只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。

将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。

function f1() {
    var num = 123;
    function f2() {
        console.log( num );
    }
    f2();
}
var num = 456;
f1();

avaScriptzuoyongyulia

5.5 函数内部的变量声明的提升

与全局作用域一样,函数作用域内部也会产生“变量提升”现象。

var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

function foo() {
    console.log(y);//undefined
    var y = 'Bob';
} 
foo();

注意: JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

对于上述foo()函数,JavaScript引擎看到的代码相当于:

function foo() {
	var y; // 提升变量y的申明 
    var x = 'Hello, ' + y; 
    alert(x);
	y = 'Bob';
}

5.6 函数本身的作用域

var a = 1;
var x = function () {
  console.log(a);
};
function f() {
  var a = 2;
  x();
}
f() // 1 
// 讨论结果为什么是 1  ?

函数本身也是一个值,也有自己的作用域。
它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

var x = function () {
  console.log(a);
};
function y(f) {
	var a = 2;
	f(); 
}
y(x);// ReferenceError: a is not defined
function foo() {
    var x = 1;
    function bar() {
        console.log(x);
    }
	return bar; 
}
var x = 2;
var f = foo();
f() // 1

5.7 闭包

5.7.1 关于作用域的问题
var n = 999;
function f1() { 
    console.log(n);
}
f1() // 999

函数内部可以直接读取全局变量,函数 f1 可以读取全局变量 n。

但是,在函数外部无法读取函数内部声明的变量。

function f1() {
  var n = 99;
}
f1()
console.log(n);

有时我们却需要在函数外部访问函数内部的变量;
正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。

function f1() {
    var n = 999;
 
	var f2 = function() {
     	console.log(n);
	} 
	return f2;
}
var f = f1();
f();

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。
但是反过来就不行,f2内部的局部变量,对f1就是不可见的。

这就是JavaScript语言特有的”链式作用域”结构(chain scope),子级会一层一层地向上寻找所有父级的变量。
所以,父级的所有变量,对子级都是可见的,反之则不成立。

既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

闭包就是函数f2,即能够读取其他函数内部变量的函数。
由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,

因此可以把闭包简单理解成**“定义在一个函数内部的函数”**。
闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁;

闭包(closure)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

理解闭包,首先必须理解变量作用域。

5.7.2 关于JS垃圾回收机制的问题
function f1() { 
    var n = 99;
	console.log(++n);
}
f1(); //100 
f1(); //100

51115414

当我们在函数内部引入一个变量或函数时,系统都会开辟一块内存空间;还会将这块内存的引用计数器进行初始化,初始化值为0,如果外部有全局变量或程序引用了这块空间,则引用计数器会自动进行+1操作,当函数执行完毕后,变量计数器重新归零,系统会运行垃圾回收,将函数运行产生的数据销毁;如计数器不是 0 ,则不会清楚数据;

这个过程就称之为 “JS的垃圾回收机制” ;

如果将上节的代码,改为闭包形式:

function f1() {
    var n = 99;
    function f2(){
        console.log(++n);
	}
	return f2; 
}
var f = f1();
f(); //100
f(); //101

运行代码发现,函数调用一次,其变量 n 变化一次;

因 函数f1被调用时,返回的结果是f2函数体,也就是说,f2函数被当作值返回给f1的调用者,
但是f2函数并没有在此时被调用执行,
所以整个 f1 函数体,无法判断子函数f2会对其产生何种影响,无法判断 变量n是否会被使用;
即使f1函数被调用结束,整个f1函数始终保留在内存中,不会被垃圾回收机制回收;

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,
即闭包可以使得它诞生环境一直存在;

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。

因此不能滥用闭包,否则会造成网页的性能问题。

闭包小案例:缓存随机数

需求:获取随机数,随机数一旦生成,会在程序中多次使用,所以,在多次使用中不变;

function f1(){
    var num = parseInt(Math.random()*100)+1;
    return function (){
        console.log(num);
    }
}
var f2 = f1();
f2();
f2();
f2();

闭包点赞案例:

<ul>
  <li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  <li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  <li><img src="images/fj.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
  <li><img src="images/bd.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
</ul>
<script>
  function f1() {
    var value=1;
    return function () {
      this.value="赞("+(++value)+")";
    }
  }
  //获取所有的按钮

  var btnObjs=document.getElementsByTagName("input");
  for(var i=0;i<btnObjs.length;i++){
    var ff=f1();
    //将 闭包函数 绑定给事件
    btnObjs[i].onclick=ff;
  }
</script>

思考以下两段代码的运行结果:

var name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function () {
    return function () {
        console.log(this.name);
    };
  }
};

object.getNameFunc()() 
var name = "The Window";  
var object = {    
  name: "My Object",
  getNameFunc: function () {
    var that = this;
    return function () {
        console.log(that.name);
    };
  }
};
object.getNameFunc()(); 

通过分析代码得知,其中的 this 是关键,那么this到底是什么?

5.8 this 到底是谁

5.8.1 this的不同指向

this关键字是一个非常重要的语法点。毫不夸张地说,不理解它的含义,大部分开发任务都无法完成。

记住一点:this不管在什么地方使用:它永远指向一个对象

下面是一个实际的例子。

var person = {
  name: '张三',
  describe: function () {
    console.log('姓名:'+ this.name);
  }
};

person.describe()
// "姓名:张三"

上面代码中,this.name表示name属性所在的那个对象。由于this.namedescribe方法中调用,而describe方法所在的当前对象是person,因此this指向personthis.name就是person.name

由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。

var A = {
  name: '张三',
  describe: function () {
    console.log('姓名:'+ this.name);
  }
};

var B = {
  name: '李四'
};

B.describe = A.describe;
B.describe()
// "姓名:李四"

上面代码中,A.describe属性被赋给B,于是B.describe就表示describe方法所在的当前对象B,所以this.name就指向B.name

稍稍重构这个例子,this的动态指向就能看得更清楚。

function f() {
  console.log('姓名:'+ this.name);
}

var A = {
  name: '张三',
  describe: f
};

var B = {
  name: '李四',
  describe: f
};

A.describe() // "姓名:张三"
B.describe() // "姓名:李四"

上面代码中,函数f内部使用了this关键字,随着f所在的对象不同,this的指向也不同。

只要函数被赋给另一个变量,this的指向就会变。

var A = {
  name: '张三',
  describe: function () {
    console.log('姓名:'+ this.name);
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

上面代码中,A.describe被赋值给变量f,的内部this就会指向f运行时所在的对象(本例是顶层对象)。

案例:判断数值合法性

<input type="text" name="age" size=3 onChange="validate(this, 10, 20);">

<script>
function validate(obj, x, y){
  if ((obj.value < x) || (obj.value > y)){
    console.log('合法');
  }else{
    console.log('不合法');
  }
}
</script>

上面代码是一个文本输入框,每当用户输入一个值,鼠标离开焦点就会触发onChange事件,并执行validate回调函数,验证这个值是否在指定范围。浏览器会向回调函数传入当前对象,因此this就代表传入当前对象(即文本框),就然后可以从this.value上面读到用户输入的值。

JavaScript语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象下运行的,this就是函数运行时所在的对象(环境)。这本来并不会让我们糊涂,但是JavaScript支持运行环境动态切换,也就是说,this的指向是动态的,没有办法事先确定到底指向哪个对象,这才是最初初学者感到困惑的地方。

5.8.2 使用场合

(1)全局环境

全局环境使用this,它指的就是顶层对象window

this === window // true

function f() {
  console.log(this === window);
}
f() // true

(2)构造函数

构造函数中的this,指的是实例对象。

function F(){
    this.hello=function(){
        console.log('Hello'+this.name);
    }
}
var f1 = new F();
f1.name = '张三';
f1.hello();


var f2 = new F();
f2.name = '刘能';
f2.hello();

(3)对象的方法

方法在哪个对象下,this就指向哪个对象。

var o1 = {
    s1:'123',
    f1:function (){
        console.log(this.s1)
    }
}

var o2 = {
    s1:'456',
    f1:o1.f1
}

o2.f1();
5.8.3 使用this时的注意事项

(1) 避免包含多层this

var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }
    f2();
  }
}

o.f1()
// Object  
// Window  

如果要在内层函数中使用外层的this指向,一般的做法是:

var o = {
  f1: function () {
    console.log(this);
    var that = this;
    var f2 = function () {
      console.log(that);
    }
    f2();
  }
}

o.f1()
// Object  
// Object  

(2)不在循环数组中使用this

var ar = ['a','b','c'];
ar.forEach(function(v,k,ar){
    console.log(this[k])
})

this的动态切换,固然为JavaScript创造了巨大的灵活性,但也使得编程变得困难和模糊。

有时,需要把this固定下来,避免出现意想不到的情况;JavaScript提供了callapplybind这三个方法,来切换/固定this的指向。

5.9 call()方法、apply()方法、bind()方法

call()方法

var lisi = {names:'lisi'};
var zs = {names:'zhangsan'};
function f(age){
    console.log(this.names);
    console.log(age);
    
}
f(23);//undefined
f.call(zs,32);//zhangsan

call方法使用的语法规则

函数名称.call(obj,arg1,arg2…argN);

参数说明:

obj:函数内this要指向的对象,

arg1,arg2…argN :参数列表,参数与参数之间使用一个逗号隔开

apply()方法

函数名称.apply(obj,[arg1,arg2…,argN])

参数说明:

obj :this要指向的对象

[arg1,arg2…argN] : 参数列表,但是要求格式为数组

var lisi = {name:'lisi'}; 
var zs = {name:'zhangsan'}; 
function f(age,sex){
	console.log(this.name+age+sex); 
}
f.apply(zs,[23,'nan']);

bind()方法

bind方法用于将创建一个新的函数,且将新函数中的this绑定到具体的某个对象上

function foo() {
    console.log(this.a);
}
var obj2 = {
    a: 2,
};
// 创建新函数,并将新函数中的this固定的指向obj2对象;
var new_foo = foo.bind(obj2);

new_foo(); //2
foo();     //undefined

第6章 再谈 面向对象

学习目标:

  • 了解ES6中新的对象语法
  • 正确使用继承

6.1 对象

6.1.1 谁说JS没有类

在JS中,想要获取一个对象,有多种方式:

var o1 = {} var o2 = new Object()

自定义构造函数方式

function Point(x, y) {
  this.x = x;
  this.y = y;
  this.toString = function () {
  	return this.x + ', ' + this.y;
  };
}

var p = new Point(1, 2);
console.log(p.toString());

但是上面这种使用构造函数获取对象的写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。

通过class关键字,可以定义类。

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

上面的代码用 ES6 的class改写,就是下面这样。

//定义类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return  this.x + ', ' + this.y ;
  }
}
var p = new Point(1, 2);
console.log(p.toString());

上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法(后面还会讲到),而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的类Point

Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

ES6 的类,完全可以看作构造函数的另一种写法。

使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

类同样也有prototype属性,而属性的值依然是实例对象的原型对象;

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return  this.x + ', ' + this.y ;
  }
}
Point.prototype.toValue = function(){
    console.log('123');
}
var p = new Point(1, 2);
p.toValue();
console.log(p.toString());
6.1.2 constructor 方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

class Point {
}

// 等同于
class Point {
  constructor() {}
}

类必须使用new 进行实例化调用,否则会报错。

而类一旦实例化,constructor()方法就会被执行,就像 人一出生就会哭一样;

constructor方法默认返回实例对象(即this);

但是返回值完全可以指定返回另外一个对象;

var o1 = {
    f1:function(){
        console.log('f1');
    }
}
class Point{
    constructor (){
        return o1;
    }
    f2(){
        console.log('f2');
    }
}

var p = new Point();
p.f1(); // f1
p.f2(); //Uncaught TypeError: p.f2 is not a function

constructor方法默认返回值尽量不要修改,一旦修改,我们获取的对象将脱离其原型;

6.1.3 变量提升

我们知道,在JS中,不管是全局变量还是局部变量都存在变量提升的特性,函数也有提升的特性,也可以先调用后声明,构造函数也一样;如下面的代码,完全没问题:

var p = new Point();
p.f2();

function Point(){
    this.f2 = function(){
        console.log('f2');
    }
}

但是,需要注意: 类不存在变量提升(hoist),这一点与 ES5 完全不同。

new Man();
class Man{}

// Man is not defined

注意,class只是在原有面向对象的基础上新加的关键字而已,本质上依然没有改变JS面向对象方式;

6.2 再谈继承

6.2.1 原型链继承的问题
//声明构造函数Run
function Run(){
  this.p = function(){
      console.log(this.name+'跑');
  }}
//声明构造函数Man
function Man(name){
    this.name = name;
}
//设置构造函数Man的原型为Run,实现继承
Man.prototype = new Run();

var m = new Man('张三');

m.p();

// 由构造函数获取原型 
console.log(Man.prototype); // 函数对象 Run
//标准方法获取对象的原型 
console.log(Object.getPrototypeOf(m)); // 函数对象 Run
//获取对象的构造函数
console.log(m.constructor); // Run 函数

运行上面的代码,我们发现对象 m 本来是通过构造函数Man 得到的,可是,m 对象丢失了构造函数,并且原型链继承的方式,打破了原型链的完整性,不建议使用;

这个问题,在3.5章节也提到过,想解决也很简单,只要在父级中手动指定子级正确的构造函数即可:

修改上面的代码:

80512213213

6.2.2 冒充方式的继承

前面我们在学习JS面向中的面向对象编程时,谈到了继承;

所谓的继承,其实就是在子类(子对象)能够使用父类(父对象)中的属性及方法;

function f1(){
    this.color = '黑色';
    this.h = function(){
        console.log(this.color+this.sex);
    }
}

function F2(){
    this.sex = '铝';
    this.fn = f1;
}

var b = new F2();
b.fn();
b.h();

运行以上代码可知,由构造函数获取的对象 b可以调用函数f1中的属性及方法;

有运行结果可知,f1 函数中的this 实际指向了对象b ,对象b 实际上已经继承了f1

这种方式称为 **对象冒充 **方式继承,ES3之前的代码中经常会被使用,但是现在基本不使用了;

为什么不使用了呢?

还是要回到上面的代码,本质上讲,我们只是改变了函数f1 中this的指向,

f1中的this指向谁,谁就会继承f1;

而call和apply就是专门用来改变 函数中this指向的;

call或apply 实现继承

function Run(){
    this.p = function(){
        console.log('ppp');
    }
}
function Man(){
//将Run函数内部的this指向Man的实例化对象;
    Run.call(this);
}
var m = new Man();
m.p();

//获取对象的构造函数
console.log(m.constructor); // Man 函数
// 由构造函数获取原型 
console.log(Man.prototype); // 函数对象 Man
//标准方法获取对象的原型 
console.log(Object.getPrototypeOf(m)); // 函数对象 Man

call或apply 实现的继承依然是使用对象冒充方式实现, 此方式即实现了继承的功能,同时也不再出现原型继承中出现的问题;

6.2.4 Object.create() 创建实例对象及原型继承

构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。

var person1 = {
  name: '张三',
  age: 38,
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  }
};

var person2 = Object.create(person1);

person2.name // 张三
person2.greeting() // Hi! I'm 张三.

console.log(Object.getPrototypeOf(person2) == person1); //true

上面代码中,对象person1person2的模板,后者继承了前者所有的属性和方法;

Object.create 的本质就是创建对象;

var obj1 = Object.create({});
var obj2 = Object.create(Object.prototype);
var obj3 = new Object();

//下面三种方式生成的新对象是等价的。

如果想要生成一个不继承任何属性(比如没有toStringvalueOf方法)的对象,可以将Object.create的参数设为null

var obj = Object.create(null);

obj.valueOf()
// TypeError: Object [object Object] has no method 'valueOf'

使用Object.create方法的时候,必须提供对象原型,即参数不能为空,或者不是对象,否则会报错。

6.2.5 Class 的继承
class Run{
    p(){
        console.log('ppp');
    }
}
class Man extends Run{
}
var m = new Man();
m.p();

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

ES5 的继承,实质是先创造子类的实例对象,然后再将父类的方法添加到上面(Parent.call(this))。

ES6 的继承机制完全不同,是先创造父类的实例对象,然后再用子类的构造函数修改。

class Run{
    p(){
        console.log('ppp');
    }
}
class Man extends Run{
   	// 显式调用构造方法 
    constructor(){}
}
var m = new Man();
m.p();
// Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived

上面代码中,Man继承了父类Run,但是子类并没有先实例化Run,导致新建实例时报错。

class Run{
    p(){
        console.log('ppp');
    }
}
class Man extends Run{
    constructor(){
        // 调用父类实例(实现父类构造方法)
        super();
    }
}
var m = new Man();
m.p();

第7章 综合案例

整体思路:

先玩几次,思考大概的实现思路;

1:创建基本的静态页面;

2:让div动起来

3:动态创建Div

4:动起来后,填补缺失的div

5:随机创建黑块

6:绑定点击事件

7:点击判断输赢

8:游戏结束后的限制处理

9:黑块触底的处理

10:加分

11:加速

注意变量作用域和this指向的问题

insertBefore、firstChild、getComputedStyle、appendChild、createElement、Math.random、Math.floor

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #cont {
            margin-top:100px; 
            width: 400px;
            height: 400px; 
            border-top:1px solid blue; 
            position: relative; 
            /*隐藏顶部*/ 
            overflow: hidden;
        }
        #main {
            width: 400px;
            height: 400px; 
            /*去掉红框*/
            /* border:1px solid red; */
            position: relative; 
            top:-100px;
        }
        .row {
            height: 100px;
        }
        .row div {
            width: 98px;
            height: 98px;
            border:1px solid gray;
            float: left;
        }
        .black {
            background: black;
        }
    </style>
</head>
<body>
    <input type="text" id="fen" value="0" disabled>
    <div id="cont">
        <div id="main"></div>
    </div>
</body>

<script>
    function Youxi(){
        // 获取main节点对象
        this.main = document.getElementById('main');
        this.interval = ''; // 定时器
        this.over = false; // 有是否结束的标志
        this.sudu = 1; // 初始化下降速度

        // 创建DIV的方法
        this.cdiv = function(classNames){
            // 创建一个div节点对象
            var div = document.createElement('div');
            // 根据传入的值,创建不同class属性的div
            if(classNames){
                div.className = classNames;
            }
            return div;
        }

        //一次生成一行div
        this.crow = function(init){
            var row = this.cdiv('row');
            // 获取0-3的随机数
            var k = Math.floor(Math.random()*4)
            // 每行div根据随机数,随机设置一个黑块
            for(var i=0;i<4;i++){
                // 随机出现黑块
                if(i==k){
                    row.appendChild(this.cdiv('black'));
                }else{
                    row.appendChild(this.cdiv());
                }
            }
            return row;
        }

        // 初始化运行
        this.init = function(){
            // 循环创建4行,并添加到main中
            for(var i = 0;i<4;i++){
                var row = this.crow();
                this.main.appendChild(row);
            }
            // 绑定点击事件
            this.clicks();
            // 设置定时器,使DIV动起来
            this.interval = window.setInterval('start.move()' , 15);
        }
        
        // 绑定点击事件
        this.clicks = function(){
            // 因为在其他作用域中要使用本对象,
            // 防止this指向冲突,将this赋值给一个新的变量,在其他作用域中使用新的变量代替this
            var that = this;
            // 为整个main绑定点击事件
            this.main.onclick = function(ev){
                // 通过事件对象,获取具体点击的节点
                var focus = ev.target;
                // 如果游戏已经结束了
                if(that.over){
                    alert('别挣扎了,游戏已经结束了!');
                }
                // 如果点击的元素有值为black的class属性,
                // 证明用户点击的是黑色块
                else if(focus.className == 'black'){
                    // 获取文本框节点对象
                    var score = document.getElementById('fen');
                    // 将文本框的值获取并加1后重新复制
                    var sc = parseInt(score.value)+1;
                    score.value = sc;
                    // 将黑块变白
                    focus.className = '';
                    // 如果此行被点击过,给这一行发一个'同行证'
                    focus.parentNode.pass = true;
                    // 得分每增加5,下降速度提高0.5个像素点
                    if(sc%5 == 0){
                        that.sudu += 0.5;
                    }

                }else{
                    // 点击的不是黑块,结束游戏
                    window.clearInterval(that.interval);
                    // 游戏已经结束了
                    that.over = true;
                    alert('游戏已结束')
                }
            }
        }

        // 每调用一次 main 的top值加2像素,main就会向下移动2像素
        // 我们只需要不断调用move,就会让main不断下降
        this.move = function(){
            // 获取top值
            var t = getComputedStyle(this.main, null)['top'];
            var tops = parseInt(t);
            // 如果tops大于1,证明一行下降结束
            if(tops>1){
                // 如果此行没有通行证,游戏结束
                if(this.main.lastChild.pass==undefined){
                    window.clearInterval(this.interval);
                    // 游戏已经结束了
                    this.over = true;
                    alert('游戏已结束')
                }else{ // 如果有通行证
                    // 如果大于5行,删除最后一行
                    if(this.main.children.length>=5) {
                        this.main.removeChild(this.main.lastChild);
                    }
                }
                // 下降结束一行,则在最顶部增加一行,完成下降的连续性
                var row = this.crow();
                this.main.insertBefore(row,this.main.firstChild);
                // 并重新隐藏新加的一行
                this.main.style.top = '-100px';
            }else{
                // 定时器每调用一次,top 值修改一次
                // 完成下降动作
                this.main.style.top = tops + this.sudu +'px';
            }
        }
    }

    var start = new Youxi();
    start.init();
</script>
</html>


第0章 先谈ES5继承

继承:多个子类对象可以共用父类对象的成员属性和成员方法(代码重用–重复使用);

0.1 原型链继承

img

核心原理:B.prototype = new A();

能够继承A构造函数以及原型链上的所有成员。

	//定义父类构造函数
	function A(){
		this.age = 10;
	}
	A.prototype.say = function(){
		console.log(100);
	}
	//定义子类构造函数
	function B(){

	}
	//设置子类构造函数的原型对象 = 父类构造函数的实例对象
	B.prototype = new A();
	
	var b = new B();
	console.log(b.age);
	b.say();

缺点:子对象自身的constructor属性丢失了, 变成了父类构造函数

0.2 冒充继承

实现方式:在子类构造函数中,调用父类构造函数的(call, apply, bind)方法,使用子类构造函数的this去冒充父类构造函数的this。

父类.call(子类的对象, 其他参数); //意思是让子类的对象,去代替父类中的this。

	//父类构造函数
	function A(){
		this.age = 10;
		this.say = function(){
			console.log(100);
		}
	}
	//子类构造函数
	function B(){
      	//将B中的this,传给A,使A中的this指向B的this。
		A.call(this);
	}

	var b = new B();
	console.log(b.age);
	b.say();

缺点:这种实现继承的方式,是不能继承父类原型对象上的成员

	function A(){
		this.age = 10;
	}
	A.prototype.say = function(){
		console.log(100);
	}
	function B(){
		A.call(this);
	}
	var b = new B();
	console.log(b.age);
	b.say();//报错

0.3 Object.create()继承

Object.create()是IE9才开始支持的。

var 新对象 = Object.create(原型对象); 该方法就是用于创建新对象并指定原型对象的。所以就可以直接使用create方法实现继承。

特点:父类构造函数以及其原型链上的成员都能继承。

适合场景:新的对象 没有直接对应的一个自定义构造函数

不指定原型对象(不继承),参数可以为null。 即 var obj = Object.create(null);

	function A(){
		this.age = 10;
	}
	A.prototype.say = function(){
		console.log(100);
	}

	var obj = Object.create(new A());

	console.log(obj.age);
	obj.say();

	//或者
/*
	var obj = Object.create({
		age:10,
		say:function(){
			console.log(100);
		}
	});
	console.log(obj.age);
	obj.say();
*/

第1章 常量

ES5没有定义声明常量的方式,ES6标准中引入了新的关键字const来定义常量。

<script>
	const PI = 3.14;
	console.log(PI);
</script>

常量必须给初始值; 常量不能在同一作用域内重新定义或赋值;

<script>
  	//常量不能在同一作用域中重新定义
	const PI = 3.14;
	const PI = 3.1415; //报错
</script>
<script>
  	//常量不能在同一作用域中重新赋值
	const PI = 3.14;
	PI = 3.1415; //报错
</script>
<script>
  	//不同作用域中可以声明同名常量
	const PI = 3.14;
	console.log(PI);//3.14
	function fn(){
      	const PI = 3.1415;
      	console.log(PI);
	}
	fn();//3.1415
</script>

第2章 块级作用域

2.1 块级作用域

JS中作用域有:全局作用域、函数作用域。

ES6中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }就属于块作用域。(不包括函数)

//注意 块级作用域中,使用var声明的变量是全局变量
{
	var a = 1;
	console.log(a);//1
}
console.log(a);//1

if(true){
	var b = 2;
	console.log(b);//2
}
console.log(b);//2

2.2、let关键字声明块级变量

ES6中增加了let关键字声明变量,声明的变量只在当前代码块中生效(块级作用域)。

<script>
    if(true){
        let i=0;
        console.log(i);
    }
  console.log(i);//报错
</script>
<script>
    for(let i=0; i<=6; ++i){
        console.log(i);
    }
    console.log(i);//报错
</script>

使用let声明的变量可以重新赋值,但是不能在同一作用域内重新声明

<script>
  	// let声明的变量可以重新赋值
    {
        let a = 1;
      	console.log(a);
      	a = 2;
     	console.log(a);;
    }
</script>

<script>
  // let声明的变量不能在同一作用域重新声明,直接报错 预解析错误
  {
  	let a = 1;
  	console.log(a);
  	let a = 2;
  	console.log(a);
}
</script>

2.3、let变量没有变量提升

{
 	console.log(i);//报错
	let i = 8;
}

2.4、应用:let块级变量解决i丢失的问题

var arr = [3,4,5,6,7];

for(let i=0; i<arr.length; i++){
	// (function(i){
		setTimeout(function(){
			console.log(i);
			//console.log(arr[i]);
		}, 1000);
	// })(i);
}

第3章 字符串模板(模板字面量)

js中单双引号字符串,均不解析变量,需要使用+号将变量拼接在字符串中。

ES6中提供了字符串模板语法,允许使用反引号(倒引号) `` 来创建字符串,里面可以包含${变量名}形式的变量占位符。 其中的变量会被解析。

反引号字符串还可以换行

//生成一个随机数
var num=Math.random();

//将这个数字输出到console
console.log('your num is ' + num);
console.log(`your num is ${num}`);

var str = `hello
欢迎来到黑马大讲堂`;
console.log(str);

第4章 函数

4.1 参数默认值

ES5中定义函数时,不能指定参数的默认值。

ES6中定义函数时,可以指定参数的默认值。

//ES5中,只能变相实现参数默认值(函数内部加判断处理)
function f1(username){
	//传统的指定默认参数的方式
	var username = username || 'zhangsan';
	console.log('Hello ' + username);
}
f1();//Hello zhangsan
f1('lisi');//Hello lisi


//ES6中,直接给形参设置默认值
function f2(username='zhangsan'){
	console.log(`Hello ${username}`);
	//console.log('Hello ' + username);
}

f2();//Hello zhangsan
f2('lisi');//Hello lisi

4.2 展开运算符(拆包)

ES6新增了展开运算符(用三个连续的点 (...) 表示),能够将数组和字符串字面量展开为多个元素

//展开数组
var arr = [1, 2, 3];
console.log(arr); // [1, 2, 3]
console.log(...arr); // 1 2 3
//展开字符串
var str = "hello";
console.log(str);
console.log(...str);

应用:拓展参数

它允许传递数组或者类数组直接做为函数的参数。

//函数本来接收三个单独的参数
function f3(x,y,z){
	console.log(x,y,z);
}

//ES6中,我们可以将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数
var arr=[3,4,5];
f3(...arr);//输出:3 4 5 

//ES5中,如果需要传递数组当参数,我们需要使用函数的apply方法
f3.apply(null,arr);//输出:3 4 5 

4.3 不定参数(可变参数/剩余参数)

不定参数是指,在函数中使用 命名参数 同时接收 不定数量 的 未命名参数,需要使用三个连续的点 (...) 。

这是一种语法糖(在原语法上的简化用法),ES5通过函数内部的arguments对象来达到类似效果。

不定参数的格式:

//不定参数 将多个实参放在一个数组变量中
//  ...x  三个点是固定格式,x是形参变量名
function f1(...x){
	console.log(x);
}
f1(3,4,5); //[3,4,5]

function f2(m, n, ...x){
  	console.log(m, n, x);
}
f2(2,3,4,5,6); // m=2  n=3  x=[4,5,6]

第5章 解构(拆包)

在ES6中,可以使用解构从数组和对象提取值并赋值给独特的变量,即将数组或对象中的值,拆成一个一个变量。

解构:自动解析数组或对象中的值,并赋值给指定的变量。

5.1 数组解构

将数组中的值,取出并赋值给多个变量

	var arr = [3,4,5];
	var [a, b, c] = arr;
	console.log(a, b, c);
	
	//还可以忽略值 需要使用,占位
	var arr = [3,4,5];
	var [a,, c] = arr;
	console.log(a, c);

	//函数返回值为数组,进行解构
	function f5(){
		return [1,2,3];
	}
	var [a, b, c] = f5();
	console.log(a, b, c);

5.2 对象解构

将对象中的成员值,取出并赋值给多个变量(变量名与对象成员名一致)

var person = {
    "nickname": "老三",
    "age": 30,
    "sex": "男"
};
//解构时 {}中的变量名,不能加引号
var {nickname, age, sex} = person;
console.log(nickname, age, sex);
//可以忽略值  直接忽略 不需要占位
var {nickname, sex} = person;
console.log(nickname, sex);

5.3 函数参数默认值与解构

5.3.1 函数参数与解构

函数参数使用解构数组或解构对象形式

//1.函数形参,使用解构数组形式,调用函数时需要传递数组实参
function f1([x,y,z]){
  	console.log(x,y,z);
}
var arr = [1,2,3];
f1(arr);

//相当于
/*
function f1(a){
  	var [x,y,z] = a;
  	console.log(x,y,z);
}
var arr = [1,2,3];
f1(arr);
*/

//2.函数形参,使用解构对象形式,调用函数时需要传递对象实参
function f2({nickname,age,sex}){
  	//变量名与对象成员名一致
  	console.log(nickname,age,sex);
}
var obj = {"nickname":"zhangsan", "age":40, "sex":"男"};
f2(obj);

//相当于
/*
function f1(a){
  	var {nickname, age, sex} = a;
  	console.log(nickname,age,sex);
}
var obj = {"nickname":"zhangsan", "age":40, "sex":"男"};
f2(obj);
*/
5.3.2 默认值与解构数组

函数参数使用解构数组 并设置默认值

<script>
//1.函数参数使用解构数组,调用函数不传参数会报错
function fn([x, y, z]){
  	console.log(x, y, z);
}
fn(); //会报错
</script>

<script>
//2.函数参数使用解构数组,对整个数组设置默认值为空数组
function f1([x, y, z] = []){
  	console.log(x, y, z);
}
f1(); //不报错  x y z 都是 undefined

//3.函数参数使用解构数组,对整个数组设置默认值,数组中每个变量对应一个默认值
function f2([x, y, z] = [1,2,3]){
  	console.log(x, y, z);
}
f2(); //不报错  x=1 y=2 z=3
f2([4,5,6]); // x=4 y=5 z=6


//4.函数参数使用解构数组,对整个数组设置默认值为空数组, 在解构数组中对每个变量设置一个默认值
function f3([x=1, y=2, z=3]=[]){
  	console.log(x, y, z);
}
f3(); //不报错  x=1 y=2 z=3
f3([4,5,6]); // x=4 y=5 z=6
</script>
5.3.3 默认值与解构对象

函数参数使用解构对象 并设置默认值

<script>
//1.函数参数使用解构对象,调用函数不传参数会报错
function fn({x, y, z}){
  	console.log(x, y, z);
}
fn(); //会报错
</script>

<script>
//2.函数参数使用解构对象,对整个对象设置默认值为空对象
function f1({x, y, z} = {}){
  	console.log(x, y, z);
}
f1(); //不报错  x y z 都是 undefined

//3.函数参数使用解构对象,对整个对象设置默认值,对象中每个变量对应一个默认值
function f2({x, y, z} = {"x":1,"y":2,"z":3}){
  	console.log(x, y, z);
}
f2(); //不报错  x=1 y=2 z=3
f2({"x":4,"y":5,"z":6}); // x=4 y=5 z=6


//4.函数参数使用解构对象,对整个对象设置默认值为空对象, 在解构对象中对每个变量设置一个默认值
function f3({x=1, y=2, z=3}={}){
  	console.log(x, y, z);
}
f3(); //不报错  x=1 y=2 z=3
f3({"x":4,"y":5,"z":6}); // x=4 y=5 z=6
</script>

第6章 简化的(增强的)对象字面量

ES5中的对象字面量

var person = {
    "nickname": "老三",
    "age": 30,
    "sex": "男",
    "say":function(){
          return "hello";
    }
};

6.1 成员属性

//如果成员属性值,是放在变量中的值, 且属性名称与变量名称一致
var nickname = "老三";
var age = 30;
var sex = "男";
// 预期 {"nickname":"老三", "age":30, "sex":"男"}
//对象字面量可简写如下
var person = {
    nickname,
    age,
    sex
};
console.log(person);

6.2 成员方法

成员方法 可省略 function 关键字

// 预期 {"nickname":"老三", "age":30, "sex":"男", "say":function(){return "hello";}}
var person = {
    "nickname":"老三",
    "age":30,
  	"sex":"男",
 	say(){
      return "hello";
 	}
};
console.log(person);
console.log(person.say());//hello

6.3 原型对象

可以在对象字面量里面定义原型

var person = {
 	say(){
      return "hello";
 	}
};
var coder = {
  	__proto__:person,
  	coding(){
      	return "I'm coding";
  	}
};
console.log( coder.say() );
console.log( coder.coding() );

第7章 for of值遍历

ES6新增了for...of循环语法。

//遍历数组
var team = ["师父", "大师兄", "二师兄", "沙师弟", "小白龙"];
for(var v of team){
  	console.log(v);
}
//也可以遍历字符串
var str = "zhangsan";
for(var v of str){
  	console.log(v);
}
//for of不能遍历对象{} 
var person = {"nickname":"老三", "age":30, "sex":"男"};
for(var v of person){	//报错
  	console.log(v);
}

第8章 Symbol数据类型

ES5数据类型:6种: string number boolean null undefined object

ES6新增了一种数据类型:Symbol,表示独一无二的值,Symbol最大的用途是用来定义对象的唯一属性名。

Symbol值通过Symbol函数生成。

var symbol1 = Symbol();

var symbol2 = Symbol("Alice");

console.log(symbol1, symbol2) // 输出:Symbol() Symbol(Alice)

typeof运算符用于Symbol类型值,返回symbol。

console.log(typeof Symbol("Alice")) // 输出:symbol

Symbol类型的值是一个独一无二的值,Symbol函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。

console.log(Symbol() == Symbol()); // 输出:false

console.log(Symbol("Alice") == Symbol("Alice")); // 输出:false

应用:作为对象属性名的Symbol

var attr_name = Symbol();
var obj = {
	[attr_name]: "Alice"
};
console.log(obj[attr_name]);

var obj = {
  	[Symbol()]:"Alice"
};
console.log(obj);

注:Symbol值作为对象属性名时,不能用点运算符。由于点运算符后面总是字符串,所以不会读取attr_name作为标识名所指代的那个值。

使用[]方括号,里面的attr_name不带引号,表示attr_name是一个变量.

第9章 类和对象

1、类的定义

ES6中添加了对类的支持,引入了class关键字

ES6中提供的类实际上只是JS原型模式的包装。现在提供class支持后,对象的创建、继承更加直观。

class类中,可以包含 构造方法、实例方法、静态方法。

//类的定义
class A {
	//ES6中的构造方法(类的属性,定义在构造方法中)
    constructor(name) {
        this.name = name;
        this.age = 30;
    }
    //实例方法
    say() {
        console.log('我是A中的实例方法say,我的名字是 '+this.name);
    }
  	//静态方法(静态方法与实例方法 同名互不影响)
 	static say(){
      	console.log("我是A中的静态方法say");
 	}
}

//直接调用静态方法
A.say();

//实例化类 调用实例方法
var a = new A('Tom');
a.say();

//类也有原型对象
console.log(A.prototype);
console.log(a.__proto__)

注意

1.class类中不能直接定义属性,只能定义方法,方法之间不需要也不能使用逗号隔开

2.类只能先定义,再使用,没有提升效果。

3.静态方法只能通过类名直接调用,实例方法只能将实例化成对象后调用。

2、类的继承

注:父类有构造函数,子类构造函数中,需要调用super() 实现父类的构造函数,否则报错。

//类的继承
//父类A
class A {
	//ES6中的构造方法
    constructor(name) {
        this.name = name;
        this.age = 30;
    }
    //实例方法
    say() {
        console.log('我是A中的实例方法say,我的名字是 '+this.name);
    }
  	//静态方法
 	static say(){
      	console.log("我是A中的静态方法say");
 	}
}

//子类B
class B extends A {
  	//构造方法
    constructor(name) {
    	//使用函数形式的super(), 直接调用父类构造方法
      	//只要子类写了构造方法,就必须调用super(),且必须在使用this之前
        super(name);
    }
  	//实例方法
  	//子类方法 会覆盖父类同名方法
    say() {
      	//子类方法中,可以使用super.方法() 调用父类的非构造方法
      	//当前方法是实例方法,则调用父类的实例方法
      	//当前方法是静态方法,则调用父类的静态方法
      	//super.say(); 
        console.log('我是B中的实例方法say,我的名字是 '+this.name);
    }
  	
    static coding() {
        console.log('我是B中的静态方法coding');
    }
  	
}
//调用静态方法
B.say(); //A的静态方法
B.coding();//B自己的静态方法

//调用实例方法
var b = new B('Lucy');
b.say();//B中的实例方法say  //如果B中没有,才调用A的实例方法say
b.coding();//报错,B和A中都没有实例方法coding  (只有静态方法)

第10章 箭头函数

ES6可以使用“箭头”(=>)定义函数,注意是普通函数,不要使用这种方式定义类(构造器)。

10.1 语法

1.具有一个参数并直接返回的函数

var f1 = a=>a;
//相当于  var f1 = function(a){ return a;};
console.log(f1('hello'));//'hello'

2.没有参数的需要用在箭头前加上小括号

var f2 = () => '来了老弟';
console.log(f2());

3.多个参数需要用到小括号,参数间逗号间隔

var f3 = (a, b) => a+b;
console.log(f3(3,4));//7

4.函数体多条语句需要用到大括号

var f4 = (a, b) => {
  	console.log('来了老弟');
  	return a+b;
}
console.log(f4(5,6));//11

5.返回对象时需要用小括号包起来,因为大括号被占用解释为代码块

var f5 = () => {
  	return ({"name":"老弟", "age":40});
}

//var f5 = () => ({"name":"老弟", "age":40});
console.log(f5());

6.直接作为事件处理函数

<input type="button" value="点击" id="btn">
<script>
	document.getElementById('btn').onclick = evt=>{
      	console.log(evt);//evt 事件对象
	}
</script>

7.赋值为对象的方法

var obj = {};
obj.say = ()=>{return "hello,我是obj的say方法";}
console.log(obj.say());

8.作为回调函数

var f6 = (f)=>{
    console.log(f(100));
};
// f6(a=>a);
var f7 = a=>a;
f6(f7);

10.2 注意点

  1. typeof 判断箭头函数 结果为function
var f1 = a=>a;
console.log(typeof f1);//'function'
  1. instanceof判断是否Function的实例,结果为true
var f1 = a=>a;
console.log(f1 instanceof Function);//true
  1. 箭头函数不绑定this, 内外this指向固定不变

这个很有用,再不用写me,self,_this了,或者bind。

var obj = {
  	say:function(){
      	//非箭头函数
      	var _this = this;
      	var f1 = function(){
      		console.log(_this);//obj
			console.log(this);//window
        };
      	f1();
      	//箭头函数
        var f2 = ()=>{
			console.log(this);
        };
      	f2();
  	}
};
obj.say();
  1. 箭头函数不能做构造函数,不能用new实例化,也没有prototype属性
var Person = ()=>{};
console.log(Person.prototype);//undefined
var p = new Person();//报错
  1. 不能使用arguments
var f1 = ()=>{
	console.log(arguments);
};
f1(); //报错

6.箭头函数也支持默认参数、剩余参数、解构

var f1 = (x=1,y)=>{
  	console.log(x, y); //3 4
};
f1(3,4);
var f2 = (...x)=>{
  	console.log(x); //[3,4]
};
f2(3,4);
var f3 = ([x,y]=[])=>{
  	console.log(x, y); //3 4
};
f3([3,4]);


第1章 什么是正则表达式

1、概述

正则表达式(regular expression)

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来对字符串的进行匹配。

用途:

检查一个字符串中是否含有某种子串

将匹配的某种子串做替换

从某个字符串中取出符合某个条件的子串等。

“996.icu”

1、判断有没有99

2、判断有没有小数点.

3、icu 替换为 tsc // ‘996.icu’.replace(/icu/, ‘tsc’)

4、获取其中的9 和 u

2、应用场景

(1)表单验证里面,验证字符的合法性,如邮箱是否合法,手机号是否合法等等。

(2)信息过滤,如论坛帖子或者评论中的非法字符,例如 sql注入、js脚本注入、煽动性的言论。

(3)信息采集,采集别人网站上面的内容,例如整页采集时 筛选出需求的部分

(4)信息替换。

(5)页面伪静态的规则(url重写)。

3、入门案例

	//匹配str字符串中p是否存在
	var str = 'php';
	var result = str.match(/p/);
	//var result = str.match(/参数就是正则表达式/); //返回包含匹配结果的数组或者null

其中,正则表达式前后的 斜杠/ 叫做定界符,是固定格式。

	//匹配str字符串中所有p
	var str = 'php';
	var result = str.match(/p/g);//进行全局匹配,匹配所有

g是global,表示全局的意思,它是正则表达式语法中的修饰符,修饰符应该放到定界符(/)的后面。

第2章 正则语法-元字符

正则表达式中的字符:

元字符:一些具有特殊含义的特殊符号。

普通字符:包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

正则表达式三步走

① 匹配符(查什么)(等价符、字符簇、修饰符、转义符)

② 限定符(查多少)

③ 定位符(从哪查)

1、限定符

限定符(量词)用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。

*匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
+匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
?匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1}。
{n}n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,}n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。
{n,m}m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “foooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。
	var str = 'phpphp';
	var res = str.match(/p+/g);//匹配所有的一个p或多个连在一起的p
	console.log(res);//["p", "pp", "p"]

贪婪匹配与非贪婪匹配:

默认情况下,正则表达式执行贪婪匹配(尽可能取多的情况)

非贪婪匹配:相对于贪婪匹配来说的。设置方式:将?加在其他限定符之后。

	var str = 'phpphp';

	var res = str.match(/p+?/g);//+后面有? 执行非贪婪匹配(最少匹配)

	console.log(res);//["p", "p", "p", "p"]

非贪婪匹配使用场景举例:

abc

匹配div2标签

2、等价符

.匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像"(.|\n)"的模式。
\d匹配一个数字字符。等价于 [0-9]。
\D匹配一个非数字字符。等价于 [ ^0-9]。
\w匹配一个字母、数字、下划线。等价于[A-Za-z0-9_]。
\W匹配非(字母、数字、下划线)。等价于 [ ^A-Za-z0-9_]。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S匹配任何非空白字符。等价于 [ ^ \f\n\r\t\v]。
\n匹配一个换行符。等价于 \x0a 和 \cJ。
\r匹配一个回车符。等价于 \x0d 和 \cM。
\t匹配一个制表符。等价于 \x09 和 \cI。
常用的:  .  \d  \w
var str = 'php1js22';
var res = str.match(/\d+/g);//匹配所有的数字
console.log(res);//["1", "22"]

3、定位符

定位符用来描述字符串或单词的边界,^$ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。

^匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。
$匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。
\b匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
\B匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
注意: 通常在表单数据验证时,严格检测字符串格式,需要使用^$
<script>
	var str = '1234';
	var res = str.match(/^\d+$/);//匹配整个数字字符串(匹配id参数值)
	console.log(res);//["1234"]
</script>

4、字符簇(方括号)

方括号表示一个范围,也称为字符簇,匹配满足条件的一个字符。

[xyz]字符集合。匹配所包含的任意一个字符。例如, [abc]可以匹配 “plain” 中的 ‘a’。
[^xyz]负值字符集合。匹配未包含的任意字符。例如, [ ^abc] 可以匹配 “plain” 中的’p’、‘l’、‘i’、‘n’。
[a-z]字符范围。匹配指定范围内的任意字符。例如, [a-z] 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。
[^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,[ ^a-z]可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。
x|y或的用法:匹配 x 或 y。例如,‘z|food’ 能匹配 “z” 或 “food”。‘(z|f)ood’ 则匹配 “zood” 或 “food”。

字符范围 参考ASCII码表

示例:

[0-9] 查找任何从 0 至 9 的一个数字。

[a-z] 查找任何从小写 a 到小写 z 的字符。

[A-Z] 查找任何从大写 A 到大写 Z 的字符。

[A-z] 查找任何从大写 A 到小写 z 的字符。包括[ \ ]^_`等六个字符。

[A-Za-z]查找任何从大写 A 到小写 z 的字符,不包括[ \ ]^_`等六个字符。

var str = 'adcd1234ABCD';
var res = str.match(/[a-z]+/g);//匹配所有小写字母字符串
console.log(res);//["abcd"] 

5、修饰符

修饰符的用法,修饰符一定要写到正则表达式末尾/之后,可以一次性使用多个修饰符。

i 执行对大小写不敏感的匹配。实际上就是不区分大小写的匹配(默认区分大小写)

g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。

m 执行多行匹配(^和$能匹配每行的开始与结束)。

/i用法示例:

var str = 'adcd1234ABCD';
var res = str.match(/[a-z]+/gi);//匹配所有字母字符串(忽略大小写)
console.log(res);//["adcd", "ABCD"]

/m用法示例

	var str = '1234\r\n5678';
	var res = str.match(/^\d+$/gm);//匹配整个数字字符串
	console.log(res);//["1234", "5678"]

6、转义字符(\)

\将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,‘n’ 匹配字符 “n”。‘\n’ 匹配一个换行符。序列 '\ \ ’ 匹配 "\ " 而 “\ (” 则匹配 “(”。

如果匹配的字符串在正则中有特殊含义的都必须加转义字符。如[]$.*?+|^{}()

但是不要乱加转义。

	var str = 'php.php';//目标字符串中包含普通字符.
	var res = str.match(/p\.p/g);//匹配普通字符. 需要转义
	console.log(res);//["p.p"]

匹配图片名称

	var str = '123.jpg';//目标字符串中包含普通字符.
	var res = str.match(/.*\.jpg/g);//匹配jpg后缀的图片名称
	console.log(res);//["123.jpg"]

练习题:

匹配字符串开始和结束的数字 预期结果 123 789; ^ \d | $

var str = '123abc456def789';
var res = str.match(/(^\d+)|(\d+$)/g);//匹配开始或结束的数字
console.log(res);//["123","789"]

第3章 js中正则对象

正则对象两种定义方式:js中正则表达式两边不用加引号

	var pattern = /[a-z]/;//将正则表达式直接当做对象使用。
	var pattern = new RegExp(/[a-z]/);//实例化RegExp对象

1、test方法

test方法检测目标字符串和正则表达式是否匹配,如果匹配返回true,不匹配返回false。

正则表达式中,一般不需要加全局修饰符g。

	var str = 'php1js22';
	var pattern = /^[a-z]/;//以小写字母开头
	var res = pattern.test(str);//匹配字符串是否以小写字母开头
	console.log(res);//true

2、exec方法

exec方法执行一个正则匹配,只匹配一次,匹配到结果就返回一个数组类型的结果,匹配不到就返回null。

正则表达式中,一般不需要加全局修饰符g。

即使正则表达式中,使用了全局匹配修饰符g,也只匹配一次。

	var str = 'php1js22';
	var pattern = /[a-z]/;//匹配小写字母字符串
	//var pattern = /[a-z]/g;//全局匹配小写字母字符串
	var res1 = pattern.exec(str);
	console.log(res1);//["p"] 只匹配一次,返回数组

3、lastIndex属性

表示正则表达式,上一次匹配结束后的位置(目标字符串中的索引),即下一次匹配开始的位置。

lastIndex是一个整数,。没有更多匹配重置lastIndex为0.

test 方法和exec方法默认都只匹配一次。匹配后lastIndex 被自动重置为0.

特殊情况:如果正则表达式使用了全局匹配修饰符g,则lastIndex不会被重置为0.

	var str = 'php1js22';
	//var pattern = /[a-z]/;//匹配小写字母字符串
	var pattern = /[a-z]/g;//全局匹配小写字母字符串
	var res1 = pattern.exec(str);
	console.log(res1);//["p"] 只匹配一次,返回数组
	console.log(pattern.lastIndex);//1  下一次匹配开始的位置
	var res2 = pattern.exec(str);
	console.log(res2);//["h"] //从位置1开始匹配

案例一

1、检测手机号格式

手机号特点:11位,纯数字,1开头, [23位号段,具体咨询各大运营商]

简单版(限制前两位):1开头,第二位3-9,后面9位数字

/^1[3-9]\d{9}$/

精确版(限制前三位):(如果出现新的手机号段,则不适用,需更新)

示例:

13[0-9]开头、14[579]开头、15[0-3,5-9]开头、166开头、17[0135678]开头、18[0-9]开头、19[89]开头

/^1(3[0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|8[0-9]|9[89])\d{8}$/

手机号检测案例:register.html

img

2、检测邮箱格式

邮箱特点:

2342.3s_-df@sina.com.cn

通用的邮箱的特点:

简单版:必须有@ 正则 /@/

精确版:

​ 必须有@和.

​ @前面是邮箱名,要求至少一个字符,要求是数字、字母、下划线、[还可以用.-],但是开头必须是数字字母下划线

​ @和点之间:是一个域名,要求至少一个字符,可以是数字字母中横线,要求开头是数字或字母

​ 点后面:要求是至少一个字符,必须是字母

​ 点xxx,这部分可以出现一次,也可以出现多次

最后一个点后面,字符长度为2-6的字母

var pattern = /^\w[\w\.-]*@[0-9a-z][0-9a-z-]*(\.[a-z]+)*\.[a-z]{2,6}$/i;

register-email.html

img

第4章 分组/捕获和反向引用

捕获和反向引用的语法的解释

子表达式
在正则表达式中,通过一对圆括号括起来的内容,我们就称之为“子表达式”。如:
var reg = /\d(\d)\d/gi;

捕获(分组)
在正则表达式中,子表达式匹配到相应的内容时,系统会自动捕获这个行为,
然后将子表达式匹配到的内容放入系统的缓存区中。我们把这个过程就称之为“捕获”。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-48gBgi3r-1669802332086)(http://oss.jakblog.com/img/reg/03-1.png)]

反向引用
在正则表达式中,我们可以使用\n(n>0,正整数,代表系统中的缓冲区编号)
来获取缓冲区中的内容,我们把这个过程就称之为“反向引用”。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dmZwEVN-1669802332086)(http://oss.jakblog.com/img/reg/03-2.png)]

在正则语法中,用“\1”来引用前面的捕获(使用子表达式匹配的结果)。用\2表示第二个捕获的内容….

在正则语法外(如replace时),用“$1”来引用前面的捕获。

	var str = '1122 3434 5566 7879 9887';
	//匹配连续四个数字,第一和第二数字相同,第三和第四数字相同
	var res = str.match(/(\d)\1(\d)\2/g);
	console.log(res);
	//匹配连续四个数字,第一和第三数字相同,第二和第四数字相同
	var res = str.match(/(\d)(\d)\1\2/g);
	console.log(res);
	//匹配连续四个数字,第一和第三数字相同
	var res = str.match(/(\d)\d\1\d/g);
	console.log(res);
	//匹配连续四个数字,第一和第二数字相同,第三和第四数字相同,并将相同的数字只保留一个
	var res = str.replace(/(\d)\1(\d)\2/g, '$1$2');
	console.log(res);

禁止引用

(?:正则) 这个小括号中的内容不能够被引用

​ //第一次捕获禁止引用

	var str = '1122 3434 5566 7879 9887';
	var res = str.match(/(?:\d)(\d)\1\d/g);
	console.log(res);

第5章 匹配中文(utf-8编码)

每个字符(中文、英文字母、数字、各种符号、拉丁文、韩文、日文等)都对应着一个Unicode编码。

查看Unicode编码,找到中文的部分,然后获取中文的Unicode编码的区间,就可以用正则匹配了。

前面我们用[a-z]表示小写字母,[0-9]表示数字,这就是一个范围表示,如果有一个数x能够表示第一个中文,有一个数y能够表示最后一个中文,那么[x-y]就可以表示所有的中文了。

中文的Unicode编码从4E00开始,到9FA5结束。

[\u4E00-\u9FA5]这个区间就能够表示中文。

完整的Unicode编码表http://blog.csdn.net/hherima/article/details/9045861

	var str = "你好,世界";
	var res = str.match(/[\u4E00-\u9FA5]/g);
	console.log(res);//["你", "好", "世", "界"]

案例二

解决结巴程序

把“今今今天晚晚晚晚晚晚上吃吃吃吃吃吃鸡”字符串换成单字的形式,即“今天晚上吃鸡”;

核心思想:匹配到重复的字符时,保留一个。

	var str = "今今今天晚晚晚晚晚晚上吃吃吃吃吃吃鸡";
	//今天晚上吃鸡
	var res = str.replace(/([\u4E00-\u9FA5])\1+/g, '$1');
	console.log(res);

第6章 环视

也叫 预查、断言、零宽断言。

正则表达式中,用于查找某些内容之前或者之后的东西,叫做环视。

环视通常也叫做预查、断言或者零宽断言。

1、正向肯定预查

也叫 顺序肯定环视

every(?=n) 匹配任何其后紧接指定字符串 n 的字符串。

	//匹配后面字符为10的win
	var str = "win7 win8 win10";
	var res = str.match(/win(?=10)/g);
	console.log(res); //["win"]

2、正向否定预查

也叫 顺序否定环视

every(?!n) 匹配任何其后没有紧接指定字符串 n 的字符串。

	//匹配后面字符不为10的win
	var str = "win7 win8 win10";
	var res = str.match(/win(?!10)/g);
	console.log(res); //["win", "win"]

(?!B)[A-Z]这种写法,其实它是[A-Z]范围里,排除B的意思,前置的(?!B)只是对后面数据的一个限定,从而达到过滤匹配的效果。

	var str = "abcd1234";
	var res = str.match(/(?!c)[a-z]/g);
	console.log(res); //["a", "b", "d"]

练习:从一堆图片地址中,找出符合条件的图片地址。

	var arr = [
		'img/20181013/a.jpg',
		'img/20181014/b.png',
		'image/20181014/a.jpg',
		'image/20181013/b.png',
		'20181013/c.png'
	];

匹配img开头的图片地址

/^img.*(jpg|png|gif)$/g

匹配不以image开头的图片地址

/^(?!image).*(jpg|png|gif)$/

img

第7章 String 对象的正则方法

1、match方法

stringObj.match(regex)

在字符串中检索匹配正则表达式regex的子串;

如果匹配,返回包含匹配结果的一个数组;不匹配返回null。

正则表达式regex中不带全局修饰符g,则只匹配一次。

正则表达式regex中带全局修饰符g,则匹配所有结果。

	var str = "1234@qq.com";
	var res = str.match(/[@\.]/g); // .要加转义
	console.log(res); //["@", "."]

2、replace方法

stringObj.replace(regex, replacement)

在字符串中检索匹配正则表达式regex的子串,并替换为指定的字符串replacement;

返回替换之后的新字符串。

正则表达式regex中不带全局修饰符g,则只匹配一次并替换一次。

正则表达式regex中带全局修饰符g,则匹配所有结果并替换所有结果。

替换的时候,使用"$1"表示匹配的第一个子表达式:

用$2表示第二个子表达式,以此类推。

3、search方法

stringObj.search(regex)

在字符串中搜索符合正则表达式的结果。如果找到结果返回结果的起始位置,停止向后检索,也就是说忽略全局标识符g;如果没有匹配结果,返回-1.

	var str = "1234@qq.com";
	var res = str.search(/[@\.]/); // .要加转义
	console.log(res); //4

4、split方法

stringObj.split(regex)

把一个字符串分割成字符串数组, 返回一个数组

	var str = "1234@qq.com";
	var res = str.split(/[@\.]/);// .要加转义
	console.log(res); //["1234", "qq", "com"]

jQuery

第1章 jQuery简介

1.1 JavaScript库的概念

JavaScript开发的过程中,处理浏览器的兼容很复杂而且很耗时,于是一些封装了这些操作的库应运而生。这些库还会把一些常用的代码进行封装。

把一些常用到的方法写到一个单独的js文件,使用的时候直接去引用这js文件就可以了。(animate.js、common.js)

常见的JavaScript 库 - jQuery、Prototype、MooTools。其中jQuery是最常用的一个

jQuery其实就是一个js文件,里面封装了一大堆的方法方便我们的开发,其实就是一个加强版的common.js,因此我们学习jQuery,其实就是学习jQuery这个js文件中封装的一大堆方法。

1.2 jQuery的优点好处

jQuery设计的宗旨是'Write Less,Do More',即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的操作,优化HTML文档操作、事件处理、动画设计和Ajax交互。
jQuery的核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。jQuery兼容各种主流浏览器。
极大地简化了 JavaScript 编程。

1.3 jQuery的版本

jQuery版本有很多,分为1.x 2.x 3.x

1.x版本:能够兼容IE678浏览器
2.x版本:不兼容IE678浏览器
1.x和2.x版本jquery都不再更新版本了,现在只更新3.x版本。
3.x版本:不兼容IE678,更加的精简(在国内不流行,因为国内使用jQuery的主要目的就是兼容IE678)

国内多数网站还在使用1.x的版本

[jQuery官网](http:// jquery.com)

1.4 体验jQuery

案例:显示与设置内容

	<input type="button" value="显示" id="btn1">
	<input type="button" value="结束" id="btn2">

    <div>请欣赏动画</div>
	<div>千呼万唤始出来</div>
	<img src="img/1.jpg" style="display:none">
	<img src="img/2.jpg" style="display:none">
        
	<script type="text/javascript" src="jquery.js"></script>
	<script>
		$(document).ready(function () {
		    $('#btn1').click(function () {
		        $('img').show(2000);
		    });

		    $('#btn2').click(function () {
		        $('div').text('下集再见');
		        $('img').hide(2000);
		    });
		});
	</script>

优点总结:

-查找元素的方法多种多样,非常灵活
-拥有隐式迭代(自动循环遍历)特性,因此不再需要手写for循环了。
-完全没有兼容性问题。
-实现动画非常简单,而且功能更加的强大。
-代码简单、粗暴。

1.5 jQuery中顶级对象

jQuery中的顶级对象是$或jQuery

用于:
获取jQuery对象
入口函数(页面加载事件)
高级功能

注意:jQuery中的$和JQuery关键字本身为同一对象;

$ 可以认为就是 一个特殊构造函数  ; 可以使用$(选择器) 方式调用$,得到一个对象,在对象上可以调用$的实例方法
也可以使用$.方法名  调用静态方法。  比如 遍历数组对象的  $.each() 相当于for循环

1.6 jQuery中页面加载事件

使用jQuery的三个步骤:

引入jQuery文件
入口函数(定义页面加载事件)
功能实现

关于jQuery的入口函数:

// 第一种写法
$(document).ready(function() {
	
});
// 第二种写法
$().ready(function() {
	
});
// 第三种写法
$(function() {
	
});

jQuery入口函数与window.onload的对比

JavaScript的入口函数要等到页面中所有资源(包括图片、文件)加载完成才开始执行。
jQuery的入口函数只会等待文档树加载完成就开始执行,并不会等待图片、文件的加载。

第2章 选择器

jQuery选择器是jQuery为我们提供的一组方法,让我们更加方便的获取到页面中的元素。注意:jQuery选择器返回的是jQuery对象。

jQuery选择器有很多,基本兼容了CSS所有的选择器,并且jQuery还添加了很多更加复杂的选择器。(查看jQuery文档)

jQuery选择器虽然很多,但是选择器之间可以相互替代,就是说获取一个元素,你会有很多种方法获取到。所以我们平时真正能用到的只是少数的最常用的选择器。

2.1 jQuery基本选择器(重点)

名称用法描述
ID选择器$(‘#id’);获取指定ID的元素
类选择器$(‘.class’);获取同一类class的元素
标签选择器$(‘div’);获取同一类标签的所有元素
并集选择器$(‘div,p,li’);使用逗号分隔,只要符合条件之一就可。
交集选择器$(‘div.redClass’);获取class为redClass的div元素
  • 总结:跟css的选择器用法一模一样。

语法模板: 00-语法模板.html(需要包含jquery.js)

<script type="text/javascript">
	//常用选择器
	//根据id获取元素 获取到的结果:类数组对象
	console.log( $('#div0') );
	console.log( $('#div0')[0] );
	//根据class获取元素
	console.log( $('.div_1') );
	//根据标签名称来获取元素
	console.log( $('div') );
	//根据属性获取元素
	console.log( $('input[name=username]') );
	//根据表单元素属性获取元素
	console.log( $(':checked') );
</script>

2.2 jQuery层级选择器(重点)

名称用法描述
子代选择器$(‘ul > li’);使用-号,获取儿子层级的元素,注意,并不会获取孙子层级的元素
后代选择器$(‘ul li’);使用空格,代表后代选择器,获取ul下的所有li元素,包括孙子等
  • 跟CSS的选择器一模一样。

2.3 jQuery过滤选择器(了解为主)

  • 这类选择器都带冒号:
名称用法描述
:eq(index)$(‘li:eq(2)’).css(‘color’, ‘red’);获取到的li元素中,选择索引号为2的元素,索引号index从0开始。
:odd$(‘li:odd’).css(‘color’, ‘red’);获取到的li元素中,选择索引号为奇数的元素
:even$(‘li:even’).css(‘color’, ‘red’);获取到的li元素中,选择索引号为偶数的元素

2.4 jQuery筛选方法(重点)

  • 筛选选择器的功能与过滤选择器有点类似,但是用法不一样,筛选选择器主要是方法。
名称用法描述
children(selector)$(‘ul’).children(‘li’)相当于$(‘ul-li’),子类选择器
find(selector)$(‘ul’).find(‘li’);相当于$(‘ul li’),后代选择器
siblings(selector)$(‘#first’).siblings(‘li’);查找兄弟节点,不包括自己本身。
parent()$(‘#first’).parent();查找父亲
eq(index)$(‘li’).eq(2);相当于$(‘li:eq(2)’),index从0开始
next()$(‘li’).next()找下一个兄弟
prev()$(‘li’).prev()找上一次兄弟
closest$(‘li’).closest(‘ul’)找最近一个祖先元素

语法模板: 00-语法模板.html(需要包含jquery.js)

<script type="text/javascript">
    //常用筛选方法
    //获取爱好对应的div
    var hobby = $('#hobby');
    //获取 hobby 父元素
    console.log( $('#hobby').parent() );
    //获取 hobby 子元素
    console.log( $('#hobby').children() );
    //获取 hobby 最近的祖先元素
    console.log( $('#hobby').closest('form') );
    //获取 hobby 所有后代元素
    console.log( $('#hobby').find('input') );
    //获取 hobby 下一个兄弟元素
    console.log( $('#hobby').next() );
    //获取 hobby 前一个兄弟元素
    console.log( $('#hobby').prev() );
    //获取 hobby 所有兄弟元素
    console.log( $('#hobby').siblings() );
</script>

第3章 jQuery对象和DOM对象

3.1 jQuery对象和DOM对象的区别

DOM对象

用原生JavaScript获取的DOM对象
	通过document.getElementById()  反馈的是元素(DOM对象)
通过document.getElementsByTagName()获取到的是什么?
	伪数组(集合),集合中的每一个对象是DOM对象

jQuery对象

jQuery对象 用$()的方式获取的对象
jQuery对象又可以叫做包装集(包装的DOM对象的集合)

区别

jQuery对象不能使用DOM对象的成员,DOM对象不能使用jQuery对象的成员

<div id="box"></div>
<script>
    // DOM对象
    var box = document.getElementById('box');
    // 错误
    box.text('hello');
    // 正确
    box.innerText = 'hello';

    // jQuery对象,jQuery对象加前缀$,用以区分DOM对象
    var $box = $('#box');
    // 错误
    $box.innerText = 'hello';
    // 正确
    $box.text('hello');
</script>

3.2 jQuery对象和DOM对象的相互转换

jQuery对象转换成DOM对象:   
	jQuery对象.get(索引值); 
	jQuery对象[索引值] 
    	jQuery对象是包装集(集合),从集合中取数据可以使用索引的方式
DOM对象转换成jQuery对象:   
	$(DOM对象) 只有这一种方法;

第4章 简单事件绑定

所有事件在jquery中都是jquery对象的方法
click(handler)			单击事件
mouseover(handler)		鼠标悬浮事件
mouseout(handler)		鼠标离开事件
...
<input type="button" value="我是一个按钮" id="btn">
<script>
	$(function(){
    	$('#btn').click(function(){
            alert("来了老弟~");
        });
	});	  
</script>

绑定事件时,jquery对象中有多个dom元素,则自动给所有元素均绑定事件。

第5章 jQuery操作属性

5.1 attr操作

  • 设置单个属性
// 第一个参数:需要设置的属性名
// 第二个参数:对应的属性值
// $obj.attr(name, value);
// 用法举例
$('img').attr('title','哎哟,不错哦');
$('img').attr('alt','哎哟,不错哦');
  • 设置多个属性
// 参数是一个对象,包含了需要设置的属性名和属性值
// $obj.attr(obj)
// 用法举例
$('img').attr({
    title:'哎哟,不错哦',
    alt:'哎哟,不错哦',
    style:'opacity:.5'
});
  • 获取属性
// 传需要获取的属性名称,返回对应的属性值
// $obj.attr(name)
// 用法举例
var oTitle = $('img').attr('title');
alert(oTitle);
  • 移除属性
// 参数:需要移除的属性名,
// $obj.removeAttr(name);
// 用法举例
$('img').removeAttr('title');

5.2 prop操作

  • 在jQuery1.6之后支持,对于checked、selected、disabled这类boolean类型的属性来说,不能用attr方法,只能用prop方法。
// 设置属性
$(':checked').prop('checked',true);
// 获取属性
$(':checked').prop('checked');// 返回true或者false

5.3 val()/text()/html()值操作

$obj.val()		获取或者设置表单元素的value属性的值
$obj.html() 	对应innerHTML
$obj.text()		对应innerText
以上三个方法:不传参数 表示获取值; 传递一个参数值,表示设置

5.4 class操作

  • 添加样式类
// name:需要添加的样式类名,注意参数不要带点.
// $obj.addClass(name);
// 例子,给所有的div添加one的样式。
$('div').addClass('one');
  • 移除样式类
// name:需要移除的样式类名
// $obj.removeClass('name');
// 例子,移除div中one的样式类名
$('div').removeClass('one');
  • 判断是否有某个样式类
// name:用于判断的样式类名,返回值为true false
// $obj.hasClass(name)
// 例子,判断第一个div是否有one的样式类
$('div').hasClass('one');
  • 切换样式类
// name:需要切换的样式类名,如果有,移除该样式,如果没有,添加该样式。
// $obj.toggleClass(name);
// 例子
$('div').toggleClass('one');

5.5 隐式迭代

  1. 设置操作的时候,如果是多个元素,那么给所有的元素设置相同的值

  2. 获取操作的时候,如果是多个元素,那么只会返回第一个元素的值。

第6章 jQuery操作样式

6.1 CSS操作

  • 功能:设置或者修改样式,操作的是style属性。

  • 操作单个样式

// name:需要设置的样式名称
// value:对应的样式值
// $obj.css(name, value);
// 使用案例
$('#one').css('background','gray');// 将背景色修改为灰色
  • 设置多个样式
// 参数是一个对象,对象中包含了需要设置的样式名和样式值
// $obj.css(obj);
// 使用案例
$('#one').css({
    'background':'gray',
    'width':'400px',
    'height':'200px'
});
  • 获取样式
// name:需要获取的样式名称
// $obj.css(name);
// 案例
$('div').css('background-color');

注意:获取样式操作只会返回第一个元素对应的样式值。

6.2 jQuery尺寸和位置操作

6.2.1 width方法与height方法
  • 设置或者获取高度,不包括内边距、边框和外边距
// 带参数表示设置高度
$('img').height(200);
// 不带参数获取高度
$('img').height();

获取网页的可视区宽高

// 获取可视区宽度
$(window).width();
// 获取可视区高度
$(window).height();
6.2.2 innerWidth/innerHeight/outerWidth/outerHeight
innerWidth()/innerHeight()	方法返回元素的宽度/高度(包括内边距)。
outerWidth()/outerHeight()  方法返回元素的宽度/高度(包括内边距和边框)。
outerWidth(true)/outerHeight(true)  方法返回元素的宽度/高度(包括内边距、边框和外边距)。
6.2.3 scrollTop与scrollLeft
  • 设置或者获取垂直滚动条的位置
// 获取页面被卷曲的高度
$(window).scrollTop();
// 获取页面被卷曲的宽度
$(window).scrollLeft();
6.2.4 offset方法与position方法
  • offset方法获取元素距离document的位置,position方法获取的是元素距离有定位的父元素(offsetParent)的位置。
// 获取元素距离document的位置,返回值为对象:{left:100, top:100}
$(selector).offset();
// 获取相对于其最近的有定位的父元素的位置。
$(selector).position();

第7章 each方法遍历

  • jQuery的隐式迭代会对所有的DOM对象设置相同的值,但是如果我们需要给每一个对象设置不同的值的时候,就需要自己进行迭代了。

作用:遍历jQuery对象集合,为每个匹配的元素执行一个函数

// 参数一表示当前元素在所有匹配元素中的索引号
// 参数二表示当前元素(DOM对象)
$(selector).each(function(index,element){});

第8章 jQuery事件机制

  • JavaScript中已经学习过了事件,jQuery对JavaScript事件进行了封装,增加并扩展了事件处理机制。jQuery不仅提供了更加优雅的事件处理语法,而且极大的增强了事件的处理能力。

8.1 jQuery事件发展历程(了解)

简单事件绑定–bind事件绑定–delegate事件绑定–on事件绑定(推荐)

  • 简单事件注册
click(handler)			单击事件
mouseenter(handler)		鼠标进入事件
mouseleave(handler)		鼠标离开事件
  • bind方式注册事件(不用)
// 第一个参数:事件类型
// 第二个参数:事件处理程序
$('p').bind('click mouseenter', function(){
    // 事件响应方法
});
  • delegate注册委托事件(不用)
// 第一个参数:selector,要绑定事件的元素
// 第二个参数:事件类型
// 第三个参数:事件处理函数
$('.parentBox').delegate('p', 'click', function(){
    // 为 .parentBox下面的所有的p标签绑定事件
});

8.2 on注册事件(重点)

  • jQuery1.7之后,jQuery用on统一了所有事件的处理方法。
  • 强烈建议使用。

on注册简单事件

// 表示给$(selector)绑定事件,并且由自己触发,不支持动态绑定。
$(selector).on( 'click', function() {});

on注册事件委托

// 表示给$(selector)绑定代理事件,当必须是它的内部元素span才能触发这个事件,支持动态绑定
$(selector).on( 'click','span', function() {});

on注册事件的语法:

// 第一个参数:events,绑定事件的名称可以是由空格分隔的多个事件(标准事件或者自定义事件)
// 第二个参数:selector, 执行事件的后代元素(可选),如果没有后代元素,那么事件将由自己执行。
// 第三个参数:data,传递给处理函数的数据,事件触发的时候通过event.data来使用(不常使用)
// 第四个参数:handler,事件处理函数
$(selector).on(events[,selector][,data],handler);

8.3 事件解绑

  • unbind方式(不用)
$(selector).unbind(); // 解绑所有的事件
$(selector).unbind('click'); // 解绑指定的事件
  • undelegate方式(不用)
$( selector ).undelegate(); // 解绑所有的delegate事件
$( selector).undelegate( 'click' ); // 解绑所有的click事件
  • off方式(推荐)
// 解绑匹配元素的所有事件
$(selector).off();
// 解绑匹配元素的所有click事件
$(selector).off('click');

8.5 触发事件

$(selector).click(); // 触发 click事件
$(selector).trigger('click');

8.6 jQuery事件对象

jQuery事件对象其实就是js事件对象的一个封装,处理了兼容性。

// screenX和screenY	对应屏幕最左上角的值
// clientX和clientY	距离页面左上角的位置(忽视滚动条)
// pageX和pageY	距离页面最顶部的左上角的位置(会计算滚动条的距离)

// event.keyCode	按下的键盘代码
// event.data	存储绑定事件时传递的附加数据

// event.stopPropagation()	阻止事件冒泡行为
// event.preventDefault()	阻止浏览器默认行为
// return false:既能阻止事件冒泡,又能阻止浏览器默认行为。

第9章 jQuery动画效果

  • jQuery提供了三组基本动画,这些动画都是标准的、有规律的效果,jQuery还提供了自定义动画的功能。
  • 演示动画效果 [08-演示jQuery动画(animate).html]

9.1 三组基本动画

  • 显示(show)与隐藏(hide)与切换(toggle)是一组动画:
  • 滑入(slideUp)与滑出(slideDown)与切换(slideToggle),效果与卷帘门类似
  • 淡入(fadeIn)与淡出(fadeOut)与切换(fadeToggle)
$obj.show([speed], [callback]);
// speed(可选):动画的执行时间
	 // 1.如果不传,就没有动画效果。如果是slide和fade系列,会默认为normal
	 // 2.毫秒值(比如1000),动画在1000毫秒执行完成(推荐)
     // 3.固定速度字符串,slow(200)、normal(400)、fast(600),如果传其他字符串,则默认为normal。
// callback(可选):执行完动画后执行的回调函数

slideDown()/slideUp()/slideToggle();同理
fadeIn()/fadeOut();fadeToggle();同理

9.2 自定义动画

  • animate: 自定义动画
$(selector).animate({params},[speed],[easing],[callback]);
// {params}:要执行动画的CSS属性,带数字(必选)
// speed:执行动画时长(可选)
// easing:执行效果,默认为swing(缓动)  可以是linear(匀速)
// callback:动画执行完后立即执行的回调函数(可选)

9.3 动画队列与停止动画

  • 在同一个元素上执行多个动画,那么对于这个动画来说,后面的动画会被放到动画队列中,等前面的动画执行完成了才会执行(联想:火车进站)。
// stop方法:停止动画效果
stop(clearQueue, jumpToEnd);
// 第一个参数:是否清除队列
// 第二个参数:是否跳转到最终效果

第10章 jQuery节点操作

10.1 创建节点

// $(htmlStr)
// htmlStr:html格式的字符串
$('<span-这是一个span元素</span-');

10.2 添加节点

append  appendTo		在被选元素的结尾插入内容   父.append().appendTo()
prepend prependTo		在被选元素的开头插入内容   父.prepend().prependTo()
before  insertBefore	在被选元素之后插入内容		后.before().insertBefor()
after	insertAfter		在被选元素之前插入内容 	前.after().insertAfter()

10.3 清空节点与删除节点

  • empty:清空指定节点的所有元素,自身保留(清理门户)
$('div').empty(); // 清空div的所有内容(推荐使用,会清除子元素上绑定的事件)
$('div').html('');// 使用html方法来清空元素,不推荐使用,绑定的事件不会被清除。
  • remove:相比于empty,自身也删除(自杀)
$('div').remove();

10.4 克隆节点

  • 作用:复制匹配的元素
// 复制$(selector)所匹配到的元素(深度复制)
// cloneNode(true)
// 返回值为复制的新元素,和原来的元素没有任何关系了。即修改新元素,不会影响到原来的元素。
$(selector).clone();//克隆元素本身及后代
$(selector).clone(true);//克隆元素本身及后代以及绑定的事件

第11章 jQuery工具方法

11. 1 数组和对象操作

①$.inArray(value, array, [fromIndex])

确定第一个参数在数组中首次出现的位置,从0开始计数(如果没有找到则返回 -1 )。

value:用于在数组中查找是否存在

array:待处理数组。

fromIndex:用来搜索数组队列,默认值为0。

$.inArray(1,[1,2,3,1]);

$.inArray(1,[1,2,3,1],2);

②$(‘选择器’).toArray(); 把jQuery集合中所有DOM元素恢复成一个数组。

$('div').toArray();

③$.merge(first, second); 合并数组

$.merge([1,2,3], [4,3,2]);

④$.parseJSON(str); 解析json字符串为对象,等价于 JSON.parse(str);

$.parseJSON('{"name":"zhangfei","age":30}');

11.2 字符串操作

$.trim(str) 去除字符串两边的空格, 等价于 str.trim()

$.trim('  123   ');

11.3、类型操作

$.type(obj)  判断数据类型  typeof
 
$.isArray(obj) 判断是否数组

$.isFunction(obj) 判断是否函数

$.isEmptyObject(obj) 判断是否空对象(没有成员)
 
$.isPlainObject(obj) 判断是否纯对象(字面量语法{}或实例化new 构造函数 定义的对象)

$.isNumeric(obj) 判断是否数字(数字型或字符串型数字)

第12章 插件

12.1 jQuery插件开发语法

给jQuery增加方法的两种方式

$.method = fn		静态方法
$.fn.method = fn	实例方法

12.2 常用插件

(function ($) {
  $.add = function (a, b) {
    return a + b;
  }
})(jQuery)

$.add(5, 6);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值