JavaScript 学习 (ES5)
目录
文章目录
课程介绍
本系列课程主要会讲到标题中的 J avascriPt 部分由于内容过多所以分开了本系列课程相对来说比较重要
Javascript 介绍
Javascript 是属于 HTML 和 web 的编程语言 Javascript 是一种轻量级的编程语言 Javascript 是可插入HTML 页面的编程代码 JavascriPt 插入HTML 页面后,可由所有的现代浏览器执行主要是前端中最重要的一门语言 Javascript 的学习使用 Javascript 可以做出很多有意思的效果以及优化和用户的交互体验
素质三连
为什么要学习 Javasciprt ?
学习了 Javascript 有什么用?
学习完了对我有什么帮助?
课程规划
这系列课程内容较多在第一阶段讲解的可能会有一大部分剩下的放在第二阶段讲解第一阶段主要重点在 Js 语法以及第二阶段主要在数据交互方面以及新语法
建议学习此课程的前提知识
html4
css3/css2
xml
Markdown
章节介绍
第一阶段学习
- JavaScript 基本语法和变量
- JavaScript 数据类型
- JavaScript 运算符
- JavaScript 流程控制
- JavaScript 函数与字符串函数(ES5)
- JavaScript 数组与数组函数(ES5)
- JavaScript 日期与时间
- JavaScript 内置函数
- JavaScript 面向对象和原型
- JavaScript 匿名函数和闭包
- JavaScript BOM 操作
- JavaScript 浏览器检测
- JavaScript DOM操作
- JavaScript 事件
- JavaScript 表单处理
- JavaScript 异常处理
- JavaScript 正则表达式
- JavaScript Cookie与存储
- JavaScript 内存管理
- JavaScript 加密技术
第二阶段学习
- PHP基础
- PHP 面向对象
- JSON与XML
- PHP解析xml与JSON
- JavaScript 解析JSON与XML
- HTTP
- JavaScript XHR 对象
- ECMASCRIPT6
- PHP 链接数据库
- TypeScript
章前知识
- JavaScript 发展历程
- JavaScript 的核心组成
- JavaScript 的使用方式
- Script 标签
JavaScript 发展历程
. SCript 标签 JaVascript 的发展历程 J avas 。: iPt 因为互联网而生,紧跟着浏览器的出现而问世。最初主要应用在 web 浏览器找域作为脚本语言进行使用要想清楚 JavascriPt 的发展史就要了解浏览器的发展史
1 990 年底,欧洲核能研究组织科学家 Tim 日 erners 一 Lee 发明了万维网( world wide web )从此可以在网上浏览网页文件。最早的网页只能在操作系统的终端里浏览,也就是说只能使用命令行操作,网页都是在字符窗口中显示,这当然非常不方便
1 992 年底,美国国家超级电脑应用中心( NcsA )开始开发一个独立的浏览器,叫做 Mosaic 。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览
1994 年 10 月, NcsA 的一个主要程序员 Marc Andreessen 联合风险投资家 Jim clark ,成立了 Mosaic 通信公司 ( Mosaic Communications ) ,不久后改名为 Netscape 。这家公司的方向,就是在 Mosaic 的基础上,开发面向昔通用户的新一代的浏览器 Netscape Navigator 1 994 年 12 月, Navigato 「发布了 1 . 0 版,市场份额一举超过 90 %
N etscaPe 公司很快发现, Navigato 「浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为管理层对这种浏览器脚本语言的设想是二功能不需要招虽,语法较为简单,容易学习和部署。那一年,正逢 sun 公司的 Java 语言问世,市场推广活动非常成功。 N etsc 即 e 公司决定与 sun 公司合作,浏览器支持嵌入 J ava 小程序(后来称为 Ja va a PPlet )。但是,浏览器脚本语言是否就选用 Java ,则存在争论.后来,还是决定不使用 J ava ,因为网页小程序不需要 Java 这么“重”的语法。但是,同时也决定脚本语言的语法要接近 Java ,并且可以支持 Java 程字 N etsca pe 公司的这种浏览器脚本语言,最初名字叫做 M ocha , 1995 年 9 月改为 Livescript 12 月, N etscape 公司与 sun 公司( Java 语言的发明者和所有者)达成协议,后者允许将这种语言叫做 JavascriPt 。这样一来, N etsoaPe 公司可以借助 Java 语言的声势,而 Sun 公司则将自己的影响力扩展到了浏览器后来 J ava 语言的浏览器插件失败了, JavascriPt 反而发扬光大。
JavaScript 的核心组成
- ECMAScript
- DOM
- BOM
ECMAScript 介绍
ECMAScript 是一个重要的标准 简单的说 ECMAScript阐述了以下内容
- 语法
- 类型
- 语句
- 关键字
- 保留字
- 运算符
- 对象
ECMAScript的版本
ECMAScript分成几个不同的版本,它是在一个叫做 ECMA-262的标准中定义的。和其他标准一样,ECMA-262会被编辑和更新。当有了主要更新时,就会发布一个标准的新版。版本是5.1,于2011年6月发布并开始6.0版的制定
ECMAScript 6(简称ES6)是于2015年6月正式发布的Javascript语言的标准,正式名为ECMAScript 2015(ES2015)。它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言
1996年8月,微软模仿 JavaScript开发了一种相近的语言,取名为JScript(JavaScript是Netscape的注册商标,微软不能用),首先内置于IE3.0。Netscape公司面临丧失浏览器脚本语言的主导权的局面。
1996年11月,Netscape 公司决定将JavaScript提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScrip能够成为国际标准,以此抵抗微软
ECMAScript 只用来标准化 Javascript 这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如DOM的标准就是由w3C组织(World wide web Consortium)制定的
DOM
DOM(文档对象模型)是HTML和XM的应用程序接口(APl)。DoM将把整个页面规划成由节点层级构成的文档。HTML 或xML 页面的每个部分都是一个节点的衍生物btml
DOM通过创建树来表示文档,从而使开发者对文档的内容和结构具有空前的控制力。用DOMAPI可以轻松地删除、添加和替换节点
BOM
BOM主要处理浏览器窗口和框架,不过通常浏览器特定的 Javascript扩展都被看做BOM的一部分。这些扩展包括
- 弹出新的浏览器窗口
- 移动、关闭浏览器窗口以及调整窗口大小
- 提供 Web浏览器详细信息的定位对象
- 提供用户屏幕分辨率详细信息的屏幕对象
- 对 cookie的支持
- IE扩展了BOM,加入了ActivexXObject类,可以通过JavaScript实例化ActiveX对象
JavaScript 的使用方式
- 在html中使用
- 外部脚本文件使用
Script 标签
- 内部的JS代码
- 外部的JS代码
第一章 基本语法和JavaScript变量
JavaScript 字面量
在编程语言中 一般固定值称为字面量 如3.14
数字(Number) 字面量可以是整数或者小数
字符串(String) 字面量可以是单引号或者双引号
表达式字面量 用于计算
5 + 6
5 + 10
输出提示语句
document.write();
console.log();
console.dir();
console.error();
console.time();
console.timeEnd();
console.info();
console.warn();
高级用法
console.table([['中国','美国'],['好']]);
//格式化输出
/*
console.log支持的格式标志有:
%s 占位符
%d 或 %i 整数
%f 浮点数
%c css样式
*/
console.log('%d + %d = %d',1,2,3)
console.group('分组1');
console.log('语文');
console.log('数学');
console.group('其他科目');
console.log('化学');
console.log('地理');
console.log('历史');
console.groupEnd('其他科目');
console.groupEnd('分组1');
JavaScript 语句
JavaScript 是发给浏览器的命令
分号
用于分隔JavaScript语句
a = 5;
b = 6;
c = a + b;
或者
a = 5; b = 6; c = a + b;
执行顺序
JavaScript 是脚本语言 浏览器在读取代码时 会逐行的执行脚本代码 对于其他有的编程语言来说 在执行前会对所有代码进行编译
注释
注释可以用于提高代码的阅读性
语法
单行注释以 //
开头
// 输出标题
document.getElementById("myH1").innerHTML = "欢迎来到我的主页";
多行注释 以 /*
开始 */
结束
/*
注释内容
*/
变量
变量是用于存储信息的容器 在JS中我们可以通过变量来存储具体的数据
var x = 5;
var y = 6;
var z = x+y;
变量声明
在ES5中我们通过 var 来声明变量
JS中不严格区分数据类型 所以我们仅仅使用var来进行变量声明 可以存储JavaScript中的任意类型
变量赋值
通过 = 符号来进行赋值 右边的是值 左边的是变量名称 给右边的值赋予左边的变量
var a = 5;
第一种声明赋值方式
var a = 15;
var b = 15;
第二种声明赋值方式
var a = 2 , b = 2;
第三种声明赋值方式
var a,b,c = 5;
第四种声明方式
var a = b = c = 1;
命名规范
首字符
- 英文字母或者下划线
组成
- 英文字母
- 数字
- 下划线
禁忌
- JavaScript 保留字 关键字
Javascript 的保留关键字不可以用作变量、标签或者函数名。有些保留关键字是作为 Javascript 以后扩展使用
小提示: 这里指的保留字关键字就是和var一样的具有特殊意义的名称不能当做变量的名称来使用
声明规范
规范
必须显式声明
陋习
重复声明
隐式声明
不声明直接赋值
正解
先声明 后读写
先赋值 后运算
变量提升
console.log(a); // undefined 将a声明提前了 所以输出undefined 并不会报错
var a = 5;
变量类型检测
值类型
- 占用固定空间
- 保存与赋值是值本身
- 使用
typeof
检测数据类型typeof name === 'string'
- 基本数据类型是值类型
typeof
typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型
它返回值是一个字符串,该字符串说明运算数的类型。typeof 一般只能返回如下几个结果
number,boolean,string,function,object,undefined。我们可以使用 typeof 来获取一个变量是否存在,如 if(typeof a!=“undefined”){alert(“ok”)},而不要去使用 if(a) 因为如果 a 不存在(未声明)则会出错,对于 Array,Null 等特殊对象使用 typeof 一律返回 object,这正是 typeof 的局限性
引用类型
- 占用空间不固定 保存在堆中
- 保存与赋值的指向是一个指针
- 使用
instanceof
检测数据类型person instanceof Person
- 使用 new 关键字实例化构造出一个对象
instanceof
instanceof 用于判断一个变量是否某个对象的实例,如 var a=new Array();alert(a instanceof Array); 返回true
变量作用域
全局变量
在函数体外部定义的变量
在函数体内部定义的无var的变量
在任何位置都可以调用
局部变量
在函数内部使用 var 定义
函数的参数
在函数内部可以调用
优先级
局部变量高于同名全局变量
参数变量高于同名全局变量
局部变量高于同名参数变量
特性
全局变量是全局对象的属性
作用域链
内层函数可以访问外层函数局部变量
外层函数不可以访问内层函数局部变量
生命周期
全局变量
- 除非被显式删除 否则一直存在
局部变量
- 自声明开始到函数运行完毕结束或者被显式删除
回收机制
- 标记清除法
- 引用计数法
第二章 - JavaScript 数据类型
在JS中我们会接触到非常多的数据类型 大致有8种到现在 在第一阶段我们接触6种
undefined
- 使用 var 声明的变量但是未初始化
- 区分空指针对象和尚未定义的变量
- 对未初始化的变量以及未声明的变量使用 typeof 会返回 undefined
- 不能用 delete 运算符来删除他
- undefined 不是常量 可以把他设置为其他值
- 当尝试访问不存在对象属性时也会返回 undefined
Null
- 特指对象的值未设置
- 逻辑上 null 表示空指针对象
- 使用 typeof 检测会返回 object
- 可以用来清空一个对象
- null 是表示缺少的标识 指示变量是为指向任何对象 把 null 作为尚未创建的对象也许更好理解
undefined和null的关系
- undefined 和 null 使用
==
会返回 true
注意:
- 没有必要将变量值显式声明为 undefined
- 声明空对象时应将其赋值为 null
Boolean
- true 为真
- false 为假
- Boolean
- 转换为 true
- 任何非空字符串
- 任何非零数值
- 任何非空对象
- 转换为 false
- 空字符串
- 0以及NaN
- null和undefined
- 转换为 true
String
- 由单引号和双引号组成 不能交叉使用
- 使用.length属性可以访问长度 转移序列标识一个字符
- 使用构造器创建String 类型检测是 object
转义序列
- \n 换行
- \t 制表符
- \b 空格
- \r 回车符
- \\ 斜杠
- \’ 单引号
- \" 双引号
String 类型转换
- “”
- toString()
- 可以接收一个参数 将对应的数值转换为其他进制
(123).toString(2)
- 可以接收一个参数 将对应的数值转换为其他进制
- String()
String 类型判断
- typeof
- 构造器创建使用 instanceof
Number
进制
- 10进制
- 八进制
- 前导0
- 16 进制
- 前导 0x
浮点数
- 小数点后至少一位数
- 缺陷
- 计算存在偏差
数值范围
- 最小值 Number.MIN_VALUE
- 最大值 Number.MAX_VALUE
- 超出范围16位
- 正无穷
- Infinity
- Number.POSITIVE_INFINITY
- 负无穷
- -Infinity
- Number.NEGATIVE_INFINITY
- 检测方法
- ifFinite
- 超出范围 false
- 合法范围 true
- ifFinite
- 超出合法范围计算会出现问题
- 正无穷
- Number.MIN_VALUE
- Number.MAX_VALUE
- Number.NaN
- Number.NEGATIVE_INFINITY
- Number.POSITIVE_INFINITY
- Number.EPSILON
- Number.MIN_SAFE_INTEGER
- Number.MAX_SAFE_INTEGER
NaN
- 含义
2. Not a Number- 非数值
- 特性
- 任何涉及NaN的操作都将返回NaN
- NaN与任何数值都不相等 包括自身
- 检测
- 可以转换为数值 返回 false
- 不可以转换为数值 返回 true
数值转换
-
Number()
- Boolean 转 Number
- true 1
- false 0
- Null 转 Number
- 0
- undefined 转 Number
- NaN
- String 转 Number
- 只包含数字
- 十进制
- 前导被忽略
- 包含有效浮点格式
- 浮点数值
- 忽略前导0
- 包含有效的十六进制格式
- 相同大小的十进制数
- 空字符串 0
- 其他格式字符串
- NaN
- 只包含数字
- Boolean 转 Number
-
parseInt()
- 特性
- 忽略前置空格
- 直接找到第一个非空格字符
- 不是数字字符或者负号
- NaN
- 数字字符
- 解析所有后续字符
- 遇到非数字字符结束
- 小数点不是有效数字
- 如果是一个浮点数直接取整
parseInt("f",16)
可以接收第二个参数进行进制的转换为10进制
- 特性
-
parseFloat()
- 从第一个字符开始解析
- 遇到无效浮点格式后结束
- 只有第一个小数点有效果
- 忽略前导0
- 16进制数始终为0
- 没有小数点 或者小数点后全是0 转换为整数
-
|
"string | 0"
结果是0"123 | 0" 结果是 123
- 如果是一个合法数值的字符串则转换为数值 否则就是0
- 自动取整
-
加减乘除转换数值
console.log("123"*1); // 123 console.log(+"123"); // 123 console.log(-"123"); // -123 console.log("123"/1); // 123
-
>>
console.log("123" >> 0); // 123
- 自动取整
-
~~
~~("123") // 123
- 自动取整
Math.js
/**
* Created by Jerry Tong on 2016/7/4.
*/
Math.add = function(v1, v2)
{
var r1, r2, m;
try
{
r1 = v1.toString().split(".")[1].length;
}
catch (e)
{
r1 = 0;
}
try
{
r2 = v2.toString().split(".")[1].length;
}
catch (e)
{
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (v1 * m + v2 * m) / m;
}
Number.prototype.add = function(v)
{
return Math.add(v, this);
}
Math.sub = function(v1, v2)
{
return Math.add(v1, -v2);
}
Number.prototype.sub = function(v)
{
return Math.sub(this, v);
}
Math.mul = function(v1, v2)
{
var m = 0;
var s1 = v1.toString();
var s2 = v2.toString();
try
{
m += s1.split(".")[1].length;
}
catch (e)
{
}
try
{
m += s2.split(".")[1].length;
}
catch (e)
{
}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
Number.prototype.mul = function(v)
{
return Math.mul(v, this);
}
Math.div = function(v1, v2)
{
var t1 = 0;
var t2 = 0;
var r1, r2;
try
{
t1 = v1.toString().split(".")[1].length;
}
catch (e)
{
}
try
{
t2 = v2.toString().split(".")[1].length;
}
catch (e)
{
}
with (Math)
{
r1 = Number(v1.toString().replace(".", ""));
r2 = Number(v2.toString().replace(".", ""));
return (r1 / r2) * pow(10, t2 - t1);
}
}
Number.prototype.div = function(v)
{
return Math.div(this, v);
}
常用方法
- Number.isInteger()
- 判断是否是一个整数
- valueOf
- 返回原始值
Number(123.54566.toFixed(2)).valueOf()
- toFixed()
- 保留小数位
- 只有数值才能调用 返回String
Number(123.54566.toFixed(2))
- toString()
- 进制转换
- 转为String类型
Object 类型
- 定义
- 一组功能或者数据的集合
- 声明
- 构造器方式
- 字面量方式
- 属性和方法
- constructor 获取到对象的构造器
- hasOwnPropety(propetyName) 一个对象中是否有某一个属性
- object.prototype.isProtoTypeOf(Object) 检测一个对象是否处于另一个对象的原型链中
- toLocalString() 转换为本地字符串 和toString很像 但是在new Date转换时区别很大
- toString() 转换为字符串 null 和 undefined 排除
- valueOf() 输出原本的值
- delete 操作符
- 可以删除变量被回收
- 可以删除对象属性 删除之后再去获取就是返回undefined
- 可以删除数组对应的内容 删除之后再去获取返回的就是 undefined
- 可以删除不通过var声明的变量 再去获取就会报错未定义 如果是var声明的变量那是不会删除的
- Object 类型判断
- Object 下面有很多子类 类似Array,String,Function等 如果要详细判断可以通过如下方式
Object.prototype.toString.call(ObjectName) // [object Array]
- Object 下面有很多子类 类似Array,String,Function等 如果要详细判断可以通过如下方式
第三章 - 运算符
算数运算符
- =
- 赋值运算符
- +
- 对数字进行求和
- 对字符串进行拼接
- 将一个数值转换为字符串
- 字符串拼接
- -
- 对数字进行减法操作
- 将字符串转为数值
- *
- 对数字进行乘法运算
- /
- 对数字进行除法运算
- %
- 取余数
- 符号问题
- 同号得正 异号得负
复合赋值运算符
- +=
- -=
- *=
- /=
- %=
自增与自减
- ++ 每一次加1
- – 每一次减1
- 运算数必须是一个变量 数组的元素或者对象的属性
- 如果运算数是个非数值 那么会转换为数值
- 符号位置决定预算结果
- 运算数之前 先进行运算操作 在进行求值
- 先+1在赋值
- 运算数之后 先求值 在进行运算
- 先赋值在+1
- 运算数之前 先进行运算操作 在进行求值
关系运算符
- >
- <
- >=
- <=
操作规则
- 数值与数值比较 比较他们的值
- 进一个运算数是数值 将另一个数值转换为数值进行比较
- 字符串间比较 逐字符比较他们的Unicode值
- 运算数即非数值也非字符串转换成数值或者字符串进行比较
- 运算数无法被转换为数值或者字符串返回false
- 与NaN比较返回false
等值关系
- 操作符
- ==
- !=
- 相同比较
- ===
- !==
对象运算符
- in 判断左侧运算数是否为右侧运算数成员
- instanceof 判断对象实例 是否是某一个类的构造实例
- new 创造一个新的对象 根据构造函数
- delete 删除对象指定的属性
- . [] 存储数组或者对象的属性元素
- () 调用函数 改变优先级
逻辑运算符
- ! 逻辑非
- && 逻辑与
- || 逻辑或
位运算符
- << 左移
- >> 右移
三元运算符
- 条件表达式 ? 取值1 : 取值2
运算符优先级
- 小括号
- 一元运算符
- 算数运算符
- 关系运算符
- 相等运算符
- 逻辑运算符
- 赋值运算符
- 逗号运算符
第四章 - 流程控制语句
选择语句
IF
语法
if(条件表达式1){
代码段1
}else if(条件表达式2){
代码段2
}else{
代码段3
}
特性
- 结构复杂 可嵌套
- 可测试多个表达式条件
- 适用于任何的数据类型
- 可处理复杂的逻辑关系
SWITCH
语法
switch(1){
case 1:
console.log(1);
break;
case 2:
console.log(20);
default:
console.log("default");
}
格式
- case 标签是常量 可以是字符串或者数字
- 每个标签均已 : 号结束
- 虽然 break 和 default 为可选项 但使还是不要省略
- case的值必须和表达式的值完全匹配
特性
- 结构简洁 专为多重选择设计
- 仅仅可以测试一个表达式语句
- 表达式的值是固定的
- 仅能应用整数 枚举 字符串等类型的数据
循环语句
WHILE
语法
var a = 0;
while(a < 5) {
console.log(a);
a++;
}
流程
特性
- 先检查条件在进行循环
- 条件不满足时则循环一次也不执行
- 一个动作被重复执行到一个条件满足时
DO-WHILE
语法
var a = 5;
do{
console.log(a);
a++;
}while(a < 10);
流程
- 先执行循环体内的代码在进行判断
- 如果表达式结果为true则继续进行进行循环
- 如果表达式的结果为false就退出循环体
特性
- 先执行循环体在进行判断
- 循环体内的代码至少执行一次
FOR
语法
for(var i = 1 ; i < 10 ; i++){
console.log(i);
}
流程
- 用循环变量和循环条件作比较 确定返回值
- 如果返回值为true则执行返回体
- 执行完一次后进行递增递减运算
- 将运算结果和循环条件相比较
- 如果返回true则继续执行循环体 如果false就不执行
- 重复一个动作到一定次数
双重for循环
乘法表
for(var i = 1 ; i < 10 ; i++){
for(var j = 1 ; j <= i; j++){
document.write(" "+j+"*"+i+"="+i*j+" ");
}
document.writeln("<br/> ");
}
二维表格
document.writeln("<table border='1'>");
for(var i = 1 ; i < 5 ; i++){
document.writeln("<tr>");
for(var j = 1 ; j < 6; j++){
document.writeln("<td>第"+i+"第"+j+"列</td>");
}
document.writeln("</tr>");
}
document.writeln("</table>");
FOR-IN
语法
var a = {
name:"Lucson",
age:18
};
for(var temp in a){
console.log(temp);
}
作用
- 枚举对象属性
注意事项
- 对象的值不能是null或者undefined
- 遍历对象获取的是属性名称
- 遍历数组获取的是数组下标
FOR-OF
语法
var a = [{},2,3]
for(var temp of a){
console.log(temp);
}
作用
- 遍历数组
注意事项
- 直接遍历获取到数组的值
- 无法遍历对象
- ES6新出的遍历方式
FOR-EACH
语法
var a = [1,2,3,4];
a.forEach(function(item,index,arr){
console.log(item,index);
})
作用
- 遍历数组
- 属于数组实例方法
特性
- 是一个功能函数
- 可以获取到数组的下标和元素
跳转语句
return
终止函数体的运行 并且返回一个值
使用方式
function getName(){
// 返回值
return "小明";
// return 下面如果还有语句 则不会执行
}
var a = getName();
console.log(a); // 小明
break
终止整个循环不在进行判断
使用方式
for(var i = 1 ; i < 5 ; i ++){
if(i == 4){
// 直接退出循环
break;
}
console.log(i); // 1 2 3
}
continue
结束本次循环 接着去判断是否执行下次循环
使用方式
for(var i = 1 ; i < 5 ; i ++){
if(i == 2){
// 跳过本次循环 继续执行下一次循环
continue;
}
console.log(i); // 1 3 4
}
第五章 - 函数 和 字符串函数
函数
介绍
函数是定义一次但是可以多次调用的一段JS代码 函数有时候会传参数 即函数被调用时指定了的局部变量 函数常常使用这些参数来计算一个返回值 这个值也成为函数调用表达式的值 同时函数是一个功能的封装 通过调用这个功能完成某一些操作
函数的声明
函数对于每一种语言来说都是一个非常核心的概念 通过函数可以封装任意多条语句 而且可以在任意地方任意时候进行调用 JavaScript 中使用 function 关键字来声明函数 后面跟一组参数以及函数体
无参的函数
function box(){
console.log("调用我我才会执行");
}
box();
带参的函数
function box(name,age){
console.log("name:"+name,"age:"+age);
}
box("lucson",21);
return 返回值
带参数和不带参数的函数 都没有定义返回值 而是调用后直接执行的 实际上任何函数都可以通过 return 关键字来返回需要返回的值
无参数函数带返回值
function box(){
return "我是返回的值";
}
console.log(box("lucson",21)); // 我是返回的值
有参数函数带返回值
function box(name,age){
console.log("name:"+name,"age:"+age);
return name+"-"+age
}
console.log(box("lucson",21)); // lucson-21
还可以给带有返回值的函数赋值给一个变量 通过变量进行操作
function box(name,age){
return name+"-"+age
}
var a = box("lucson",21);
console.log(a); // lucson-21
return关键字还有一个作用就是退出当前函数 和 break 不同 break是用在循环里和switch语句中的
第一个 return 后面的语句不在执行
function box(name,age){
return 100;
return name+"-"+age;
}
var a = box("lucson",21);
console.log(a); // 100
break用在函数内将会提示JS错误信息
function box(name,age){
break;
}
var a = box("lucson",21); // SyntaxError: Illegal break statement
arguments
JavaScript 不介意传递进来多少参数 也不会因为参数杂乱无章产生错误 但是函数内部有一个对象来接受到我们所传递进来的参数 它是一个数组
function box(name,age){
console.log(arguments[0],arguments[1])
}
var a = box("lucson",21); // lucson 21
arguments 对象的 length 属性可以得到参数的数量
function box(name,age){
return arguments.length;
}
var a = box("lucson",21);
console.log(a); // 2
没有参数我们通过arguments[]获取会获得undefined
可以利用length属性来智能判断有多少参数 然后吧参数进行应用
现在我们实现一个累计进行相加操作的函数但是其传递的参数是不固定的
function box(){
var sum = 0;
if(arguments.length == 0) return sum;
for(var i = 0 ; i < arguments.length ; i++){
if(typeof arguments[i] != "number") {
sum = 0;
break;
}
sum += arguments[i];
}
return sum;
}
var a = box(1,2,10);
console.log(a); // 2
Js中没有重载功能
重载指的就是相同的函数名称 不同的参数列表或者类型
function box(name){
console.log(name);
}
function box(name,age){
console.log(name,age)
}
box("Lucson",21);
函数的其他声明方式
普通声明
function box(num1,num2){
return num1 + num2;
}
使用变量初始化函数
var box = function(num1,num2){
return num1 + num2;
};
使用构造函数声明
var box = new Function("num1","num2","return num1+num2;");
函数的返回值进行传递
function box(sum,num){
return sum+num;
}
function sum(num){
return num+10;
}
var result = box(sum(10),10);
console.log(result); // 30
作为值的函数
function box(sum,num){
return sum(10)+num;
}
function sum(num){
return num+10;
}
var result = box(sum,10);
console.log(result);
传递对象的函数
有时候我们需要很多个数据 这个时候我们就可以传递一个对象到函数中去
function box(obj){
return obj.name+"-"+obj.age;
}
var result = box({
name:"lee",
age:21
});
console.log(result);
回调函数
有时候我们需要先获取到某些数据在进行下一步操作这时候可能就需要
function requestData(sendMessage,num){
sendMessage(num)+"-新的数据";
}
requestData(function(num){
console.log(num);
},10);
函数内部属性
在函数的内部 有两个特殊的对象:arguments 和 this arguments 是一个类数组对象 包含着传入函数中的所有参数 主要用途是保护函数参数 但是这个对象还有一个 名叫 callee 的属性 该属性是一个指针 指向拥有arguments对象的函数
递归函数
实现阶乘就必须使用递归函数
function num(n){
if(n <= 1){
return 1;
}else{
return n * num(n-1);
/*return n * arguments.callee(n-1);*/
}
}
/* 理解递归 */
4*3
4*3*2
4*3*2*1
4*3*2
4*6
24
console.log(num(4)); //4 * 3 * 2 * 1
我们实现了一个阶乘函数 但是现在如果我们的函数名称发生了改变 那么我们函数内部的函数也是需要进行修改的 如果有多个函数名的话我们难不成要都修改了吗?
其实我们这时候就可以通过 arguments.callee() 来搞定这个问题 详细代码例如上面的注释内容
THIS
函数内部另一个特殊对象时 this 其实 this 就相当于它被调用时那个函数的作用域
但是这个也有点不太准确 因为this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
当在全局作用域中使用 this 的话 this 指向的使 window 对象
window 是 JS 中最大的对象 最外围的
console.log(window); // window
console.log(this); // window
我们可以发现通过给 window 添加属性可以直接获取到
window.color = "蓝色的";
console.log(this.color);
以下例子演示了this的指向
window.color = "蓝色的";
var obj = {
color:"红色的",
sayHello:function(){
return this.color;
}
}
console.log(this.color);
console.log(obj.sayHello());
全局作用域下的函数内部的this也是指向 window
window.color = "蓝色的";
function sayHello(){
return this.color;
}
console.log(window.sayHello());
下面例子再次加深this的理解
window.color = "蓝色的";
function sayHello(){
return this.color;
}
var obj = {
color:"红色的"
}
obj.sayHello = sayHello;
console.log(window.sayHello());
console.log(obj.sayHello());
简单来说 就是谁调用某个属性或者方法那么this就指向谁
持续看例子:
function a(){
var user = "xxx";
console.log(this.user); //undefined
console.log(this); //Window
}
a();
按照我们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就可以证明
function a(){
var user = "xxx";
console.log(this.user); //undefined
console.log(this); //Window
}
window.a();
和上面代码一样吧,其实alert也是window的一个属性,也是window点出来的
持续看例子:
var o = {
user:"xxx",
fn:function(){
console.log(this.user); //xxx
}
}
o.fn();
这里的this指向的是对象o,因为你调用这个fn是通过o.fn()执行的,那自然指向就是对象o 在调用的时候才能决定,谁调用的就指向谁
持续看例子:
var o = {
user:"追梦子",
fn:function(){
console.log(this.user); //追梦子
}
}
window.o.fn();
这段代码和上面的那段代码几乎是一样的,但是这里的this为什么不是指向window,如果按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,我们创建的变量实际上是给window添加属性,所以这里可以用window点o对象
这里先不解释为什么上面的那段代码this为什么没有指向window,我们再来看一段代码
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //12
}
}
}
o.b.fn();
这里的输出结果也是 12
总结: this只会指向直接调用他的那个对象 即使调用他的那个对象也被调用 仍然指向他的直接上级的对象
再看例子:
var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西
特殊的情况:
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
这里this指向的是window this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的
然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window 因为最后是被wwindow调用执行的
this 碰上 return
当我们函数返回一个对象时再去实例化这个时候这个内部的this指向就指向了 window
function fn() {
this.aaa = "Lucson";
return {};
}
let v = new fn();
console.log(v.aaa); // undefined
当我们函数返回一个不是对象的内容再去实例化这个时候这个内部的this指向就不会指向 window 了
function fn() {
this.aaa = "Lucson";
return 123;
}
let v = new fn();
console.log(v.aaa); // Lucson
函数的属性和方法
JS 中 函数是对象 因此函数也拥有属性和方法
每个函数都包含两个属性 一个 是 length 一个 是 prototype
length 表示函数希望接受参数的个数
function box(name,age){}
console.log(box.length); // 2
apply,call,bind
prototype
对于 prototype 属性 他是保存所有实例方法的所在 也就是原型 这个属性现在暂时不讲 我们在面向对象的时候在去说它
prototype 下面有两个方法
apply 和 call 是一样的 不一样的是 传递参数的方式不同 两个都能通过假冒其他的对象去调用其他对象的方法
call 对象冒充
function box(num1,num2){
return num1+num2;
}
function sum(num1,num2){
return box.call(this,num1,num2);
}
console.log(sum(10,10));
传递参数调用方法不是 apply 和 call 真正需要使用的地方 它们的主要作用是修改作用域
使用 apply 和 call 的最大好处就是对象不需要与方法发生耦合 就是相互关联 那么扩展和维护就会产生一些连锁反应
例子
var color = "red";
function sayColor(){
return this.color;
}
var obj = {
color:"blue"
}
// obj.sayColor = sayColor; 这一句不需要了 断开关联
console.log(sayColor.call(obj));
console.log(sayColor.call(window));
apply 对象冒充
function box(num1,num2){
return num1+num2;
}
function sum(num1,num2){
return box.apply(this,[num1,num2]);
}
console.log(sum(10,10));
bind
通过 bind 方法 可以改变函数内部的this指向 但是我们使用apply和call冒充之后是直接调用了函数而使用bind仅仅是绑定而不会同时调用
他返回的使一个函数所以我们需要再加一个小括号进行调用
window.color = "蓝色的";
function sayHello(num){
return this.color+num;
}
var obj = {
color:"红色的"
}
concole.log(sayHello.bind(this,10)());
concole.log(sayHello.bind(obj,10)());
call函数自定义
其实 call 函数 就是在对应的对象里添加一个函数 达到调用的目的
function Fn(a,b,c,d,e) {
console.log(this.clothes,a,b,c,d,e);
return a;
}
const o = {
clothes:"shirt",
};
Function.prototype.myCall = function (o) {
o = o || window;
const newArgs = [];
o.fn = this;
for(let i = 1 ; i < arguments.length; i++){
newArgs.push("arguments["+i+"]");
}
let res = eval("o.fn("+newArgs+")");
delete o.fn;
return res;
};
console.log(Fn.myCall(o,1,2,3,4,5));
apply函数自定义
function Fn(a,b,c,d,e) {
console.log(this.clothes,a,b,c,d,e);
return a;
}
const o = {
clothes:"shirt",
};
Function.prototype.myApply = function (o,arrArgs) {
o = o || window;
const newArgs = [];
o.fn = this;
let res = null;
if(!arrArgs){
res = o.fn();
}else {
for(let i = 0 ; i < arrArgs.length; i++){
newArgs.push("arrArgs["+i+"]");
}
res = eval("o.fn("+newArgs+")");
}
delete o.fn;
return res;
};
console.log(Fn.myApply(o,[{},2,3,4,5]));
bind函数自定义
function Fn(a,b,c,d) {
console.log(this.clothes,a,b,c,d);
return a;
}
const o = {
clothes:"shirt",
};
Function.prototype.myBind = function (o) {
let that = this,args1 = Array.prototype.slice.call(arguments,1),args2;
return function () {
args2 = Array.prototype.slice.call(arguments);
that.apply(o,args1.push.apply(args1,args2) && args1);
}
};
Fn.myBind(o,1,2,3)(4);
函数的执行机制
函数在哪存储 如何存储的?
我们都知道 在JS中分为两大类数据类型 一种是基本类型 一种是引用类型
函数也是属于引用类型 也是在堆中进行存储的 不过函数在 堆空间的存储就是一串字符串
同时也会分配一个引用地址 在函数调用时会在栈中运行 通过函数名访问到当前这个堆中存储的函数字符串 然后进行解析
ECStack(Execution Context Stack) 执行环境栈
ECStack 执行环境栈,它也是浏览器从内存中拿出的一块内存,但是是栈结构内存,作用是执行代码,而非保存代码
EC(Execution Context) 执行上下文(词法作用域)
EC 执行上下文,EC不同于ECStack它虽然同样是栈内存,但是它的作用是为了在函数执行时,区分全局和函数作用域执行所处的不同作用域(保障每个词法作用域下的代码的独立性
GO(Global Object) 全局对象
浏览器会将供JS调用的属性和方法存放在GO中(内置对象),同时浏览器会声明一个名为window的属性指向这个对象
AO 私有变量对象 & VO 全局变量对象 (Active Object & Variable Object)
代码执行时,会创建变量,VO就是用于存储这些变量的空间
VO通常用于存放全局变量(全局变量一般情况下是不被释放的),而函数执行或其他情况下形成的私有上下文的变量存储在AO中,AO是VO的一种,区别是AO通常指的是进出栈较为频繁的对象
带VAR和不带VAR的区别
- 带VAR相当于给VO(G)添加一个变量,同时映射给window【GO】设置一个属性
- 不带VAR相当于只给window【GO】设置一个属性
函数的创建和执行
函数创建时,会从内存中新建一块堆内存来存储代码块。在函数执行时,会从堆中取出代码字符串,存放在新建的栈中
创建函数
- 开辟堆内存(16进制得到内存地址)
- 声明当前函数的作用域(函数创建的上下文才是他的作用域,和在那执行的无关)
- 把函数的代码以字符串的形式存储在堆内存中(函数再不执行的情况下,只是存储在堆内存中的字符串)
- 将函数堆的地址,放在栈中供变量调用(函数名)
准备执行函数
- 会形成一个全新的执行上下文EC(xx)(目的是供函数体中的代码执行),然后进栈(ECStack执行环境栈)执行
- 在私有上下文中有一个存放变量的变量对象AO
代码执行之前
- 初始化作用域链<自己的上下文,函数的作用域>
- 初始化this(箭头函数没有this)
- 初始化arguments实参集合(箭头函数没有arguments)
- 形参赋值(形参变量是函数的私有变量,需要存储在AO中)
- 变量提升(在私有上下文中声明的变量都是私有变量)
代码执行
把之前在函数堆中存储的字符串拿过来在当前上下文中执行
出栈
字符串函数
通过学习大量的字符串处理函数 让我们对字符串的处理游刃有余 可以进行多样的格式化操作
String 构造函数
要注意实例出来的String可直接赋值的String的区别
var str1 = new String("Lucson");
var str2 = "Lucson";
console.log(str1 == str2); // true
console.log(str1 === str2); // false
console.log(typeof str1); // object
console.log(typeof str2); // string
console.log(str2[0]); // u
console.log(str1[0]); // u
length属性
获取到字符串的长度
var str = "Lucson";
console.log(str.length); // 6
var str1 = "中文";
console.log(str1.length); // 2
var str1 = "\n\t";
console.log(str1.length); // 2
字符方法
charAt()
功能
返回字符串中第N个字符
参数
下标
返回值
合法范围 返回地n个字符的实际值
超出范围 返回空字符串
例子
var str = "Lucson";
console.log(str.charAt(0)) // L
charCodeAt()
功能
返回字符串中第N个字符的代码
参数
超出范围 返回NaN
返回值
- 内容
- string中第N个字符的Unicode编码
- 范围
- 0 - 65535 之间
例子
var str = new String("Lucson");
console.log(str.charCodeAt(2)); // c的 Unicode 值 99
console.log(str.charCodeAt(6)); // NaN 超出范围
fromCharCode()
功能
根据字符编码创建字符串
参数
0个或者多个参数,代表字符的Unicode编码
返回值
由指定编码字符组成的新字符串
特性
静态方法 实为构造函数String的属性
例子
console.log(String.fromCharCode("99")); // c
charCodeAt 和 fromCharCode 互为反向操作
位置方法
indexOf()
功能
根据字符串查找位置下标
从前向后进行检索 看其是否含有子串
参数
- 可选
- 将要查询的字符串
- 必选
- 开始查找位置的下标
- 值为负数 视为0
- 省略从起始位开始找
- 超出0~length-1返回-1
- 开始查找位置的下标
返回值
- 找到
- 子串首次出现的下标
- 未找到
- 返回-1
例子
var str = "Lucson";
console.log(str.indexOf("u")) // 1
console.log(str.indexOf("u",2)) // -1
lastIndexOf()
功能
根据字符串查找位置下标
从后向前进行检索 看其是否含有子串
参数
- 可选
- 将要查询的字符串
- 必选
- 开始查找位置的下标
- 值为负数 视为0
- 省略从起始位开始找
- 超出0~length-1返回-1
- 开始查找位置的下标
例子
var str = "Lucson";
console.log(str.lastIndexOf("u")) // 1
console.log(str.lastIndexOf("u",2)) // 1
匹配方法
match()
功能
找到一个或者多个正则表达式的匹配
参数
要进行模式匹配的正则表达式
非正则表达式 将其传递给RegExp()构造函数,并转换为正则表达式对象
返回值
- 存放匹配结果的数组
- 有全局标记g
- 执行全局搜索
- 找到
- 返回数组
- 内容
- 所有匹配的子串
- 缺陷
- 没有派生属性
- 不提供与子表达式的文本信息
- 不声明每个字串匹配的位置
- 弥补
- 采用 RegExp.exec() 方法
- 内容
- 返回数组
- 没找到
- 返回 Null
- 找到
- 执行全局搜索
- 无全局标记g
- 执行一次搜索
- 找到
- 返回数组
- 内容
- 第0个元素是匹配的文本
- 其他元素是与正则表达式匹配的文本
- 属性
- input
- 调用该方法的字符串对象
- index
- 匹配文本在字符串中的位置
- input
- 内容
- 返回数组
- 没找到
- 返回 Null
- 找到
- 执行一次搜索
- 有全局标记g
例子
var str = "Lucson 1 Lucson 2";
console.log(str.match("1")); // ["1", index: 7, input: "Lucson 1 Lucson 2"]
console.log(str.match(/\d+/g)); // ["1", "2"]
console.log(str.match("99")); // null
search()
功能
检索字符串中与正则表达式匹配的子串
参数
与 match() 一样
返回值
- 找到
- 字符串中第一个与正则表达式相匹配的子串的起始位置
- 未找到
- -1
特性
忽略全局标记 g
例子
console.log(str.search("L")); // 0
console.log(str.search(/L/)); // 0
console.log(str.search(/L/g)); // 0
console.log(str.search("z")); // -1
replace()
功能
替换一个与正则表达式匹配的子串
参数
- 参数1
- 需要进行替换的正则表达式对象或者字符串
- 参数2
- 替换文本或者替换函数
特性
如果参数1是字符串则仅进行一次匹配替换
如果使用正则全局匹配则能多次替换
返回值
一个全新的替换后的字符串
例子
var str = "LucsonL";
console.log(str.replace("L","P")); // PucsonL
console.log(str.replace(/L/,"P")); // PucsonL
console.log(str.replace(/L/g,"P")); // PucsonP
console.log(str.replace(/L/g,function(){
return "P";
})); // PucsonP
console.log(str.replace(/qwe/g,"P"));// LucsonL
split()
功能
根据指定分隔符将字符串分割成多个子串,并返回数组
参数
- 必选
- 指定的分隔符
- 可选
- 指定数组的长度
例子
var str = "Lucson";
console.log(str.split(",")) // ["L", "u", "c", "s", "o", "n"]
console.log(str.split(",")) // ["Lucson"]
console.log(str.split("",10)) // ["L", "u", "c", "s", "o", "n"]
console.log(str.split("",2)) // ["L", "u"]
操作方法
拼接方法
concat()
语法
string.concat(value1,value2,…)
功能
链接字符串
参数
要连接到字符串上的一个或者多个值
返回值
一个全新的字符串
特性
功能与+相同 但是不会影响到原始字符串 而是返回一个全新的字符串
例子
var str = "LucsonL";
console.log(str.concat("123","789"),str); // LucsonL123789 LucsonL
console.log(str.concat("123",[123,456]),str); //LucsonL123123,456 LucsonL
截取方法
slice()
功能
根据下标截取子串
参数
参数一是开始截取的下标 参数二是结束的下标
省略参数2 则以字符串长度为准
特性
截取的位置是从参数一开始到参数二前一位结束
负值参数与字符串长度相加
也可以作用于数组上
源字符串不受影响
例子
var str = "LucsonL";
console.log(str.slice(0,6)); // Lucson
console.log(str.slice(0,str.length)); // LucsonL
console.log(str.slice(-4,-2)); // so
var arr = [1,2,3,4,5,6,7];
console.log(arr.slice(0,6)); // [1, 2, 3, 4, 5, 6]
console.log(arr.slice(0,str.length)); // [1, 2, 3, 4, 5, 6, 7]
console.log(arr.slice(-4,-2)); // [4, 5]
substring()
特性
负值参数转换为0 不能用作数组 其他和 slice 一致
例子
var str = "LucsonL";
console.log(str.substring(0,6)); // Lucson
console.log(str.substring(0,-1)) //
console.log(str.substring(-1,6)); // Lucson
substr()
功能
截取字符串
参数
参数一负数 和 字符串长度相加
参数二负数转换为0
例子
var str = "LucsonL";
console.log(str.substr(0,6)); // Lucson
console.log(str.substr(0,10)) // LucsonL
console.log(str.substr(-1,6)); // L
空格处理
trim(),trimLeft(),trimRight()
例子
var str = " Luc sonL ";
console.log(str); // Luc sonL
console.log(str.trim()); //Luc sonL
console.log(str.trimLeft()); //Luc sonL
console.log(str.trimRight()); // Luc sonL
缺陷
无法去除中间空格
比较方法
localCompare()(了解)
功能
用本地特定顺序比较两个字符串
参数
与原字符串进行比较的字符串
返回值
负数 原字符串 < 参数字符串
0 原字符串 = 参数字符串
正数 原字符串 > 参数字符串
转换方法
大小写转换
例子
console.log("Lucson".toUpperCase()) // LUCSON
console.log("Lucson".toLocaleUpperCase()) // LUCSON
console.log("Lucson".toLowerCase()) // lucson
console.log("Lucson".toLocaleLowerCase()) // lucson