GENIA

JS语言学习

2023-02-20

概述

完整的javascript实现由三部分构成

ECMAScript 标准

DOM 文档对象模型

BOM 浏览器对象模型

JS 的特点

1.解释型语言

2.类似于C和Java的语法结构

3.动态语言

4.基于原型的 面向对象

三种输出方式

1.控制浏览器弹出一个警告框:alert(“ “);

2.在页面中输出内容:document.write() ——> 向body中输出内容

3.向控制台输出内容:console.log()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!--这是html的注释方式-->
<script type="text/javascript">
// 控制浏览器弹出警告框
alert("HelloWorld!");
// 在页面中输出内容(向body中)
document.write("HelloWorld!");
// 向控制台输出内容
console.log("HelloWorld!");
</script>
</head>
<body>
</body>
</html>

通过调试可以发现, JS代码和其他语言一样,是从上往下顺次执行的。

JS的编写位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>

<!--
可以将JS代码编写到外部JS文件中,然后通过script标签引入
写到外部文件中可以在不同的页面中同时引用,也可以利用到浏览器的缓存机制
-->
<script type="text/javascript" src="script.js"></script>

<!--
一个cript标签一旦用于引用外部文件,就不能再编写代码了,即使编写了浏览器也会忽略
如果需要则可以再创建一个script标签编写内部代码
-->

<script type="text/javascript">

alert("这是内部script标签中的代码。")

</script>

</head>
<body>

<!-- 可以将JS代码编写到标签的onclick属性中每点击一次,JS代码会执行一次-->
<button onclick="alert('stupidstupid~');">不要点我!</button>

<!-- 可以将JS代码写在超链接的href属性中,当点击超链接时,会执行js代码 -->
<a href="javascript:alert('又上当了吧!');">点我点我!</a>

<!-- JS代码虽然可以写在标签的属性中,但是这样的写法导致了结构与行为的耦合,不方便网页的维护 -->
</body>
</html>

基本语法

注释

1
2
3
4
5
6
7
8
<script type="text/javascript">
/*
这是JS的多行注释,和CSS是一样的
快捷键 ctrl+shift+/
*/

// 这是JS的单行注释 快捷键 ctrl+/
</script>

代码块

程序是由一条一条语句构成的,语句是按照自上向下的顺序一条一条执行的。
在JS中可以使用{}来为语句进行分组:
同一个{}中的语句是一组语句,它们要么都执行,要么都不执行,即一个代码块。

和其他语言不同的是,JS中的代码块仅仅具有分组的作用,代码块内部的内容在外部是完全可见的。

1
2
3
4
5
{
alert("hello");
console.log("hello");
document.write("hello");
}

其它

1.JS中严格区分大小写,几乎和所有语言一样
2.JS中的每一条语句以分号结尾(类C)
– 如果不写分号,浏览器会自己添加,但是会影响一些系统资源
– 而且有的时候浏览器会加错分号,导致执行错误
3.JS中会自动忽略多个空格和换行,所以可以利用空格和换行对代码进行格式化操作(类C)

字面量和变量

1.字面量都是一些不可更改的值
比如:1,2,3,4,5等
字面量都可以直接使用,但是一般并不直接使用
2.多用变量,少用字面量

1
2
3
4
5
6
7
8
9
10
11
12
// 使用字面量
alert(12344556);
// 在JS中用 var 关键字来声明一个变量
var a;
// 为变量赋值
a = 12344556;
// 变量可以修改
a = 654321;
// 同时声明和赋值变量,变量名任意。
var b = 789;
var c = 0;
console.log(c);

标识符

在JS中可以由我们自主命名的都可以是标识符
例如:变量名、函数名、属性名等
命名规则(基本和其他语言一致):
1.可以含有数字,字母,_,$
2.标识符不能以数字开头
3.标识符不能是ES标准中的关键字或保留字
4.标识符一般采用驼峰命名法
– helloWorld
– 首字母小写,每个单词开头字母大写,其余字母小写
JS底层保存标识符时采用的是unicode编码,所以理论上所有utf-8中的内容都可以作为标识符(包括汉字)

数据类型

数据类型指的就是字面量的类型
JS中一共只有六种数据类型
– String 字符串
– Number 数值
– Boolean 布尔值
– Null 空值
– Undefined 未定义
– Object 对象
其中前五种属于基本数据类型,最后一种属于引用数据类型

String

用引号引起来(单双引号都行)
– 跟python一样,单双引号没有任何区别,同样的引号不能嵌套
– 在字符串中可以用\作为转义字符,对特殊符号进行转义

1
2
var str = "hello";
str = "stupid_stupid~";

Number

最大、最小与无穷

在JS中所有的数值都是Number类型,包括整数和浮点数。
JS可以表示的数字的最大值为:Number.MAX_VALUE (1.7976931348623157e+308)
如果Number表示的数字超过最大值,则返回Infinity,表示正无穷。对应的,-Infinity表示负无穷。
注:Infinity是一个字面量,类型为number

JS可以表示的最小正数为:Number.MIN_VALUE (5e-324)

1
2
console.log(Number.MAX_VALUE);  //1.7976931348623157e+308
console.log(Number.MIN_VALUE); //5e-324

typeof运算符

可以使用运算符 typeof 检查变量类型
语法:typeof 变量
检查字符串时,返回string
检查数值时,返回number

​ 以此类推

1
2
3
4
var b = 12345 + 54321;
var a = "12345";
console.log(typeof a); //string
console.log(typeof b); //number

NaN

NaN 是一个特殊的数字(number类型),表示 Not a number ,同样也是一个字面量。
当进行类似于字符串乘字符这类非法运算时返回 NaN 。

浮点数计算

和其他语言类似,整数运算可以基本保证精确。浮点数无法做到完全精确。
所以不要用JS进行高精度运算计算。

Boolean

Boolean只有两种结果:True 和 False

1
2
var bool = true;
console.log(typeof bool); //boolean

Null

Null只有一个值,即null,用于表示空的对象
它的类型是一个对象(object)

1
2
var a = null;
console.log(typeof a); //object

Undefined

Undefined 只有一个值,即undefined
当声明一个变量但不给它赋值时,其值即undefined
它的类型也是undefined

Object

String, Number, Boolean, Null, Undefined

以上五种属于基本数据类型,除了这五种类型,其它都是对象(Object)。

对象的特点

基本数据类型都是单一的值,值和值之间没有任何联系。
比如说:

1
2
3
4
//用JS表示一个人的信息(name gender age):
var name = "李华";
var gender = "男";
var age = 80;

如果使用基本数据结构,我们创建的变量则都是独立的,不能成为一个整体。
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。

对象的分类

1.内建对象
– 由ES标准中定义的对象,在任何的ES的实现中都可以使
– 如:Math String Number …
2.宿主对象
– 由JS的运行环境提供的对象,目前来说主要指由浏览器提供的对象
– 比如BOM DOM
3.自建对象
– 由开发人员自己创建的对象

创建对象

1
2
3
4
5
6
7
		/* 
使用new关键字调用的函数,是构造函数constructor
构造函数是专门用来创建对象的函数
*/
var obj = new Object();
console.log(obj); //Object{}
console.log(typeof obj); //object

属性名

向对象中添加属性
属性名:
– 对象的属性名不强制要求遵守标识符的规范,什么名字都可以使用
– 但我们尽可能按照标识符的规范命名

1
2
obj.name = "李华";
obj.var = "hello";

如果要使用特殊的属性名,不能采用.的方式来操作,而需要使用另一种方式:
语法:对象[“属性名”] = 属性值
读取时需要采用这种方式。
使用[]这种形式去操作属性,更加地灵活,在[]中可以直接传递一个变量,读取变量值对应的属性。

1
2
3
4
obj["123"] = 789;
obj["$$$$$$$$$$"] = "好多米";
var n = "$$$$$$$$$$";
console.log(obj[n]); //好多米

属性值

JS对象的属性值,可以是任意的数据类型,也可以是一个对象。

1
2
3
4
5
6
7
var obj1 = new Object();
obj1.test = undefined;
var obj2 = new Object();
obj2.name = "李华的爸爸";
obj1.test = obj2;
console.log(obj1); //{test: {name: '李华的爸爸'}}
console.log(obj1.test.name); //李华的爸爸

in 运算符

通过该运算符可以检查一个对象中是否含有指定的属性
如果有则返回true,没有则返回false
语法:”属性名” in 对象

console.log("test2" in obj); //false

增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//向对象中添加属性
/*
在对象中保存的值成为属性
向对象添加属性
语法:对象.属性名 = 属性值;
*/
obj.name = "李华";
obj.gender = "男";
obj.age = 80;
console.log(obj);

//删除对象的属性
/*
语法:delete 对象.属性名
*/
delete obj.name;

//读取对象中的属性
/*
语法:对象.属性名
如果读取对象中没有的属性,不会报错而是会返回undefined
*/
obj.name = "李华";
console.log(obj.name); //李华
console.log(obj.hello); //undefined

//修改对象的属性值
/*
语法:对象.属性名 = 新值
*/
obj.name = "华强";
console.log(obj.name); //华强

对象字面量

可以通过对象字面量来创建一个对象:

1
2
3
4
var obj = {};
console.log(typeof obj);
obj.name = "李华";
console.log(obj.name);

使用对象字面量,可以在创建对象时,直接指定对象中的属性
语法:{属性名:属性值,属性名:属性值……}
对象字面量的属性名可以加引号也可以不加,建议不加,如果要使用一些特殊的名字,则必须加引号。
属性名和属性值是一组一组的名值对结构,
名和值之间使用 : 连接,多个名值对之间使用 , 隔开
如果一个属性之后没有其它属性,就不必再写 ,
对象的嵌套也同样可以使用,如:

1
2
3
4
5
6
7
var obj3 = {name:"wakuwaku"}
var obj2 = {name:"李华",
gender:"男",
age:80,
test:obj3
};
console.log(obj2);

枚举对象中的属性

使用for…in 语句:
语法:
for(var 变量 in 对象){
}
for…in语句 对象中有几个属性,循环就会执行几次。
每次执行时,会将对象中一个属性的名字赋值给变量。

1
2
3
4
5
6
7
8
9
10
11
var obj = {
name:"teriri",
age:12,
gender:"Unknown",
address:"休伯利安"
};
//枚举对象中的属性
for(var n in obj){
console.log("属性名:"+n);
console.log("属性值:"+obj[n]);
}

基本数据类型和引用数据类型的区别

基本数据类型
String Number Boolean Null Undefined

引用数据类型
Object

JS 中的变量保存在栈内存中,基本数据类型的值直接在栈内存中存储,值与值之间是独立存在的,修改一个变量不会影响其它变量。

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

对象保存在堆内存中,每创建一个新的对象,就会在堆内存中开辟出一个新的空间。
而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当通过一个变量修改属性时,另一个也会受到影响。

1
2
3
4
5
6
7
var obj = new Object();
obj.name = "李华";
var obj2 = obj;
//修改obj的name属性
obj.name = "华强";
console.log(obj.name); //华强
console.log(obj2.name); //华强

当比较两个基本数据类型时,就是比较值。
而比较两个引用数据类型时,它比较的是对象的内存地址。即便两个对象一模一样,只要地址不同则不同。

1
2
3
4
5
var obj3 = new Object();
var obj4 = new Object();
obj3.name = "chichichi";
obj4.name = "chichichi";
console.log(obj3 == obj4); //false

强制类型转换

强制类型转换
– 将一个数据类型强制转换为其它数据类型
– 类型转换主要指将其他数据转换为
String Number Boolean

转换为String

方式一:
– 调用被转换数据类型的toString()方法
– 该方法不会影响到原变量,它会将转换的结果返回
– 注意:null 和 undefined 两个值无法使用 toString() 方法

1
2
3
4
5
6
7
8
var a = true;
console.log(typeof a); //输出boolean

// 方法一:即xxx.yyy()
var b = a.toString();
console.log(typeof b); //输出string
a = a.toString();
console.log(typeof a); //输出string

方式二:
– 调用String()函数,并将被转换的数据作为参数传递给函数。
– 使用String()函数做强制类型转换时,从底层上来讲
对于Number和Boolean实际上就是调用的 toString() 方法
对 null 和 undefined 会执行直接转换 –> “null” 和 “undefined”

1
2
3
4
5
6
7
8
9
10
// 方法二:使用String()
a = 666;
a = String(a);
console.log(typeof a); //输出String
a = null;
a = String(a);
console.log(typeof a); //输出String
a = undefined;
a = String(a);
console.log(typeof a); //输出String

转换为Number

方法一:
使用Number()函数将数据转换为Number类型
– 字符串 –> 数值
1.如果是纯数字的字符串,则将其直接转换成数字
2.如果字符串中有非数字内容,则转换为 NaN
3.如果字符串是一个空串(或全是空格),则转换为0
– 布尔值 –> true 转化为数字 1,false 转换为数字 0
– Null –> 数字 0
– undefined –> NaN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var a = "123";
a = Number(a);
console.log(typeof a); //输出number
console.log(a);

a = "abc";
a = Number(a);
console.log(typeof a); //输出NaN
console.log(a);

a = "";
a = Number(a);
console.log(typeof a); //输出0
console.log(a);

a = true;
a = Number(a);
console.log(typeof a); //输出1
console.log(a);

a = false;
a = Number(a);
console.log(typeof a); //输出0
console.log(a);

a = null;
a = Number(a);
console.log(typeof a); //输出0
console.log(a);

a = undefined;
a = Number(a);
console.log(typeof a); //输出NaN
console.log(a);

方法二:
– 此方法专门针对字符串使用
– parseInt() 把一个字符串转换为一个整数
– parseFloat() 把一个字符串转换为一个浮点数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 
parseInt()可以将一个字符串中的有效的整数内容提取出来,然后转换为Number
parseFloat()作用和 parseInt() 类似。可以获得有效的小数
*/
a = "123stupid456";
a = parseInt(a);
console.log(typeof a); //输出number
console.log(a); //输出123

a = "stupid123456";
a = parseInt(a);
console.log(typeof a); //输出number
console.log(a); //输出NaN

/*
如果对非String使用parseInt()和parseFloat(),它会先将其转换为String然后再操作
也可以利用parseInt()对小数进行取整
*/

转换为Boolean

将其它的数据类型转换为Boolean
– 使用Boolean()函数
– 数字 –> 除了0 和 NaN,其余都是true
– 字符串 –> 除了空串,其余的都是true
– null 和 undefined–> 都是false
– 对象 –> true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//测试数字
var a = 123;
var b = -123;
var c = 0;
var d = Infinity;
var e = NaN;
//调用Boolean()函数
a = Boolean(a);
b = Boolean(b);
c = Boolean(c);
d = Boolean(d);
e = Boolean(e);
console.log(a); //true
console.log(b); //true
console.log(c); //false
console.log(d); //true
console.log(e); //false

其它进制数表示和编码方式

进制表示

0x 开头表示16进制,表示数字的字母不区分大小写
0 开头的是8进制
0b 开头表示2进制(不是所有的浏览器都支持)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a = 0xAB;
console.log(a); //171
b = 077;
console.log(b); //63
c = 0b10;
console.log(c); //2

//歧义:"070"这种字符串,有些浏览器会当成8进制解析,有的会当成10进制解析
d = "070";
d = parseInt(d);
console.log(typeof d);
console.log(d); //edge解析为十进制,输出70

//为了解决这种歧义,可以在parseInt()中传递第二个参数,来指定数字的进制
e = "070";
e = parseInt(e,8);
console.log(typeof e);
console.log(e); //输出56

unicode编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
/*
通过转义符 \u 输出unicode码表中的任意字符
格式:\u + 四位编码
*/
console.log("\u0054"); //T
console.log("\u2655"); //♕
</script>
</head>
<body>
<!--
在网页中使用unicode编码
&#编码; 但是这里需要的是十进制
-->
<!-- 设置字符为200像素,分别输出国象中的白后和白王 -->
<h1 style="font-size: 200px;">&#9813;</h1>
<h1 style="font-size: 200px;">&#9812;</h1>
</body>
</html>

运算符

运算符也叫操作符
运算符可以对一个或多个值进行运算,并获取运算结果。
比如:typeof就是运算符,可以来获得一个值的类型。它会将该值的类型以字符串的形式返回。

算数运算符

当对非Number类型的值进行运算时,会先将这些值转换为Number然后再运算。
任何值和NaN做运算,最后的结果都是NaN。
+ 1.对两个值进行加法运算,并将结果返回
2.可以对两个字符串进行连接

​ 任何的值和字符串做加法运算,都会先转换字符串,然后再进行拼接。
​ – 可以利用这一特点,来将一个任意的数据类型转换为String。即隐式类型转换,由浏览器自动完成,从底层来看依旧是调用了String()。

1
2
3
4
5
6
//隐式string类型转换
result = 123 + "456";
console.log(result); //输出123456
var c = 123;
c = c + "";
console.log(typeof c); //输出string

​ 3.也可以作为格式化输出的一种方式

1
2
3
4
5
6
7
8
//格式化输出
console.log("c = "+c); //输出c = 123

//容易产生歧义的情况
result = 1 + 2 + "3";
console.log(result); //输出33
result = "1" + 2 + 3;
console.log(result); //输出123

- 1.对两个值进行减法运算,并将结果返回
​ 2.没有隐式转换作用

* 对两个值进行乘法运算
/ 除法运算

任何做 - * / 运算时都会自动转换为Number,属于第二种隐式类型转换
**可以通过对一个值 -0 *1 /1 来将其转换为Number。**原理和Number()一样,但是使用更简便。

% 取模运算

一元运算符

正负号

一元运算符只需要一个操作数
+ 正号
– 正号对数字不会产生任何影响
- 负号
– 负号对数字进行符号取反
对于非Number类型的值,会先转换为number再进行运算

1
2
3
var a = true;
a = -a;
console.log(a); //-1

​ 可以对一个其他数据类型使用 + ,来将其转换为Number

1
2
3
4
5
//第三种隐式类型转换
var result = 1 + "2" + 3;
console.log(result); //输出123
var result = 1 + +'2' +3;
console.log(result); //输出6

自增和自减

自增
– 使变量在自身的基础上增加1
– 对一个变量自增后,原变量的值会立即自增1
– 自增分为两种:后++(a++) 和 前++(++a),都会立即使原变量的值自增1
但是二者本身的值不同。后++的值等于自增以前的值(原),前++的值等于自增之后的值(新)
自减
– 使变量在自身的基础上减1
– 一切属性类似自增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var a = 1;
//使a自增。
a++;
console.log(a); //2
++a;
console.log(a); //3

var c = 10;
c++;
console.log(c++); //输出11

var d = 20;
console.log(++d); //21
console.log(++d); //22

var result = d++ + ++d +d; //=22+24+24
console.log(result); //70

d = d++; //相当于给d赋值两次
console.log(d); //24

逻辑运算符

JS提供三种逻辑运算符
! 非
– 用来对一个值进行非运算
– 对一个布尔值进行取反操作
– 两次取反结果不变
– 对非布尔值进行非运算,会先将数据转换为布尔类型再进行取反运算
所以可以利用该特点,来将一个其它数据类型转换为布尔值
隐式类型转换:
可以为一个任意数据类型取反两次,将其转换为布尔值
原理同Boolean()

1
2
3
4
5
6
7
8
9
var a = true;
a = !a;
console.log(a); //false
a = !!a;
console.log(a); //false
var b = 10;
b = !b;
console.log(typeof b); //boolean
console.log(b); //false

​ && 与
​ – 对符号两侧的值进行与运算并返回结果
​ – 运算规则:
​ – 两个值中只要有一个值为false就返回false
​ 两个值均为true才返回true
​ – JS中的 与 是短路的 与,如果第一个值为false,就不再判断第二个值

1
2
3
4
var result = true && true;
console.log(result); //true
var result = true && false;
console.log(result); //false

​ || 或
​ – 对符号两侧的值进行与运算并返回结果
​ – 运算规则:
​ – 两个值中只要有一个值为true即为true
​ 两个值均为false才返回false
​ – JS中的 或 是短路的 或,同 与

​ && || 对非布尔值情况:
​ – 对于非布尔值进行与或运算时,会先将其转换为布尔值再运算

​ – 与运算:
​ – 如果第一个值为true,直接返回第二个原值
​ – 如果第一个值为false,直接返回第一个原值

1
2
3
4
5
6
7
8
9
10
11
12
//true && true
//与运算:如果两个值都为true,则会返回后面的
var result = 5 && 6;
console.log(result); //6

//与运算:如果两个值中有false,返回靠前的false
//false && true
result = 0 && 2;
console.log(result); //0
//false && false
result = NaN && 0;
console.log(result); //NaN

​ – 或运算:
​ – 如果第一个值为true,则直接返回第一个原值
​ – 如果第一个值为false,则直接返回第二个原值

1
2
3
4
5
6
7
//true || true(false)
//或运算:如果第一个值为true,直接返回第一个值
result = 2 || 1;
console.log(result); //2
//或运算:如果第一个值为false,直接返回第二个值
result = NaN || 1;
console.log(result); //1

相等运算符

1.== 相等运算符用来比较两个值是否相等,相等返回true,不等返回false
使用==来做相等运算
– 当使用==比较两个值时,如果值的类型不同,会进行自动类型转换,
将其转换为相同类型后再作比较

2.!= 不相等用来判断两个值是否不相等,不相等返回true,相等返回false
使用!=来做不相等运算
– 性质与==类似

1
2
3
4
5
6
7
8
9
10
11
12
console.log("1" == 1);  //true
console.log(true == "1"); //true
console.log(true == "hello"); //false
console.log(null == 0); //false
/*
undefined 衍生自 null
所以这两个值做相等判断时会返回true
NaN不和任何数相等,包括它本身
因此不能通过 == 来判断一个值是否为NaN
可以通过isNaN()函数来判断一个值是否为NaN
如果该值是 NaN 则返回 true,否则返回 false
*/

3.=== 全等,用来判断两个值是否全等,和相等类似,不同点在于它不会进行自动类型转换

4.!== 不全等,用来判断两个值是否不全等,特殊性同上

1
2
3
4
5
var a = NaN;
console.log(isNaN(a)); //true

console.log("1" === 1); //false
console.log(null === undefined); //false

关系运算符

通过关系运算符可以比较两个值之间的大小关系,关系成立则返回true,否则返回false。
>
>=
<
<=

对于非数值情况
– 对于非数值进行比较时,会先转换为Number再比较。
– 如果符号两侧的值都是字符串时,不会将他们转换成数字再比较,而是直接比较它们的unicode编码。
一位一位进行比较,两位一样则比较下一位,可以借用此特性对英文进行排序。对中文而言不是很有 意义。

1
2
3
4
5
6
7
8
9
10
console.log(1 > true);//false
console.log(1 >= true);//true
console.log(1 > "0");//true
console.log(10 > null);//true
console.log(true > false);//true
//任何值和NaN做任何比较都是false
console.log(10 <= "hello");//false
//如果表示两个字符串形式的数字,往往会得到非预期结果
//注意在比较前进行转型
console.log("11" < "5");//true

条件运算符

条件运算符也叫三元运算符
语法:条件表达式 ? 语句1 : 语句2 ;
– 执行的流程:
条件运算符在执行时,首先对条件表达式进行求值,
如果值为true,则执行语句1,并返回执行结果
如果值为false,则执行语句2,并返回执行结果
如果条件表达式的结果是一个非布尔值,则会将它转换为布尔值再做判断

1
2
3
4
5
6
7
8
9
10
11
12
13
true ? alert("语句1") : alert("语句2");  //语句1
false ? alert("语句1") : alert("语句2"); //语句2

var a = 30;
var b = 20;
//取a和b之间的最大值
var max = a > b ? a : b;
console.log("max = "+max); //a
var c = 50;
//取a,b,c中的最大值
//这么写会被打,所以不推荐
var max = a > b ? (a > c ? a : c) : (b > c ? b : c); //50
console.log("max = "+max);

其它

1
2
3
4
5
6
/* 
, 运算符
使用 , 可以分割多个语句,一般可以在声明多个变量时使用
*/
//同时声明多个变量
var a , b = 2 , c = 3;

JS运算符的优先级:https://github.com/xhlwill/blog/issues/16
不用背,查表即可,越靠上优先级越高,先进行计算。如果优先级一样,则从左向右运算。如果实在不清楚,就多加加括号。

流程控制语句

几乎和C语言相同,不多作记录。

JS中的程序是从上到下一行一行控制执行的,通过流程控制语句可以控制程序执行流程,使程序可以根据一定条件来选择执行。

条件判断语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//if语句
if(条件表达式) 语句;
//if……else
if(条件表达式){
语句
}else{
语句
}
//if……else if……else
if(条件表达式){
语句
}else if{
语句
}else{
语句
}

条件分支语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
switch(条件表达式){
case 表达式:
语句;
break;
case 表达式:
语句;
break;
case 表达式:
语句;
break;
default:
语句;
break;
}

循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//while循环
while(条件表达式){
语句……
}

//do……while循环
do{
语句……
}while(条件表达式);

//for循环
for(初始化表达式;条件表达式;更新表达式){
语句……
}
//初始化表达式可以沿用C99规则。

break和continue

break 关键字 可以用来退出switch或循环语句。
不能在if语句中使用break和continue。
break会立即终止离他最近的那个循环语句。
特别的是:可以为循环语句创建一个label,来标识当前的循环
label:循环语句
使用break语句时,可以在break后跟着一个label
这样break将会结束指定的循环,而不是最近的

1
2
3
4
5
6
7
8
outer:
for(var i = 0; i<5; i++){
console.log("外层: "+i);
for(var j=0; j<5 ; j++){
break outer; //直接终止最外层循环
console.log("内层: "+j);
}
}

continue 关键字 可以用来跳过当此循环
它同样默认只对离它最近的循环起作用。

函数

函数也是一个对象。
函数中可以给封装一些功能(代码),在需要时可以执行这些功能(代码)。也可以保存一些代码,在需要的时候调用。

使用typeof检查一个函数对象时,会返回function。

1
2
3
4
5
6
7
8
9
10
//这种方式在开发过程中很少使用
//创建一个函数对象
//可以将要封装的代码以字符串的形式传递给构造函数
var fun = new Function("console.log("第一个JS函数");");
console.log(typeof fun); //function

//封装到函数中的代码不会立刻执行,而是在调用的时候执行
//调用语法:函数对象()
//当调用函数时,函数中封装的代码会按照顺序执行
fun();

函数的创建

使用函数声明来创建一个函数

常规方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
         /*
语法:
function 函数名([形参1,形参2……形参N]){
语句……
}
*/
function fun2(){
console.log("这是第二个函数");
alert("欸嘿");
document.write("~stupidstupid~");
}
console.log(fun2);
fun2();

使用函数表达式来创建一个函数

1
2
3
4
5
6
7
8
9
10
/* 
var 函数名 = function([形参1,形参2……形参N]){
语句……
}
*/
var fun3 = function(){
console.log("这是匿名函数中封装的代码");
}

fun3();

函数的参数

可以在函数的 () 中指定一个或多个形参(形式参数)
– 多个形参之间用 , 隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值
在调用函数时,可以在()中指定实参(实际参数)
– 实参将会赋值给函数中对应的形参

需要注意的是:

1.调用函数时解析器不会检查实参的类型,要注意是否有可能接收到非法的参数,如果有可能则需要对参数进行类型的检查。

2.调用函数时,解析器也不会检查实参的数量,多余的实参不会产生影响。如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined。

函数的返回值

可以用 return 来设置函数的返回值。
return 后的值将会作为函数的执行结果返回,可以通过定义一个变量来接受结果。
在函数中,return 后的语句都不会执行。
如果 return 语句之后不跟任何值,就相当于返回undefined,不写return也是如此。
return 后可以跟任意类型的值。
返回值可以是任意的数据类型,包括对象和函数。

1
2
3
4
5
6
7
8
function fun1(){
function fun2(){
alert("terri_terri");
}
return fun2;
}
a = fun1();
a()(); //terri_terri

立即执行函数

立即执行函数在定义完后立即被调用,往往只会执行一次。

1
2
3
4
5
6
7
8
9
//无参数
(function(){
alert("立即执行函数");
})();
//有参数
(function(a, b){
alert("a = "+a);
alert("b = "+b);
})(123,456);

对象与方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = new Object();
obj.name = "teriri";
obj.age = 12;
//对象的属性值可以是任何的数据类型,也可以是个函数
obj.sayName = function(){
console.log(obj.name);
};

console.log(obj.sayName);
/*
ƒ (){
console.log(obj.name);
}
*/
obj.sayName(); //teriri

函数也可以成为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数为这个对象的方法。

调用这个函数即调用对象的方法(method),和调用单独定义的函数没有本质区别。

this

解析器在调用函数时,每次都会向函数内部传递一个隐含的参数,这个隐含的参数即this,this指向的是一个对象,即函数执行的上下文对象。
根据函数调用方式的不同,this会指向不同的对象:
1.以函数的形式调用时,this指向window
2.以方法的形式调用时,this指向调用方法的对象
3.当以构造函数的形式调用时,this就是新创建的那个对象
4.使用call和apply调用时,this是指定的那个对象

1
2
3
4
5
6
7
8
9
10
function fun(a,b){
console.log(this);
}
fun(123,456); //[Object Window]

var obj ={
name:"teriri",
inside:fun
};
obj.inside(); //[Object Object]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//案例
function fun0(){
console.log(this.name);
}

var obj1 = {
name:"teriri_1",
sayName:fun0
}
var obj2 = {
name:"teriri_2",
sayName:fun0
}

obj1.sayName(); //teriri_1
obj2.sayName(); //teriri_2

call和apply

call()和apply()
– 是函数对象的方法,需要通过函数对象来调用
– 当对函数调用 call() 和 apply() 都会调用函数执行
– call() 和 apply() 可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fun1(){
console.log(this);
}
var obj1 = {
name: "OBJ1",
sayName:function(){
console.log(this.name);
}
};
var obj2 = {
name: "OBJ2"
}
fun1.call(obj2); //Object
fun1.call(obj1); //Object
obj1.sayName.apply(obj2); //OBJ2

– call() 方法可以将实参在对象之后依次传递。
– apply() 方法需要将实参封装到数组中统一传递。

1
2
3
4
5
6
7
function fun2(a,b){
console.log("a = "+a);
console.log("b = "+b);
}
fun2.call(obj1,2,3);
fun2.apply(obj1,[2,3]); //a = 2
//b = 3

arguments

在调用函数时,浏览器每次都会传递进两个隐含的参数:
1.函数的上下文对象this
2.封装实参的对象 arguments
– arguments是一个类数组对象,它可以通过索引来操作数据,也可以获取长度
– 在调用函数时,我们所传递的实参都会在arguments中保存
– arguments.length可以用来获取实参的长度
– 我们即使不定义形参,也可以通过arguments来使用实参
– 它里面有一个callee属性,对应当前正在执行的函数的对象

1
2
3
4
5
6
7
8
function fun(){
console.log(arguments instanceof Array); //false
console.log(Array.isArray(arguments)); //false
console.log(arguments.length); //输出参数的个数
console.log(arguments[0]); //实参可以取出
console.log(arguments.callee);
}
fun();

作用域

作用域指一个变量作用的范围
在JS仅有两种作用域:
1.全局作用域
– 直接编写在script标签中的代码,都在全局作用域中
– 全局作用域在页面打开时创建,在页面关闭时销毁
– 全局作用域有一个全局对象window(由浏览器创建),我们可以直接使用
– 在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
– 全局作用域中的变量都是全局变量,在页面的任意部分都可以访问得到
2.函数作用域
– 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
– 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
– 函数作用域中可以访问到全局作用域的变量,但在全局作用域中无法访问到函数作用域中的变量
– 在函数作用域中操作一个变量时,它会先在自身作用域中寻找,如果有就直接用
如果没有就在上一级作用域中寻找,直到到达全局作用域,如果依旧找不到,会直接报错ReferenceError

声明提前

变量的声明提前
– 使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会被赋值)
但是如果声明变量时不使用var关键字,则变量不会被声明提前

1
2
3
4
5
6
console.log(a);
var a = 123;
//相当于:
var a;
console.log(a);
a = 123;

函数的声明提前
– 使用函数声明形式创建的函数 function 函数(){}
它会在所有代码执行之前就被创建,所以我们可以在函数声明前调用函数
– 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 
函数作用域也有声明提前的特性:
使用var关键字声明的变量,会在函数中所有代码执行之前被声明
函数声明也会在函数中所有代码执行之前执行
*/
var a = 10;
var b = 20;
console.log(window.a); //10
function fun(){
console.log("fun函数");
}
window.fun(); //fun函数
/*
在函数中,不使用var声明的变量都会成为全局变量
*/
function fun5(){
d = 100; //相当于 window.d = 100;
}
console.log(d); //100
/*
定义形参就相当于在函数作用域中声明了变量。
*/

使用工厂方法创建对象

通过该方法可以大批量创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function creatPerson(name, age){
//创建一个新的对象
var obj = new Object();
//向对象中添加属性
obj.name = name;
obj.age = age;
obj.sayName = function(){
alert(this.name);
};
//将新的对象返回
return obj;
}

var obj2 = creatPerson("teriri",12);
console.log(obj2);

使用工厂方法创建的对象,使用的构造函数都是Object,所以创建的对象都是Object这个类型。
导致我们无法区分出多种不同类型的对象。

构造函数

创建一个构造函数,专门用来创建person对象
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写(还有加不加new的区别)

构造函数和普通函数的区别就是调用方式的不同
普通函数是直接调用,而构造函数需要使用new关键词来调用

1
2
3
4
5
6
function Person(name, age){
this.name = name;
this.age = age;
}
var per = new Person("teriri", 12);
console.log(per);

构造函数的执行流程:
1.立刻创建一个新的对象
2.将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
3.逐行执行函数中的代码
4.将新建的对象作为返回值返回

使用一个构造函数创建的对象,我们称为一类对象,也将一个构造函数成为一个类,我们将通过一个构造函数创建的对象,成为该类的实例

使用 instanceof 可以检查一个对象是否是一个类的实例。
语法:
对象 instanceof 构造函数
返回值:是则返回true,否则返回false

1
2
3
4
5
console.log(per instanceof Person);  //true
/*
所有对象都是Object的后代,所以任何对象和Object做instanceof检查时都会返回true
*/
console.log(per instanceof Object); //true

局限性

创建一个Person构造函数:

1
2
3
4
5
6
7
8
9
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
//向对象中添加一个方法
this.sayName = function(){
alert("Helloworld!I'm " + this.name);
};
}

在Person构造函数中,为每一个对象都添加了一个sayName方法
目前我们的方法是在构造函数内部创建的,即构造函数每执行一次都会创建一个新的Sayname方法
即所有实例的sayName都是唯一的。这样就导致了构造函数每执行一次就会创建一个新的方法。

1
2
3
4
5
6
//创建一个Person实例
var per = new Person("teriri", 12, "unknown");
var per2 = new Person("lihua", 18, "man");

//比较两个方法
console.log(per.sayName == per2.sayName); //false

​ 但是这是完全没必要的,我们可以使所有对象共享同一个方法。

一个方法是:将sayName方法在全局作用域中定义。

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
alert("Helloworld!I'm " + this.name);
};
this.sayName2 = fun;
}
function fun(){
alert("hello! I'm " + this.name);
}

将函数定义在全局作用域中,污染了全局作用域的命名空间。定义在全局作用域中也很不安全,因此有很大的局限性。

原型对象

我们所创建的每一个函数,解析器都会向函数中添加一个属性 prototype ,这个属性对应着一个对象,这个对象就是原型对象。
如果函数作为普通函数调用,prototype没有任何作用。
当函数以构造函数的形式调用时,它所创建的对象中,都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过 __proto__来访问该属性。

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中所有的内容,统一设置到原型对象中。

当我们访问对象的一个属性或方法时,会优先在对象自身中寻找,如果有则直接用,如果没有则会去原型对象中寻找,如果找到则直接使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Person(){

}
function MyClass(){

}
MyClass.prototype.a = 123;

//向MyClass的原型中添加一个方法
MyClass.prototype.sayHello = function(){
alert("hello!");
};

var mc = new MyClass();
var mc2 = new MyClass();

//向mc中添加a属性
mc.a = "这是mc对象自身中的属性";
console.log(mc.a); //这是mc对象自身中的属性
console.log(mc2.a); //123

console.log(mc.sayHello);
/*
ƒ (){
alert("hello!");
}
*/

创建构造函数时,可以将对象共有的属性统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法。

1
2
3
console.log(Person.prototype == MyClass.prototype);  //false
console.log(mc.__proto__ == MyClass.prototype); //true
console.log(mc2.__proto__ == MyClass.prototype); //true

in,hasOwnProperty方法和原型链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 
创建一个构造函数
*/
function MyClass(){

}
//向MyClass原型中添加一个name属性
MyClass.prototype.name = "这是原型中的名字";

var mc = new MyClass();

console.log(mc.name); //这是原型中的名字

//使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,则也会返回true
console.log("name" in mc); //true

//可以使用对象的hasOwnProperty()来检查对象自身是否含有属性
//使用该方法时,只有当对象自身中含有属性时,才会返回true
console.log(mc.hasOwnProperty("name")); //false

原型对象也是对象,所以它也有原型,当我们使用一个对象的属性或方法时,会现在自身中寻找,自身如果有则直接使用。
如果没有就去原型里找,还是找不到就去原型的原型里找,以此类推,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object中依然找不到,则返回unfined。

1
2
3
4
5
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));  //false
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true

//究竟可以嵌套几层呢?
console.log(mc.__proto__.__proto__.__proto__); //null

toString

当我们直接在页面中打印一个对象时,实际上是输出对象的 toString 方法的返回值。
如果我们希望在输出对象时不输出[object Object],可以为对象添加一个 toString 方法。
例如 Person[name= ...,age=...,gender=...]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
//修改Person原型的toString
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
}
//创建一个Person实例
var per = new Person("teriri", 12, "unknown");
var per2 = new Person("lihua", 18, "man");
per.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
};
console.log(per2);

var result = per.toString();
console.log(per);
console.log("result = " + result);

测试 toString 的位置:

1
2
3
4
console.log(per.hasOwnProperty("toString"));  //flase
console.log(per.__proto__.hasOwnProperty("toString")); //false
console.log(per.__proto__.__proto__.hasOwnProperty("toString")); //true
//存在于原型的原型中。

垃圾回收(GC)

当一个对象没有任何变量或属性对它进行引用,此时我们将无法操作该对象。(一种内存泄漏)
这种对象过多会占用大量的内存空间,导致程序运行变慢。
JS拥有自动的垃圾回收机制,会自动将垃圾对象从内存中销毁。因此我们不需要也不能进行垃圾回收的操作。
我们只需要将不再使用的对象设置为null即可。

1
2
3
var obj = new Object();
//对对象进行操作,类似于C类语言中解决迷途指针的方法
obj = null;

数组(Array)

– 数组是一个内建对象
– 它和普通的对象功能类似,用于存储一些值
– 不同的是,普通对象使用字符串作为属性名,而数组使用数字作为索引来操作元素。
– 索引从0开始
– 存储性能比普通对象好

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//创建数组对象
var arr = new Array();
console.log(arr); //Array(0)
console.log(typeof arr); //object

//向数组中添加元素
arr[0] = 10;
arr[1] = 33;
arr[2] = 22;
console.log(arr); //(3) [10, 33, 22]

//读取数组中的元素
//如果读取不存在的索引,不会报错,返回undefined
console.log(arr[0]); //10

//使用length属性获取数组的长度,但是只适用于连续的数组
console.log(arr.length); //3

//对于非连续的数组,使用length属性会获取到数组的 最大索引+1

//修改length
//增大数组长度:多出部分为空
arr.length = 10;
console.log(arr.length); //10
console.log(arr); //(10) [10, 33, 22, 空 ×7]
//减小数组长度:多出的元素被删除
arr.length = 2;
console.log(arr.length); //2
console.log(arr); //(2) [10, 33]

//一种合理的添加元素的方法:向数组的最后添加元素。
arr[arr.length] = 50;
arr[arr.length] = 60;
arr[arr.length] = 70;
console.log(arr); //(5) [10, 33, 50, 60, 70]

数组字面量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//使用字面量创建数组
//使用字面量创建数组时,可以在创建时就指定数组中的元素
var arr = [1,2,3,4,5,6,7,8];
console.log(typeof arr); //object
console.log(arr); //(8) [1, 2, 3, 4, 5, 6, 7, 8]

//使用构造函数创建数组
var arr2 = new Array(8,7,6,5,4,3,2,1);
console.log(arr2); //(8) [8, 7, 6, 5, 4, 3, 2, 1]

//不一样的是:
arr = [10];
arr2 = new Array(10);
console.log(arr); //[10]
console.log(arr2); //(10) [空 ×10]

//数组中的元素可以是任意的数据类型
arr = ["hello",1,true,null,undefined];
console.log(arr); //(5) ['hello', 1, true, null, undefined]

//也可以是对象
var obj = {name:"teriri"};
arr[arr.length] = obj;
console.log(arr[5]); //{name: 'teriri'}

//也可以是函数
arr = [function(){alert(1)},function(){alert(2)}];
console.log(arr); //(2) [ƒ, ƒ]
arr[0](); //1
arr[1](); //2

//也可以在数组里放数组
arr =[[1,2,3],[3,4,5],[6,7,8]];
console.log(arr[0]); //(3) [1, 2, 3]

遍历数组

使用for循环

1
2
3
4
var arr = [1,2,3,4,5,6,7,8];
for(var i = 0; i< arr.length; i++){
console.log(arr[i]);
}

使用forEach方法

该方法只支持IE8以上浏览器。

forEach方法需要一个函数作为参数,这种函数由我们创建但不由我们调用,称为回调函数
数组中有几个元素,函数就执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式传递。我们可以定义形参来读取这些内容。
浏览器会在回调函数中传递三个参数
第一个参数,是当前正在遍历的元素
第二个参数,是当前正在遍历元素的索引
第三个参数,是正在遍历的数组

1
2
3
4
5
6
7
8
9
arr.forEach(function(){
console.log("hello!"); //9forEach.html:15 hello!
});

arr.forEach(function(a,b,c){
console.log("a = "+a);
console.log("b = "+b);
console.log("c = "+c);
})

输出:

数组常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//创建一个数组
var arr = [1,2,3];

//push()向数组中添加一个或多个元素,并返回新的长度
//元素自动添加到数组末尾
result = arr.push(4,5,6);
console.log(arr); //(6) [1, 2, 3, 4, 5, 6]
console.log(result); //6

//pop()删除并返回数组的最后一个元素,并返回被删除的元素
result2 = arr.pop();
console.log(arr); //(5) [1, 2, 3, 4, 5]
console.log(result2); //6

//unshift()向数组的开头添加一个或多个元素,并返回新的数组长度
//向前面添加元素后,后面元素的索引会自动进行调整
result3 = arr.unshift(-1,0);
console.log(arr); //(7) [-1, 0, 1, 2, 3, 4, 5]
console.log(result3); //7

//shift()可以删除数组的第一个元素,并将删除的元素作为返回值返回
result4 = arr.shift();
console.log(arr); //(6) [0, 1, 2, 3, 4, 5]
console.log(result4); //-1

var arr = [1,3,5,7,9];
/* slice可以从数组中提取指定元素
该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
参数:1.截取开始位置的索引,包含开始索引
2.截取结束位置的索引,不包含结束索引
-- 第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
-- 索引可以传递一个负值,则从后往前计算
*/
var result = arr.slice(1,4);
console.log(result); //(3) [3, 5, 7]

/*
splice可以用于删除数组中的指定元素
-- 会影响到原数组,将被删除的元素作为返回值返回。
参数:1.开始位置的索引
2.删除的数量
3-N.传递新的元素,这些元素将自动插入到开始位置索引前
splice是一个多功能的方法,相当于insert和del功能的集合。
*/
result2 = arr.splice(1,4,"teriri");
console.log(arr); //(2) [1, 'teriri']
console.log(result2); //(4) [3, 5, 7, 9]

/*
concat()可以连接两个或多个数组,并将新的数组返回
-- 该方法不会对原数组产生影响
*/
var arr1 = [1,2,3];
var arr2 = [4,5,6];

var result = arr1.concat(arr2,"teriri");
console.log(result); //(7) [1, 2, 3, 4, 5, 6, 'teriri']

/*
join()
-- 可以将数组转换为字符串
-- 该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回
-- 可以指定一个字符串作为参数,成为数组元素之间的连接符如果不指定连接符,则默认使用 , 作为连接符
*/
var result2 = arr1.join();
console.log(result2); //1,2,3
console.log(typeof result2); //string
var result3 = arr1.join("-");
console.log(result3); //1-2-3

/*
reverse()
-- 该方法用于反转数组
-- 会直接修改原数组
*/
arr1.reverse()
console.log(arr1); //(3) [3, 2, 1]

/*
sort()
-- 可以用来对数组中的元素进行排序
-- 也会影响原数组
-- 默认按unicode编码进行排序,因此即时对于数字数组,也依照unicode编码
*/
var arr3 = ["c","b","a"];
arr3.sort();
console.log(arr3); //(3) ['a', 'b', 'c']
/*
排序的规则可以由我们自己指定。
可以在sort中添加回调函数,来指定排序规则。
回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数。
使用哪个元素调用不确定,但肯定的是数组中a一定在b的前面
-- 浏览器会根据回调函数的返回值来决定元素的顺序,
如果返回一个大于0的值,则元素会交换位置
如果返回一个小于0的值,则元素位置不变
如果返回0,则认为两个元素相等,也不交换
-- 如果需要升序排列,则返回 a-b ,如果需要降序排列,则返回 b-a 。
*/
var arr4 = [5,11,3,1,7,6];
arr4.sort(function(a,b){
if(a>b){
return 1;
}else if(a<b){
return -1;
}else{
return 0;
}
});
console.log(arr4); //(6) [1, 3, 5, 6, 7, 11]