1. Js简介
1.1 什么是语言
计算机就是一个由人来控制的机器,人让它干嘛,它就得干嘛。
我们要学习的语言就是人和计算机交流的工具,人类通过语言来控制、操作计算机。
编程语言和我们说的中文、英文本质上没有区别,只是语法比较特殊。
语言的发展:
-
纸带机:机器语言
-
汇编语言:符号语言
-
现代语言:高级语言
1.2 Js起源
JavaScript诞生于1995年,它的出现主要是用于处理网页中的前端验证。
所谓的前端验证,就是指检查用户输入的内容是否符合一定的规则。(浏览器检查格式,如果对再进行提交,不对直接提示,增强了用户体验)
比如:用户名的长度,密码的长度,邮箱的格式等。
除此之外,大部分的动态效果都需要使用js
1.3 js简介
-
JavaScript是由网景公司发明,起初命名为LiveScript,后来由于SUN公司的介入更名为了JavaScript。
-
1996年微软公司在其最新的IE3浏览器中引入了自己对JavaScript的实现JScript。
-
于是在市面上存在两个版本的JavaScript,一个网景公司的JavaScript和微软的JScript。
-
为了确保不同的浏览器上运行的JavaScript标准一致,所以几个公司共同定制了JS的标准名命名为ECMAScript。(就是一个标准,也就是文档)
时间表
年份 | 事件 |
---|---|
1995年 | 网景公司开发了JavaScript |
1996年 | 微软发布了和JavaScript兼容的JScript |
1997年 | ECMAScript第1版(ECMA-262) |
1998年 | ECMAScript第2版 |
1998年 | DOM Level1的制定 |
1998年 | 新型语言DHTML登场 |
1999年 | ECMAScript第3版 |
2000年 | DOM Level2的制定 |
2002年 | ISO/IEC16262:2002的确立 |
2004年 | DOM Level3的制定 |
2005年 | 新型语言AJAX登场 |
2009年 | ECMAScript第5版 |
2009年 | 新型语言HTML5登场 |
1.4 实现
ECMAScript是一个标准,而这个标准需要由各个厂商去实现。
不同的浏览器厂商对该标准会有不同的实现。
浏览器 | JavaScript实现方式 |
---|---|
FireFox | SpiderMonkey |
Internet Explorer | JScript/Chakra |
Safari | JavaScriptCore |
Chrome | v8 (执行最快的一个引擎) |
Carakan | Carakan |
我们已经知道ECMAScript是JavaScript标准。所以一般情况下,这两个词我们认为是一个意思。
但是实际上JavaScript的含义却要更大一些。
一个完整的JavaScript实现应该由以下三个部分构成:
ECMA:标准
DOM:文档对象模型,提供一组对象,用来操作网页
BOM:浏览器对象模型,提供一组对象,用来操作浏览器
1.5 特点
-
解释型语言(不需要编译,和python一样)
-
类似于C和Java的语法结构(废话)
-
动态语言
-
基于原型的面向对象(是面向对象的语言)
本质上都是,向计算机发送指令,是写在网页上的
1.6 helloworld
**三个输出语句:**用于做测试的
控制浏览器弹出一个警告框
alert("Hello World!");
让计算机在页面中输出一个内容
document.write("Hello World!");
向控制台输出一个内容
console.log("Hello World!");
2. js基础
2.1 基础准备
2.1.1 js编写位置(html页面中)
① 可以将js代码编写到标签的onclick
属性中当我们点击按钮时,js代码才会执行
<button onclick="alert(\"Fuck! Do not touch me!\")"></button> <!-- \" 是用于转义 可以直接写成单引号的 -->
② 可以将js代码写在超链接的href
属性中,这样当点击超链接时,没有跳转,会执行js代码
<a href="jacascript:alert('让你点你就点~~')">Try to click me</a>
<a href="jacascript:;">Try to click me</a> <!--这样处理,点击之后超链接不会进行跳转,不做任何处理-->
③ 虽然可以写在标签的属性中,但是他们属于结构与行为耦合,不方便维护,不推荐使用
可以将js代码编写到script
标签, 刷新页面就会立即执行
<script type="text/javascript"> // type 可写可不写,不写默认是这个值
alert("I'm inner script.");
</script>
④ 可以将js代码编写到外部js文件中,然后通过script
标签引入
写到外部文件中可以在不同的页面中同时引用,也可以利用浏览器的缓存机制,推荐使用
<script src="/js/script.js" type="text/javascript"></script>
script
标签一旦用于引入外部文件了,就不能在编写代码了,即使编写了浏览器也会忽略
如果需要则可以在创建一个新的script
标签用于编写内部代码
注意:js代码是从上到下按照顺序一步一步进行执行的
2.1.2 注释
多行注释,注释中的内容不会被执行,但是可以在源代码中查看
/*
多行注释...
多行注释...
多行注释...
*/
单行注释
// 单行注释
2.1.3 注意点
-
JS中严格区分大小写
-
JS中每一条语句以分号
;
结尾,表示指令完成
- 如果不写分号,浏览器会自动添加,但是会消耗一些系统资源
- 而且有些时候,浏览器会加错分号,所以在开发中分号必须写 -
JS中会忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化 (也就是说,缩进与换行对代码不受影响)
2.1.4 字面量和变量
字面量:
字面量,都是一些不可改变的值
比如:1 2 3 4 5 1111111111578148151515
字面量都是可以直接使用,但是我们一般都不会直接使用字面量
变量:
变量可以用来保存字面量,而且变量的值是可以任意改变的变量更加方便我们使用
所以在开发中都是通过变量去保存一个字面量,而很少直接使用字面量
可以通过变量对字面量进行描述
// 声明变量: 在js中使用var关键字来声明一个变量
var a;
// 为变量赋值
a = 123;
a = 456;
a = 123124223423424;
// 声明和赋值同时进行
var b = 789;
var c = 0;
var age = 80;
console.log(age);
2.1.5 标识符
在JS中所有的可以由我们自主命名的都可以称为是标识符
例如:变量名、函数名、属性名都属于标识符
命名一个标识符时需要遵守如下的规则:
-
标识符中可以含有字母、数字、_、$
-
标识符不能以数字开头
-
标识符不能是ES中的关键字或保留字
-
标识符一般都采用驼峰命名法 (这是一个规范,并不是强制要求)
-
-
首字母小写,每个单词的开头字母大写,其余字母小写
helloword -> helloWord
-
关键字
if | else | do | while | for |
---|---|---|---|---|
break | continue | try | catch | finally |
throw | true | false | function | return |
switch | case | null | typeof | instanceof |
new | var | void | in | with |
default | debugger | delete | this |
保留字
class | enum | extends | super | const | export |
---|---|---|---|---|---|
import | implements | let | private | public | yield |
interface | package | protected | static |
其他不建议使用的标识符
boolean | byte | short | char |
---|---|---|---|
float | double | String | Boolean |
Date | Array | Math | Error |
TypeError | URIError | RangeError | ReferenceError |
parselnt | parseFloat | NaN | isNaN |
throws | native | goto | eval |
arguments | isFinite | volatile | abstract |
synchronize | final | encodeURICOmponent | decodeURIComponent |
JS底层保存标识符时实际上是采用的Unicode编码,所以理论上讲,所有的utf-8中含有的内容都可以作为标识符(utf-8中也含有中文,理论上来说中文也是可以作为标识符的,但没有必要)
2.2 数据类型
数据类型指的就是字面量的类型,在JS中一共有六种数据类型
基本数据类型 | String | 字符串 |
---|---|---|
基本数据类型 | Number | 数值 |
基本数据类型 | Boolean | 布尔值 |
基本数据类型 | Null | 空值 |
基本数据类型 | Undefined | 未定义 |
引用类型 | Object | 对象 |
2.2.1 String
JS中,字符串需要使用引号引起来
-
使用单引号或双引号都可以,但不要混合使用
-
同一种引号不能嵌套,双引号不能放双引号,单引号不能放单引号
在字符串中我们可以使用\
作为转义字符,当表示一些特殊符号时可以使用\
进行转义
-
\"
表示"
-
\'
表示'
-
\n
表示换行 -
\t
制表符 -
\\
表示\
注意
var str = "hello";
var str = 'hello'; //单双引号都可以
str = "我说:"今天天气真不错" " //这个是错误的,引号不能嵌套,下面两个都是可以的
str = "我说:'今天天气真不错' "
str = "我说:\"今天天气真不错\" "
2.2.2 Number
在JS中,所有的数值都是Number
类型,包括整数和浮点数(小数)
可以使用一个运算符typeof
,来检查一个变量的类型。语法:typeof 变量
-
检查字符串时,会返回
string
-
检查数值时,会返回
number
var a = 123; //整数
console.log(typeof a) // 输出是number
a = 456.789; //浮点数
a = "123" //字符串
console.log(typeof a) //输出是string ,是小写
(1) MAX_VALUE
JS中可以表示的数字的最大值
Number.MAX_VALUE=1.7976931348623157e+308 , 科学计数法,表示1后面最多308位,不能再大了
如果使用Number表示的数字超过了最大值,则会返回一个Infinity,正无穷
var a = Number.MAX_VALUE * Number.MAX_VALUE;
console.log(a); // Infinity
a = Infinity; // a就是一个字面量,不能加引号,表示正无穷,类型是一个number
console.log(typeof a); // number
(2) MIN_VALUE
大于0的最小值 Number.MIN_VALUE=5e-324
var a = Number.MIN_VALUE * Number.MIN_VALUE;
console.log(a); // 0
-
Infinity
表示正无穷 -
-Infinity
表示负无穷
使用typeof
检查,Infinity
会返回Number
(3) NaN
NaN
是一个特殊的数字,表示Not A Number
var a = 'abc' * 'def';
console.log(a); // NaN ,意思是不会算但又需要返回一个值,所以就成了NaN
使用typeof检查一个NaN也会返回number
var b = NaN; //是一个字面量,可以直接进行使用,但不能加引号
console.log(typeof b); // number
(4) 运算精度
在JS中整数的运算基本可以保证精确
如果使用JS进行浮点运算,可能得到一个不精确的结果
var a = 0.1 + 0.2; //本质就是在计算机转成二进制运算时,无法对小数进行准确的表示
console.log(a); // 0.30000000000000004
所以千万不要使用JS进行对精确度要求比较高的运算,尤其是对钱的计算,这些精密计算应该放到服务器进行处理,而不是前端页面
2.2.3 Boolean
布尔值只有两个,主要用来做逻辑判断
-
true
表示真 -
false
表示假
使用typeof
检查一个布尔值时,会返回boolean
2.2.4 Null
Null(空值)类型的值只有一个,就是null
null这个值专门用来表示一个为空的对象
使用typeof检查一个null值时,会返回object
var a3 = null;
console.log(a3); // null
console.log(typeof a3); // object
2.2.5 Undefined
Undefined(未定义)类型的值只有一个,就是undefind
当声明一个变量,但是并不给变量赋值时,它的值就是undefined
使用typeof检查一个undefined
时,也会返回undefined
var a4;
console.log(a4); // undefind
console.log(typeof a4); // undefind
2.2.6 强制类型转换(基本数据类型)
指将一个数据类型强制转换为其他的数据类型
类型转换主要指,将其他的数据类型,转换为String
、Number
、Boolean
(1) 其他类型转换成String
方法一:调用被转换数据类型的toString()方法
该方法不会影响到原变量,它会将转换的结果返回, 也就是说 xxx.toString() 返回新的值,不改变原值
// Number转换为String
var a1 = 123;
var b1 = a1.toString();
console.log(typeof a1); // number
console.log(typeof b1); // string
// Boolean转换为String
var a2 = true;
var b2 = a2.toString();
console.log(typeof a2); // boolean
console.log(typeof b2); // string
但是注意:null
和undefined
这两个值没有toString()
,如果调用他们的方法,会报错
// Null转换为String
var a3 = null;
var b3 = a3.toString(); // UncaughtTypeError: Cannot read property 'toString' of null
console.log(typeof a3);
console.log(typeof b3);
// Undefined转换为String
var a4 = undefined;
var b4 = a4.toString(); // UncaughtTypeError: Cannot read property 'toString' of undefined
console.log(typeof a4);
console.log(typeof b4);
方法二:调用String()函数,并将被转换的数据作为参数传递给函数
使用String()
函数做强制类型转换时,对于Number
和Boolean
实际上就是调用的toString()
方法
但是对于null
和undefined
,就不会调用toString()
方法,而是将
-
null
直接转换为"null"
-
undefined
直接转换为"undefined"
// Number转换为String
var a1 = 123;
var b1 = String(a1);
console.log(typeof a1); // number
console.log(typeof b1); // string
// Boolean转换为String
var a2 = true;
var b2 = String(a2);
console.log(typeof a2); // boolean
console.log(typeof b2); // string
// Null转换为String
var a3 = null;
var b3 = String(a3);
console.log(typeof a3); // object
console.log(typeof b3); // string
// Undefined转换为String
var a4 = undefined;
var b4 = String(a4);
console.log(typeof a4); // undefined
console.log(typeof b4); // string
(2) 其他数据类型转换为Number
方法一:使用Number()函数
-
字符串 --> 数字
-
- 如果是纯数字的字符串,则直接将其转换为数字
-
- 如果字符串中有非数字的内容,则转换为
NaN
- 如果字符串中有非数字的内容,则转换为
-
- 如果字符串是一个空串或者是一个全是空格的字符串,则转换为
0
- 如果字符串是一个空串或者是一个全是空格的字符串,则转换为
// ① 纯数字的字符串
var a1 = '123';
a1 = Number(a1);
console.log(typeof a1); // number
console.log(a1); // 123
// ② 非数字的内容
// var a2 = 'abc';
var a2 = undefined;
a2 = Number(a2);
console.log(typeof a2); // number
console.log(a2); // NaN
// ③ 空串
// var a3 = ' '; 空格
var a3 = null; 空
a3 = Number(a3);
console.log(typeof a3); // number
console.log(a3); // 0
-
布尔 --> 数字
-
true
转成1
-
false
转成0
var a4 = true;
a4 = Number(a4);
console.log(typeof a4); // number
console.log(a4); // 1
var a5 = false;
a5 = Number(a5);
console.log(typeof a5); // number
console.log(a5); // 0
方法二:专门用来对付字符串
,对字符串进行有效提取
-
parseInt()
把一个字符串转换为一个整数:可以将一个字符串中的有效整数部分取出来,然后转换为Number -
parseFloat()
把一个字符串转换为一个浮点数:可以同时将一个字符串中的有效小数部分取出来,然后转换为Number既取整数又取小数 -
如果对非String使用
parseInt()
或parseFloat()
,它会先将其转换为String,然后再操作
/* parseInt */
var a1 = "123";
a1 = parseInt(a1);
console.log(typeof a1); // number
console.log(a1); // 123
var a2 = "123.456";
a2 = parseInt(a2);
console.log(typeof a2); // number
console.log(a2); // 123
var a3 = "123px";
a3 = parseInt(a3);
console.log(typeof a3); // number
console.log(a3); // 123
123a567 => 123
b123a567 =>NaN
// var a4 = null;
// var a4 = undefined;
// var a4 = '';
// var a4 = 'abc';
// var a4 = true;
var a4 = false; 过程: false => 'false' => NaN
a4 = parseInt(a4);
console.log(typeof a4); // number
console.log(a4); // NaN
(3)其他数据类型转换为Boolean
**方法一:**使用Boolean()
函数
-
数字-—->布尔
-
- 除了
0
和NaN
,其余的都是true
- 除了
-
字符串-—->布尔
-
- 除了空串,其余的都是
true
- 除了空串,其余的都是
-
null
和undefined
都会转换为false
-
对象也会转换为
true
// - 数字-—->布尔 // - 除了`0`和`NaN`,其余的都是`true` // var a1 = 0; var a1 = NaN; a1 = Boolean(a1); console.log(a1); // false var a2 = 123; a2 = Boolean(a2); console.log(a2); // true // - 字符串-—->布尔 // - 除了空串,其余的都是`true` var a3 = "123"; a3 = Boolean(a3); console.log(a3); // true var a4 = " "; a4 = Boolean(a4); console.log(a4); // true var a5 = ""; a5 = Boolean(a5); console.log(a5); // false // - `null`和`undefined`都会转换为`false` // var a6 = null; var a6 = undefined; a6 = Boolean(a6); console.log(a6); // false
方法二: 隐式类型转换
为任意的数据类型做两次非运算,即可将其转换为布尔值(下一节会介绍)
var a = "123"; var b = !!a; console.log("a="+a+",b="+b); // a=true,b=true
(4) 其他进制的数字
在js中,如果需要表示16进制的数字,则需要以0x
开头
如果需要表示8进制的数字,则需要以0
开头
如果需要表示2进制的数字,则需要以0b
开头,但是不是所有的浏览器都支持
// 十六进制数字
var a = 0x10;
console.log(a); // 16
a = 0xff;
console.log(a); // 255
a = 0xCafe;
console.log(a); // 51966
a = "0x70";
a = parseInt(a,16);
console.log(a); // 112
// 八进制数字
a = 070;
console.log(a); // 56
a = "070";
a = parseInt(a,8);
console.log(a); // 56
// 二进制数字
a = 0b10;
console.log(a); // 2
2.3 运算符
pass
2.4 流程控制
pass
2.5 对象
JS中数据类型
-
String 字符串
-
Number数值
-
Boolean 布尔值
-
Null空值
-
Undefined 未定义
以上这五种类型属于基本数据类型,以后我们看到的值只要不是上边的5种,全都是对象
基本数据类型都是单一的值"hello" 123 true
,值和值之间没有任何的联系。
在JS中来表示一个人的信息(name gender age):
var name = "孙悟空";
var gender = "男";
var age = 18;
如果使用基本数据类型的数据,我们所创建的变量都是独立,不能成为一个整体。
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。
2.5.1 对象的分类
(1) 内建对象
由ES标准中定义的对象,在任何的ES的实现中都可以使用 (浏览器或者是node.js都可以使用)
常见内建对象有以下,都可以直接通过new调用构造函数创建对象实例:
-
Object、Function、Array、Date、RegExp,String、Number、Boolean、
-
Error(EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError)
// Math
Math.sqrt(2);
// String
String(2);
// Number
Number("2");
(2) 宿主对象
由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象,不需要创建对象,直接使用
比如 BOM DOM
// console
console.log("hello");
// document
document.write("hello");
JavaScript实现包括三部分:
组成 | 作用 | 地位 | 例子 |
---|---|---|---|
ES(ECMAScript) | 描述JS语法和基本对象 | 核心 | |
DOM(Document Object Model 文档对象模型) | HTML和XML的应用程序接口,处理网页内容的方法和接口 | W3C标准 | document |
BOM(Browser Object Model 浏览器对象模型) | 描述与浏览器进行交互的方法和接口,处理浏览器窗口和框架 | 浏览器厂商对DOM的实现 | window |
(3) 自定义对象 (最难的)
由开发人员自己创建的对象
使用new
关键字调用的函数,是构造函数constructor
,构造函数是专门用来创建对象的
函数使用typeof
检查一个对象时,会返回object
在对象中保存的值称为属性
-
添加或修改对象属性的语法:
对象.属性名=属性值;
-
读取对象属性的语法:
对象.属性名
-
删除对象属性的语法:
delete 对象.属性名;
/*
要使用对象(塑料袋),必须先创建一个对象 var obj = new Object();
*/
//创建对象,使用new关键字调用的函数,是构造函数constructor,构造函数是专门用来创建对象的
var obj = new Object();
// 向obj中添加一个name属性
obj.name = "孙悟空";
// 向obj中添加一个gender属性
obj.gender = "男";
// 向obj中添加一个age属性
obj.age = "18";
// 打印obj
console.log(typeof obj); // object
console.log(obj); // {"age":"18","gender":"男","name":"孙悟空"}
console.log(obj.name); // 孙悟空
注意:如果读取对象中没有的属性,不会报错而是会返回undefined
属性名
对象的属性名不强制要求遵守标识符的规范,什么乱七八糟的名字都可以使用,但是我们使用是还是尽量按照标识符的规范去做
如果要使用特殊的属性名,不能采用.
的方式来操作,而需要使用另一种语法:对象["属性名"]=属性值
,读取时也需要采用这种方式
obj["name"] = "齐天大圣";
console.log(obj["name"]); // 齐天大圣
使用[]
这种形式去操作属性,更加的灵活,在[]
中可以直接传递一个变量,这样变量值是哪个就会读取哪个属性
var n = "nihao";
obj[n] = "你好";
console.log(obj[n]); // 你好
回顾:.
、[]
、new
这几个运算符的优先级是最高的
属性值
JS对象的属性值,可以是任意的数据类型,包括对象
var obj2 = new Object();
obj2.name = "猪八戒";
obj.bro = obj2;
console.log(obj.bro.name); // 猪八戒
in 运算符
通过该运算符可以检查一个对象中是否含有指定的属性
如果有则返回true
,没有则返回false
语法:"属性名" in 对象
console.log("test" in obj); // false
console.log("name" in obj); // true
2.5.2 基本数据类型和引用数据类型
基本数据类型 String Number Boolean Null Undefined
引用数据类型 Object
(1) 基本数据类型
-
JS中的变量都是保存到栈内存中的,基本数据类型的值直接在栈内存中存储
-
值与值之间是独立存在,修改一个变量不会影响其他的变量
var a = 1;
var b = a; // 此时,b将a所保存的值复制了一份,保存到自己的值里面,所以 a 的 1 与 b 的 1 本质上不是同一个
console.log("a=" + a + ", b=" + b); // a=1, b=1
b = 2;
console.log("a=" + a + ", b=" + b); // a=1, b=2
(2) 引用数据类型
-
对象是保存到堆内存中的
-
每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用)
-
如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响
var obj3 = obj;
obj3.name = "斗战胜佛";
console.log(obj.name); // 斗战胜佛
console.log(obj3.name); // 斗战胜佛
(3) 比较
- 当比较两个基本数据类型的值时,就是比较值。
- 而比较两个引用数据类型时,它是比较的对象的内存地址,如果两个对象是一摸一样的,但是地址不同,它也会返回false
var o1 = new Object();
var o2 = new Object();
o1["name"] = "周瑜";
o2["name"] = "周瑜";
console.log(o1 == o2); // false
2.5.3 对象字面量
使用对象字面量,可以在创建对象时,直接指定对象属性的语法:{属性名: 属性值, 属性名: 属性值...}
对象字面量的属性名可以加引号也可以不加(建议不加),如果要使用一些特殊的名字,则必须加引号
属性名和属性值是一组一组的名值对结构,名和值之间使用:
连接,多个名值对之间使用,
隔开
如果一个属性之后没有其他的属性了,就不要写,
了
var obj = {
name: "孙悟空",
age: 1000,
gender: "男",
bor:{
name: "猪八戒"
}
}
console.log(obj); // {"age":1000,"bor":{"name":"猪八戒"},"gender":"男","name":"孙悟空"}
2.5.4 方法
对象的属性值可以是任何的数据类型,也可以是个函数(下一节知识)
函数也可以称为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法
调用函数就说调用对象的方法,但是它只是名称上的区别没有其他的区别
var obj2 = {
name: "猪八戒",
age: 18,
sayName: function() {
console.log(obj2.name);
}
};
obj2.sayName(); // 猪八戒
2.5.5 枚举对象中的属性
使用for...in
语句语法:
for(var 变量 in 对象) {
语句...
}
for...in
语句对象中有几个属性,循环体就会执行几次
每次执行时,会将对象中的一个属性的名字赋值给变量
var obj = {
name: "孙悟空",
age: 1000,
gender: "男",
address: "花果山"
};
for(var key in obj){
console.log(key + "=" + obj.key);
// name=undefined
// age=undefined
// gender=undefined
// address=undefined
console.log(key + "=" + obj[key]);
// name=孙悟空
// age=1000
// gender=男
// address=花果山
}
/* 取出来的 key = "name"
key = "age"
...
所以只能是使用[]来进行取值 */
2.6 函数
函数也是一个对象,可以封装一些功能(代码),在需要时可以执行这些功能(代码),可以保存一些代码在需要的时候调用
使用typeof
检查一个函数对象时,会返回function
2.6.1 创建函数的方式
(1) 通过new进行创建
// 创建一个函数对象
// 可以将要封装的代码以字符串的形式传递给构造函数
var fun = new Function("console.log('Hello World.');");
// 封装到函数中的代码不会立即执行
// 函数中的代码会在函数调用的时候执行
// 调用函数语法:函数对象()
// 当调用函数时,函数中封装的代码会按照顺序执行
fun(); // Hello World.
(2) 使用函数声明
function 函数名([形参1, 形参2...形参N]) {
语句...
}
// 调用函数
函数名(); //常用
(3) 使用函数表达式(匿名函数)
var 函数名 = function([形参1, 形参2...形参N]) {
语句...
};
// 调用函数
函数名();
//示例如下:
var fun1 = function(){
console.log("Hello world.");
alert("Hello World!");
document.write("Helloworld");
};
fun1();
2.6.2 参数
定义一个用来求两个数和的函数
可以在函数的()
中来指定一个或多个形参(形式参数)多个形参之间使用,
隔开,声明形参就相当于在函数内部声明了对应的变量
在调用函数时,可以在()
中指定实参(实际参数)
-
调用函数时解析器不会检查实参的类型。所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查
-
调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值。如果实参的数量少于形参的数量,则没有对应实参的形参将是
undefined
(意思就是,传入的实参可多也可少,不会影响函数的执行,至于是否异常就是执行函数本身的问题了)
// 创建一个函数,用来计算三个数的和
function sum(a, b, c) {
alert(a + b + c);
}
sum(1, 2, 3, 4); // 6
实参可以是任意的数据类型,也可以是一个对象。当我们的参数过多时,可以将参数封装到一个对象
function sayHello(o){
console.log("我是" + o.name
+ ",今年我" + o.age
+ "岁了,我是一个" + o.gender
+ "人,我住在" + o.address);
}
var obj = {
name: "孙悟空",
age: 1000,
gender: "男",
address: "花果山"
};
sayHello(obj); // 我是孙悟空,今年我1000岁了,我是一个男人,我住在花果山
实参可以是一个对象,也可以是一个函数
function calCirc(radius) {
return Math.PI * Math.pow(radius, 2);
}
function fun(a){
console.log("a = " + a);
}
fun(calCirc);
// a = function calCirc(radius) {
// return Math.PI * Math.pow(radius, 2);
// }
fun(calCirc(10)); // a = 314.1592653589793
2.6.3 返回值
可以使用return来设置函数的返回值语法:return 值
return后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果
在函数中return后的语句都不会执行
如果return语句后不跟任何值,就相当于返回一个undefined;如果函数中不写return,则也会返回undefined
return后可以跟任意类型的值
// 创建一个函数,用来计算三个数的和
function sum(a, b, c) {
// var result = a + b + c;
// return result;
return a + b + c;
}
// 调用函数
// 变量result的值就是函数的执行结果
// 函数返回什么result的值就是什么
var result = sum(1, 2, 3);
console.log("result = " + result);
2.6.4 自执行函数
函数定义完,立即被调用,这种函数叫做立即执行函数
立即执行函数往往只会执行一次
(function (形参){})(实参)
!function(形参){}(实参)
//示例如下:
// 函数对象()
(function(){
console.log("I'm anoymous function.");
})(); // I'm anoymous function.
!function(a, b){
console.log(a + b);
}(2,3); // 5
2.7 作用域(Scope)
作用域指一个变量的作用的范围
在JS中一共有两种作用域:
-
全局作用域
-
函数作用域
2.7.1 全局作用域
直接编写在script标签中的JS代码,都在全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁
在全局作用域中有一个全局对象window
,它代表的是一个浏览器的窗口,由浏览器创建,可以直接使用
在全局作用域中:
-
创建的变量都会作为window对象的属性保存
-
创建的函数都会作为window对象的方法保存
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
var a = 3;
console.log(window.a); //3
console.log(a); //3
b = 3;
console.log(b); //3
2.7.2 变量和函数声明提前
使用var关键字声明的变量,会在所有的代码执行之前被声明
但是如果声明变量时不使用var关键字,则变量不会被声明提前
// 1、变量的声明提前
console.log("a = " + a); // a = undefined
var a = "abc";
// ======相当于======
var a;
console.log("a = " + a); // a = undefined
a = "abc";
// 2、没有变量的声明提前,报错
console.log("b = " + b); // UncaughtReferenceError: b is not defined
b = "abc";
// ======相当于======
console.log("b = " + b); // UncaughtReferenceError: b is not defined
window.b = "abc";
使用函数声明形式创建的函数function
, 它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数
fun1(); // fun1...
fun2(); // UncaughtTypeError: fun2 is not a function
// 函数声明,会被提前创建
function fun1(){
console.log("fun1...");
}
// 函数表达式,不会被提前创建(变量会被提前声明,但函数不会被提前创建)
var fun2 = function(){
console.log("fun2...");
}
2.7.3 函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
-
在函数作用域中可以访问到全局作用域的变量
-
在全局作用域中无法访问到函数作用域的变量
当在函数作用域操作一个变量时,它会先在自身作用域中寻找,
-
如果有就直接使用
-
如果没有则向上一级作用域中寻找,直到找到全局作用域
-
如果全局作用域中依然没有找到,则会报错
在函数中要访问全局变量可以使用window
对象
var a = 10;
function fun2(){
var a = 20;
function fun3(){
var a = 30;
console.log("fun3 ==> a = " + a); // fun3 ==> a = 30
}
fun3();
console.log("fun2 ==>a = " + a); // fun2 ==>a = 20
console.log("a = " + window.a); // a = 10
}
fun2();
console.log("a = " + a); // a = 10
在函数作用域也有声明提前的特性,使用var
关键字声明的变量,会在函数中所有的代码执行之前被声明
函数声明也会在函数中所有的代码执行之前执行
// 在函数作用域也有声明提前的特性,使用`var`关键字声明的变量,会在函数中所有的代码执行之前被声明
function func1(){
console.log(a);
var a = "func1";
// 函数声明也会在函数中所有的代码执行之前执行
func2(); // fun2...
function func2(){
console.log("fun2...");
}
}
func1(); // undefined
在函数中,不适用var
声明的变量都会成为全局变量
// 函数声明且调用
func3();
function func3() {
a = 4;
}
console.log("a = " + window.a); // a = 4
console.log("a = " + window["a"]); // a = 4
console.log("a = " + a); // a = 4
// 函数声明不调用
function func4() {
b = 4;
}
console.log("b = " + window.b); // b = 4
console.log("b = " + window["b"]); // b = 4
console.log("b = " + b); // UncaughtReferenceError: b is not defined
定义形参就相当于在函数作用域中声明了变量
var e = 10;
function fun5(e){
console.log(e);
}
fun5(); // undefined
fun5(55); // 55
3. js高级
3.1 函数高级
3.1.1 函数的概念
1 什么是函数
- 实现特定功能的 n 条语句的封装体
- 只有函数是可以执行的,其它类型的数据不能执行
- 凡是通过 new Function() 创建的对象都是函数对象(本质)
2 为什么要用函数?
- 提高代码复用
- 便于阅读交流
3 定义函数
- 函数声明
- 表达式
//函数声明
function fn1(){
console.log('fn1()')
}
const fn2 = function(){
console.log('fn2()')
};
//表达式
const fn2 = () => console.log('fn2()') //这个是箭头函数吗,看不太懂
3.1.2 调用(执行) 函数
test()
直接调用,this = window
obj.test()
通过对象调用this = obj
var obj =
new test()
new 调用this = obj
test.call/apply(obj)
临时让 test 成为 obj 的方法进行调用this = obj
改变this指向
var obj = {}
//此处不能使用箭头函数,因为箭头函数会改变this指向
function test2 () {
this.xxx = 'shangguigu'
}
// obj.test2() 不能直接, 根本就没有
test2.call(obj) // 可以让一个函数成为指定任意对象的方法进行调用
console.log(obj.xxx) // 输出为 shangguigu
3.1.3 回调函数
(1) 什么函数才是回调函数?
- 你定义的
- 你没有调
- 但最终它执行了(在某个时刻或某个条件下)
(2) 常见的回调函数?
- dom事件回调函数 ==>发生事件的dom元素
- 定时器回调函数 ===>window
- ajax请求回调函数(后面讲)
- 生命周期回调函数(后面讲)
// dom事件回调函数
document.getElementById('btn').onclick = function () {
alert(this.innerHTML)
}
// 定时器回调函数
setTimeout(function () {
alert('到点了'+this)}, 2000) // 2s后调用函数
3.1.4 IIFE (自调用函数)
-
全称:
Immediately-Invoked Function Expression
自调用函数 -
作用:
- 隐藏实现
- 不会污染外部(一般指全局)命名空间 (函数外与函数内无关)
(function () { //匿名函数自调用
var a = 3
console.log(a + 3)
})()
console.log(a) // a is not defined 可以说明不会污染外部命名空间
- 用它来编码js模块
//此处前方为何要一个`;`-->因为自调用函数外部有一个()包裹,可能与前方以()结尾的代码被一起认为是函数调用
//不加分号可能会被认为这样 console.log(a)(IIFE)
;(function () {//不会污染外部(全局)命名空间-->举例
let a = 1;
function test () { console.log(++a) } //声明一个局部函数test
window.$ = function () { return {test: test} }// 向外暴露一个全局函数
})()
test () //test is not defined
$().test() // 1. $是一个函数 2. $执行后返回的是一个对象
3.1.5 函数中的this
(1) this是什么?
- 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
- 所有函数内部都有一个变量this
- 它的值是
调用函数的当前对象
(2) 如何确定this的值?
- test(): window
- p.test(): p
- var obj = new test(): 新创建的对象,也就是obj
- p.call(obj): obj
3.1.6 原型与原型链
(1) 原型 [prototype]
- 函数的
prototype
属性
- 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
- 原型对象中有一个属性constructor, 它指向函数对象
- 给原型对象添加属性(
一般都是方法
)
- 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
// 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
console.log(Date.prototype, typeof Date.prototype)
function Fun () { }
console.log(Fun.prototype) // 默认指向一个Object空对象(没有我们的属性)
// 原型对象中有一个属性constructor, 它指向函数对象
console.log(Date.prototype.constructor===Date)
console.log(Fun.prototype.constructor===Fun)
//给原型对象添加属性(一般是方法) ===>实例对象可以访问
Fun.prototype.test = function () { console.log('test()') }
var fun = new Fun()
fun.test()
(2) 显式原型与隐式原型
-
每个函数function都有一个
prototype
,即显式
原型(属性) -
每个实例对象都有一个[
__ proto __
],可称为隐式
原型(属性) -
对象的隐式原型的值为其对应构造函数的显式原型的值
实例对象.__proto__ === 对应构造函数.prototype
-
内存结构
- 总结:
- 函数的[
prototype
]属性: 在定义函数时自动添加的, 默认值是一个空Object对象 - 对象的[
__ proto __
]属性: 创建对象时自动添加的,默认值为构造函数的prototype属性值
- 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
//定义构造函数
function Fn() {
// 内部默认执行语句: this.prototype = {}
}
// 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象
console.log(Fn.prototype)
// 2. 每个实例对象都有一个__proto__,可称为隐式原型
//创建实例对象
var fn = new Fn() // 内部默认执行语句: this.__proto__ = Fn.prototype
console.log(fn.__proto__)
// 3. 对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype===fn.__proto__) // true
//给原型添加方法
Fn.prototype.test = function () {
console.log('test()')
}
//通过实例调用原型的方法
fn.test()
(3) 原型链
① 原型链
- 访问一个对象的属性时,
- 先在自身属性中查找,找到返回
- 如果没有, 再沿着[
__ proto __
]这条链向上查找, 找到返回 - 如果最终没找到, 返回undefined
- 别名: 隐式原型链
- 作用: 查找对象的属性(方法)
②构造函数/原型/实例对象的关系(图解)
-
var o1 = new Object(); var o2 = {}; //这两个是创建对象的两种方法
-
function Foo(){ } 等价于 var Foo = new Function()
ps:所有函数的[__ proto __
]都是一样的
③ 属性问题
-
读取对象的属性值时: 会自动到原型链中查找
-
设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
-
方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
function Fn() { }
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a, fn1) //xxx Fn{}
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn1.a, fn2.a, fn2) //xxx yyy Fn{a: "yyy"}
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
var p1 = new Person('Tom', 12)
p1.setName('Bob')
console.log(p1) //Person {name: "Bob", age: 12}
var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2) //Person {name: "Cat", age: 12}
console.log(p1.__proto__===p2.__proto__) // true -->所以方法一般定义在原型中
(4) instanceof
- instanceof是如何判断的?
- 表达式: A instanceof B
- 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
- Function是通过new自己产生的实例
/*
案例1
*/
function Foo() { }
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true
/*
案例2
*/
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
function Foo() {}
console.log(Object instanceof Foo) // false
3.2 箭头函数
使用 =(){}
来代替 function()