JavaScript
从交互的角度,描述行为(提升用户体验)。
特点
- 简单易用:可以使用任何文本编辑工具编写,只需要浏览器就可以执行程序。
- 解释型语言:事先不需要被编译为机器码再执行,逐行执行、无需进行严格的变量声明。
- 基于对象:内置大量现成对象,编写少量程序可以完成目标。
注释
// 我是单行注释
/*
我是多行注释1
我是多行注释2
*/
变量
变量可以用来保存字面量,而且变量的值可以任意改变。
变量定义
var a = 100;//赋值式声明
var b;//单纯的声明
var _abc;
var $abc;//必须是$或_或字母作为开头,变量名称里还可以包含。
//var 1abc;//不允许数字开头
//var .abc;//不能以除_$之外的符号开头
- 定义变量:
var
是一个关键字,用来定义变量。关键字是有特殊功能的小词语。关键字后面一定要有空格隔开。 - 变量的赋值:等号表示赋值,将等号右边的值,赋给左边的变量。
- 变量名:必须是$_字母作为开头,变量名称里还可以包含。
命名规则
- 建议用驼峰命名规则:getElementById/matherAndFather/aaaOrBbbAndCcc
- 变量命名必须以字母或是下标符号”_”或者”$”为开头。
- 变量名长度不能超过255个字符。
- 变量名中不允许使用空格,首个字不能为数字。
- 不用使用脚本语言中保留的关键字及保留字作为变量名。
- 变量名区分大小写(javascript是区分大小写的语言)。
- 汉语可以作为变量名。但是不建议使用。
标识符的命名规则和变量的命令规则是一样的。
命名规范
变量名有命名规范:只能由英语字母、数字、下划线、美元符号$构成,且不能以数字开头,并且不能是JavaScript保留字。
JavaScript保留字:
abstract、boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto
implements、import、int、interface、long、native、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile
大写字母是可以使用的,并且大小写敏感。也就是说A和a是两个变量。
var A = 250; //变量1
var a = 888; //变量2
声明提升
JavaScript在解释执行之前,有预编译的过程,会将变量声明提升到最前面,但是不会将赋值提升。
变量类型
在JS中一共有六种数据类型:
- 基本数据类型(值类型):
- String 字符串
- Number 数值
- Boolean 布尔值
- Null 空值
- Undefined 未定义。
- 引用数据类型(引用类型):
- Object 对象。
注意:内置对象function、Array、Date、RegExp、Error等都是属于Object类型。除了五种基本数据类型之外,都称之为 Object类型。
面试问:引用数据类型有几种?
面试答:只有一种,即 Object 类型。
数据类型之间最大的区别:
- 基本数据类型:参数赋值的时候,传数值。
- 引用数据类型:参数赋值的时候,传地址(修改的同一片内存空间)。
字符串: String
- 字符串使用需要引号引起来。使用双引号或单引号都可以,但不能混用。
- 引号不能嵌套:双引号里不能再放双引号,单引号里不能再放单引号。但是单引号里可以嵌套双引号、双引号里可以嵌套单引号。
- 转义字符:在字符串中使用
\
作为转义字符,当表示一些特殊符号时可以使用\
进行转义。
将其他数值转换为字符串有三种方式:
- 拼串
- toString()
- String()
数值型:Number
- 由于内存的限制,ECMAScript 并不能保存世界上所有的数值。
- 如果使用Number表示的变量超过了最大值,则会返回
Infinity
。
NaN和isNaN()函数
NaN:是一个特殊的数字,表示Not a Number,非数值。
注意:typeof NaN
的返回结果是number。-
isNaN() :任何不能被转换为数值的值,都会让这个函数返回 true。
isNaN(NaN);// true isNaN("blue"); // true isNaN(123); // false
浮点数运算
在JS中,整数的运算基本可以保证精确;但是小数的运算,可能会得到一个不精确的结果。所以,千万不要使用JS进行对精确度要求比较高的运算。
var a = 0.1 + 0.2;
console.log(a); //打印结果:0.30000000000000004
布尔值:Boolean
true 和 false。主要用来做逻辑判断。
空值:null
专门用来表示一个为空的对象(例如:var a = null
)。
- Null类型的值只有一个,就是null。
- 使用 typeof 检查一个null值时,会返回object。
未定义:undefined
声明了一个变量,但是没有赋值(例如:var a;
),此时它的值就是undefined
。
- Undefined类型的值只有一个,就是undefind
- 使用 type of 检查一个undefined时,会返回undefined。
null和undefined有最大的相似性。看看null == undefined的结果(true)也就更加能说明这点。
但是null === undefined的结果(false)。它们虽然相似,但还是有区别的,其中一个区别是:和数字运算时,10 + null结果为:10;10 + undefined结果为:NaN。
任何数据类型和undefined运算都是NaN;
任何值和null运算,null可看做0运算。
赋值
将等号右边的值,赋给左边的变量;等号右边的变量,值不变。
隐式转换
我们知道,"2"+1得到的结果其实是字符串,但是"2"-1得到的结果却是数值1,这是因为计算机自动帮我们进行了“隐式转换”。
也就是说,-、*、/、`%``这几个符号会自动进行隐式转换。例如:
var a = "4" + 3 - 6;
console.log(a);
输出结果:
37
虽然程序可以对-、*、/、`%``这几个符号自动进行“隐式转换”;但作为程序员,我们最好自己完成转换,方便程序的可读性。
强制类型转换
强制类型转换:将一个数据类型强制转换为其他的数据类型。
类型转换主要指,将其他的数据类型,转换为:String、Number、Boolean。
其他的简单类型 --> String
-
变量+"" 或者 变量+"abc"
var a = 123; // Number 类型 console.log(a + ''); // 转换成 String 类型 console.log(a + 'haha'); // 转换成 String 类型
-
调用toString()方法
变量.toString()
该方法不会影响到原变量,它会将转换的结果返回。可以写成
a = a.toString()
,这样的话,就是直接修改原变量。null和undefined这两个值没有toString()方法。如果调用,会报错。
Number类型的变量,在调用toString()时,可以在方法中传递一个整数作为参数。此时它将会把数字转换为指定的进制,如果不指定则默认转换为10进
-
使用String()函数
String(变量)
- 对于Number和Boolean而言,实际上就是调用toString()方法。
- 但是对于null和undefined,就不会调用toString()方法。它会将 null 直接转换为 "null"。将 undefined 直接转换为 "undefined"。
其他的简单类型 --> Number
-
使用Number()函数
- 字符串 --> 数字
- 如果字符串中是纯数字,则直接将其转换为数字。
- 如果字符串中有非数字的内容,则转换为NaN。(此处可以看到Number()函数的局限性)
- 如果字符串是一个空串或者是一个全是空格的字符串,则转换为0。
- 布尔值 --> 数字
- true 转成 1
- false 转成 0
- null --> 数字
- 结果为:0
- undefined --> 数字
- 结果为:NaN
- 字符串 --> 数字
-
字符串 -> 整数:parseInt()
parseInt()
将字符串中的有效的整数内容转为数字。向下取整数。- 只保留字符串最开头的数字。
- 自动带有截断小数的功能:向下取整,不四舍五入。
- 如果对非String使用parseInt()或parseFloat(),它会先将其转换为String然后再操作。对于非字符串的非数值类型,不要使用parseInt进行转换,直接使用Number()函数进行转换。
- 带两个参数时,表示进制转换。
-
字符串 --> 浮点数:parseFloat()
parseFloat()
的作用是:将字符串转换为浮点数。parseFloat()
和parseInt()
的作用类似,不同的是,parseFloat()
可以获得有效的小数部分。
转换为 Boolean
使用Boolean()
- 情况一:数字 --> 布尔。除了0和NaN,其余的都是true。
- 情况二:字符串 ---> 布尔。除了空串,其余的都是true。
- 情况三:null和undefined都会转换为false。
- 情况四:对象也会转换为true。
typeof 运算符
获取变量类型:typeof ()
返回结果:
- typeof 数值的返回结果:number
- typeof 字符串的返回结果:string
- typeof 布尔型的返回结果:boolean
- typeof undefined的返回结果:undefined
- typeof null的返回结果:object
运算符
比如说+
、*
、/
、(
都是运算符,而(3+5)/2
则是表达式。
定义和分类
运算符也叫操作符。通过运算符可以对一个或多个值进行运算,并获取运算结果。
注:运算符都是会返回结果的。
运算符有很多分类,比如:
- 算数运算符
- 自增运算符
- 逻辑运算符
- 赋值运算符
- 关系运算符
- 三元运算符(条件运算符)
算术运算符
符号 | 功能 |
---|---|
+ | 加、字符串连接 |
- | 减 |
* | 乘 |
|除 | |
% | 取余 |
( ) | 括号 优先级 |
乘方
计算a的b次方Math.pow(a, b);
开方
计算数值a的开二次方Math.sqrt(a);
自增运算符(自减运算符)
自增 ++
自增分成两种:a++
和++a
。
对于一个变量自增以后,原变量的值会立即自增1。也就是说,无论是
a++
还是++a
,都会立即使原变量的值自增1。我们要注意的是:a是变量,而
a++
和++a
是表达式。a++
的值等于原变量的值(a自增前的值)++a
的值等于新值 (a自增后的值)
自减 --
原理同上。
逻辑运算符
逻辑运算符有三个:
-
&&
与(且):两个都为真,结果才为真。 -
||
或:只要有一个是真,结果就是真。 -
!
非:对一个布尔值进行取反。
注意:
- 能参与逻辑运算的,都是布尔值
- JS中的
&&
属于短路的与,如果第一个值为false
,则不会看第二个值。 - JS中的
||
属于短路的或,如果第一个值为true
,则不会看第二个值。 - 如果对非布尔值进行逻辑运算,则会先将其转换为布尔值,然后再操作。
非布尔值的与或运算
在实际开发中,经常用这种代码做容错处理。
非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,但返回结果是原值。
与运算的返回结果:(以两个非布尔值的运算为例)
- 如果第一个值为true,则必然返回第二个值(所以说,如果所有的值都为true,则返回的是最后一个值)
- 如果第一个值为false,则直接返回第一个值
或运算的返回结果:(以两个非布尔值的运算为例)
- 如果第一个值为true,则直接返回第一个值
- 如果第一个值为false,则返回第二个值
赋值运算符
可以将符号右侧的值赋值给符号左侧的变量。
-
+=
。a += 5 等价于 a = a + 5 -
-=
。a -= 5 等价于 a = a - 5 -
*=
。a *= 5 等价于 a = a * 5 -
/=
。a /= 5 等价于 a = a / 5 -
%=
。a %= 5 等价于 a = a % 5
关系运算符
通过关系运算符可以比较两个值之间的大小关系,如果关系成立它会返回true,如果关系不成立则返回false。
-
>
大于 -
<
小于 -
>=
大于或等于 -
<=
小于或等于 -
==
等于 -
===
全等于 -
!=
不等于 -
!==
不全等于
非数值的比较
- 对于非数值进行比较时,会将其转换为数字然后再比较。
- 特殊情况:如果符号两侧的值都是字符串时,不会将其转换为数字进行比较。比较两个字符串时,比较的是字符串的
Unicode
编码。- 比较字符编码时,是一位一位进行比较。如果两位一样,则比较下一位,所以借用它可以来对英文进行排序。
- 比较两个字符串型的数字时,要先转型。
- 任何值和NaN做任何比较都是false。
==
的强调
注意==
这个符号,它是判断是否等于,而不是赋值。
-
==
这个符号,可以验证字符串是否相同。 -
==
这个符号并不严谨,会将不同类型的东西,转为相同类型进行比较(大部分情况下,都是转换为数字)。 - undefined 衍生自 null,所以这两个值做相等判断时,会返回true。
- NaN不和任何值相等,包括他本身。
===
的强调
如果要保证完全等于,我们就要用三个等号===
。全等不会做类型转换。
三元运算符(条件运算符)
三元运算符也叫条件运算符。
语法:条件表达式?语句1:语句2;
执行的流程:
- 条件运算符在执行时,首先对条件表达式进行求值:
- 如果该值为true,则执行语句1,并返回执行结果
- 如果该值为false,则执行语句2,并返回执行结果
如果条件表达式的求值结果是一个非布尔值,会将其转换为布尔值然后再运算。
运算符优先级
建议多用( )
运算符的优先级如下:(越往上,优先级越高)
-
.
、[]
、new
()
-
++
、--
-
!
、~
、+
(单目)、-
(单目)、typeof
、void
、delete
-
%
、*
、/
-
+
(双目)、-
(双目) -
<<
、>>
、>>>
-
<
、<=
、>
、>=
-
==
、!==
、===
&
^
|
&&
||
?:
-
=
、+=
、-=
、*=
、/=
、%=
、<<=
、>>=
、>>>=
、&=
、^=
、|=
,
if语句
-
条件判断语句
条件成立才执行。如果条件不成立,那就什么都不做。if (条件表达式) { // 条件为真时,做的事情 }
-
条件分支语句
-
格式1:
if (条件表达式) { // 条件为真时,做的事情 } else { // 条件为假时,做的事情 }
格式2:
if (条件表达式1) { // 条件1为真时,做的事情 } else if (条件表达式2) { // 条件1不满足,条件2满足时,做的事情 } else if (条件表达式3) { // 条件1、2不满足,条件3满足时,做的事情 } else { // 条件1、2、3都不满足时,做的事情 }
-
switch语句(条件分支语句)
格式:
switch(表达式) {
case 值1:
语句体1;
break;
case 值2:
语句体2;
break;
...
...
default:
语句体 n+1;
break;
}
执行流程
- 首先,计算出表达式的值,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结束。
- 然后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分。
结束条件
- 情况a:遇到break就结束,而不是遇到default就结束。(因为break在此处的作用就是退出switch语句)
- 情况b:执行到程序的末尾就结束。
注意: 必须要在每个case
和default
中写上break;
,否则会出现case穿透
。
case穿透
:在满足条件的case
或default
中,没有遇到break;
,程序继续按顺序执行,直到遇到后续case
或default
中的break;
,或整个程序全部执行完毕后,程序才会结束。
for循环
语法:
for(①初始化表达式; ②条件表达式; ④更新表达式){
③语句...
}
执行流程:
- ①执行初始化表达式,初始化变量(初始化表达式只会执行一次)
- ②执行条件表达式,判断是否执行循环:
- 如果为true,则执行循环③
- 如果为false,终止循环
- ④执行更新表达式,更新表达式执行完毕继续重复②
while循环
语法:
while(条件表达式){
语句...
}
执行流程:
- while语句在执行时,先对条件表达式进行求值判断
- 如果值为true,则执行循环体
- 循环体执行完毕以后,继续对表达式进行判断
- 如果为true,则继续执行循环体,以此类推
- 如果值为false,则终止循环
注意: 如果有必要的话,可以使用break;
来终止循环。
do...while循环
语法:
do{
语句...
}while(条件表达式)
执行流程:
- do...while语句在执行时,先执行循环体
- 循环体执行完毕以后,在对while后的条件表达式进行判断
- 如果结果为true,则继续执行循环体,执行完毕继续判断以此类推
- 如果结果为false,则终止循环
while循环和 do...while循环的区别:
- while是先判断后执行,而do...while是先执行后判断。
- do...while可以保证循环体至少执行一次,而while不能。
break
和continue
break
- break可以用来退出switch语句或整个循环语句(循环语句包括for、while)。
- break会立即终止离它最近的那个循环语句。
continue
-
continue
可以用来跳过当次循环。 -
continue
默认只会离他最近的循环起作用。
输出信息
弹出警告框:alert()
alert(请少用alert())
-
控制台输出:console.log()
var a = "请少用console.log()"; console.log(a); console.error("Error!"); console.warn("Warning!");
-
带确定和取消按钮的弹框:confirm()
var res = confirm("请少用confirm()"); // 选择确定,res值为true;选择取消,res值为false
-
带输入框的弹框:prompt()语句
var a = prompt("请少用prompt()"); console.log(a);
函数
函数:就是将一些功能或语句进行封装,在需要的时候,通过调用的形式,执行这些语句。
- 函数也是一个对象
- 使用typeof检查一个函数对象时,会返回function
函数的作用:
- 将大量重复的语句写在函数里,以后需要这些语句的时候,可以直接调用函数,避免重复劳动。
- 简化编程,让编程模块化。
定义
-
使用函数声明来创建一个函数。语法:
function 函数名([形参1,形参2...形参N]){ // 备注:语法中的中括号,表示“可选” 语句... } // 举例 function sum(a, b){ return a+b; }
- function:是一个关键字。中文是“函数”、“功能”。
- 函数名字:命名规定和变量的命名规定一样。只能是字母、数字、下划线、美元符号,不能以数字开头。
- 参数:可选。
- 大括号里面,是这个函数的语句。
- 在有些编辑器中,方法写完之后,我们在方法的前面输入
/**
,然后回车,会发现,注释的格式会自动补齐。
函数声明提升,会将整个函数对象提升到最前面。
-
使用函数表达式来创建一个函数。语法:
var 函数名 = function([形参1,形参2...形参N]){ 语句.... } // 举例 var fun3 = function() { console.log("我是匿名函数中封装的代码"); };
所谓的“函数表达式”,其实就是将匿名函数赋值给一个变量。
使用构造函数来创建一个对象。(用的少)
调用
语法:
函数名([形参1,形参2...形参N]);
参数
形参
- 可以在函数的
()
中来指定一个或多个形参。 - 多个形参之间使用
,
隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值。
实参
- 在调用函数时,可以在
()
中指定实参。 - 实参将会赋值给函数中对应的形参。
实参的类型:
- 函数的实参可以是任意的数据类型。
- 调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查。
实参的数量:
注意: JavaScript里不会对函数实际传入的参数进行检测。可以传值,也可以不传值,也可以任意传多个值
调用函数时,解析器也不会检查实参的数量:
- 多余实参不会被赋值。
- 如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined。
类数组arguments
可以通过arguments
获取所有的实际传递的参数。
// 定义add()函数
function add() {
//arguments:可以获取所有实际传递的参数
var sum = 0;
console.log(arguments)
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
return sum
}
// 调用add()函数
var res = add(1, 3, 3, 5, 6, 7)
console.log(res);
在调用函数时,浏览器每次都会传递进两个隐含的参数:
- 函数的上下文对象
this
- 封装实参的对象
arguments
arguments
是一个类数组对象,它可以通过索引来操作数据,也可以获取长度。
arguments
代表的是实参。在调用函数时,我们所传递的实参都会在arguments
中保存。有个讲究的地方是:arguments
只在函数中使用。
- 返回函数实参的个数:
arguments.length
-
arguments.length
可以用来获取实参的长度。
-
- 返回正在执行的函数:
arguments.callee
-
arguments
里边有一个属性叫做callee
,这个属性对应一个函数对象,就是当前正在指向的函数对象。 - 在使用函数递归调用时,推荐使用
arguments.callee
代替函数名本身。
-
-
arguments
可以修改元素-
arguments
是伪数组,是因为:arguments
可以修改元素,但不能改变数组的长短。
-
返回值
return
的作用是结束方法。
//举例
function sum(a, b) {
return a + b;
}
console.log(sum(3, 4))
注意:
-
return
后的值将会作为函数的执行结果返回,可以定义一个变量,来接收该结果。 - 在函数中
return
后的语句都不会执行(函数在执行完return
语句之后停止并立即退出)。 - 如果
return
语句后不跟任何值,就相当于返回一个undefined
。 - 如果函数中不写
return
,则也会返回undefined
返回值可以是任意的数据类型,可以是对象,也可以是函数。
函数名、函数体和函数加载问题
函数名 == 整个函数 :
//定义fn方法
function fn(){
alert(1)
};
console.log(fn) == console.log(function fn(){alert(1)});
调用一个函数时,通常使用函数()
这种格式;此时,是直接使用函数这种格式,它的作用相当于整个函数。
函数的加载问题:JS加载的时候,只加载函数名,不加载函数体。所以如果想使用内部的成员变量,需要调用函数。
fn()
和fn
的区别:
-
fn()
:调用函数。相当于获取了函数的返回值。 -
fn
:函数对象。相当于直接获取了函数对象。
立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数。
立即执行函数往往只会执行一次。为什么呢?因为没有变量保存它,执行完了之后,就找不到它了。
方法
函数也可以称为对象的属性。如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法。
调用这个函数就说调用对象的方法(method)。相比于方法,它只是名称上的区别,并没有其他的区别。
函数举例:
// 调用函数
fn();
方法举例:
// 调用方法
obj.fn();
作用域
作用域指一个变量的作用范围。在js中,一共有两种作用域:
- 全局作用域
- 函数作用域
全局作用域
直接编写在script标签中的JS代码,都在全局作用域。
- 全局作用域在页面打开时创建,在页面关闭时销毁。
- 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用。
在全局作用域中:
- 创建的变量都会作为window对象的属性保存。
- 创建的函数都会作为window对象的方法保存。
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到。
函数作用域
- 作用域:变量和函数生效的区域。作用域在函数定义时,就已经确定了。
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。 - 执行期上下文:当函数执行时,会创建一个执行期上下文的内部对象。每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立的。当函数执行完毕,它所产生的执行期上下文会被销毁。
当函数内部的变量被另外一个函数所引用,那么这个函数的变量将不会在执行完毕后销毁。
参考链接 - 作用域的上下级关系:
- 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用(就近原则)。如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错
ReferenceError
。 - 在函数中要访问全局变量可以使用
window
对象。(比如说,全局作用域和函数作用域都定义了变量a,如果想访问全局变量,可以使用window.a)
- 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用(就近原则)。如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错
提醒1:
- 在函数作用域也有声明提前的特性:
- 使用var关键字声明的变量,会在函数中所有的代码执行之前被声明。
- 函数声明也会在函数中所有的代码执行之前执行。
因此,在函数中,没有var
声明的变量都是全局变量,而且并不会提前声明。
提醒2:
- 定义形参就相当于在函数作用域中声明了变量。
面向对象
对象的作用是:封装信息。比如Student类里可以封装学生的姓名、年龄、成绩等。
对象具有特征(属性)和行为(方法)。
面向对象:可以创建自定义的类型,很好的支持继承和多态。
面向对象的特征:封装、继承、多态。
简介
基本数据类型:
- 基本数据类型的值直接保存在栈内存中,值与值之间是独立存在,修改一个变量不会影响其他的变量。
对象:
- 只要不是五种基本数据类型,就全都是对象。
- 如果使用基本数据类型的数据,我们所创建的变量都是独立,不能成为一个整体。
- 对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。
- 对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间。变量保存的是对象的内存地址(对象的引用)。
- 换而言之,对象的值是保存在堆内存中的,而对象的引用(即变量)是保存在栈内存中的。
- 如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响。
分类
- 内置对象:
由ES标准中定义的对象,在任何的ES的实现中都可以使用
比如:Math、String、Number、Boolean、Function、Object.... - 宿主对象:
由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象。
比如BOM、DOM。比如console
、document
。 - 自定义对象:
由开发人员自己创建的对象
基本操作
-
创建对象
- 使用
new
关键字调用的函数,是构造函数constructor
。构造函数是专门用来创建对象的函数。 - 例如:
var obj = new Object();
- 使用
-
向对象中添加属性
- 在对象中保存的值称为属性。
- 向对象添加属性的语法:
对象.属性名 = 属性值;
- 对象的属性值可以是任何的数据类型,也可以是个函数(也称之为方法)。
- js中的属性值,也可以是一个对象。
-
获取对象中的属性
- 方式1:
对象.属性名
。如果获取对象中没有的属性,不会报错而是返回undefined。 - 方式2:可以使用
对象["属性名"] = 属性值
这种形式去操作属性。- 重要:使用
[]
这种形式去操作属性,更加的灵活,因为,我们可以在[]
中直接传递一个变量,这样变量值是多少就会读取那个属性。
- 重要:使用
- 方式1:
-
修改对象的属性值
对象.属性名 = 新值 对象[属性名] = 新值
-
删除对象的属性
delete obj.name delete obj['name']
-
in
运算符
通过in
运算符可以检查一个对象中是否含有指定的属性。如果有则返回true
,没有则返回false
。"属性名" in 对象 对象.hasOwnProperty('属性名')
-
用对象字面量创建对象
var obj = {};
// 举例 var obj1 = { name: "小明", age: 12, gender: "男", body: { height: "150cm", weight: "35Kg" }, sayName: function () { console.log("我是" + this.name) } } console.log(obj1.body.weight); console.log(obj1.sayName()); }
-
遍历对象中的属性:for in
语法:for (var 变量 in 对象) { }
解释:对象中有几个属性,循环体就会执行几次。每次执行时,会将对象中的每个属性的属性名赋值给变量。
创建对象
对象字面量
对象的字面量就是一个{}
。里面的属性和方法均是键值对。
var Person = {
name: "小明",
age: "12",
gender: "男",
sayName: function() {
console.log("我是" + this.name)
}
}
工厂模式
可以大批量的创建对象。
function Person(name, age, gender) {
var person = {
name: name,
age: age,
gender: gender,
sayName: function () {
console.log("我是" + this.name);
}
}
return person
}
var p = Person("小明", "12", "男")
console.log(p);
p.sayName()
弊端:
- 使用工厂方法创建的对象,使用的构造函数都是Object。所以创建的对象都是Object这个类型,就导致我们无法区分出多种不同类型的对象。
利用构造函数
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.sayName = function () {
console.log("我是" + this.name)
}
}
var p = new Person("小明", "12", "男")
console.log(p)
p.sayName()
- 构造函数和普通函数的区别
- 构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。
- 构造函数和普通函数的区别就是调用方式的不同:普通函数是直接调用,而构造函数需要使用new关键字来调用。
-
this
的指向也有所不同:- 以函数的形式调用时,
this
永远都是window
。比如fun();
相当于window.fun();
- 以方法的形式调用时,
this
是调用方法的那个对象 - 以构造函数的形式调用时,
this
是新创建的那个对象
- 以函数的形式调用时,
new
一个构造函数的执行流程:
- 开辟内存空间,存储新创建的对象
- 将新建的对象设置为构造函数中的this,在构造函数中可以使用this来引用 新建的对象
- 执行函数中的代码(包括设置对象属性和方法等)
- 将新建的对象作为返回值返回
利用class
创建对象
利用class
创建对象是es6
的规范。
class Person{
constructor(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.sayName = function() {
console.log("我是" + this.name)
}
}
}
var p = new Person("小明", "12", "男")
console.log(p)
p.sayName()
完整构造函数创建对象的方式
- 创建构造函数
- 创建原型对象
- 设置构造函数原型对象属性
prototype
原型对象
共享属性和方法。
prototype
var objProto = {
fn1: function() {},
fn2: function() {}
}
Obj.prototype = objProto
Oby.prototype.fn3 = function() {}
-
实例.__proto__
和构造函数.prototype
都指的是原型对象。 - 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
- 使用
in
检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
。 - 可以使用对象的
hasOwnProperty()
来检查对象自身中是否含有该属性。
原型链
原型对象也是对象,所以它也有原型,当我们使用或访问一个对象的属性或方法时:
- 它会先在对象自身中寻找,如果有则直接使用;
- 如果没有则会去原型对象中寻找,如果找到则直接使用;
- 如果没有则去原型的原型中寻找,直到找到Object对象的原型。
- Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回
null
类、实例
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。
通过一个构造函数创建的对象,称为该类的实例。
使用instanceof
可以检查一个对象是否为一个类的实例。
语法如下:
对象 instanceof 构造函数
如果是,则返回true
;否则返回false
。
所有的对象都是Object的后代,因此 任何对象 instanceof Object
的返回结果都是true
。
栈内存和堆内存
JS中,所有的变量都是保存在栈内存中的。
基本数据类型:
- 基本数据类型的值,直接保存在栈内存中。值与值之间是独立存在,修改一个变量不会影响其他的变量。
引用数据类型:
- 对象是保存到堆内存中的。每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存了对象的内存地址(对象的引用)。如果两个变量保存了同一个对象的引用,当一个通过一个变量修改属性时,另一个也会受到影响。
this
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的 上下文对象。
根据函数的调用方式的不同,this会指向不同的对象:
- 以函数的形式调用时,this永远都是window。比如
fun();
相当于window.fun();
- 以方法的形式调用时,this是调用方法的那个对象
- 以构造函数的形式调用时,this是新创建的那个对象
- 使用call和apply调用时,this是指定的那个对象
箭头函数中this的指向:
- ES6中的箭头函数并不会使用上面四条标准的绑定规则,而是会继承外层函数调用的this绑定(无论this绑定到什么)。
改变this指向
-
通过
call
或apply
在调用函数时修改this
指向obj1.fn.call(obj2 [, "参数1", "参数2", ···]) // 参数按次序传入 obj1.fn.apply(obj2 [, ["参数1", "参数2", ···]]) // 参数以数组的形式传入
-
通过
bind
修改this
指向var obj1 = { fn1: function() {}.bind(obj2) } var fn2 = function() {}.bind(obj3)
JSON
JSON:JavaScript Object Notation(JavaScript对象表示形式)。
JSON和对象字面量的区别:JSON的属性必须用双引号引号引起来,对象字面量可以省略。
{
"name": "小明",
"age": "12",
"gender": "男",
};
注:json里一般放常量、数组、对象等,但很少放function。
对象和json没有长度,json.length
的打印结果是undefined
。不能用for循环遍历(因为遍历时需要获取长度length)。
json采用 for...in...
进行遍历
数组
数组(Array)是属于内置对象。
数组和普通对象的功能类似,也是用来存储一些值的。不同的是:
- 普通对象是使用字符串作为属性名的,而数组是使用数字来作为索引来操作元素。索引:从0开始的整数就是索引。
数组的存储性能比普通对象要好。在实际开发中我们经常使用数组来存储一些数据,使用频率非常高。
数组的基本操作
数组的元素可以是任意的数据类型,也可以是对象,也可以是函数,也可以是数组。
数组的元素中,如果存放的是数组,我们就称这种数组为二维数组。
-
创建数组对象
- 方式一:字面量定义。
var arr = [1,2,3];
- 方式二:对象定义(数组的构造函数)。
var arr = new Array(参数);
- 如果参数为空,则表示创建一个空数组
- 参数位置是一个数值时,表示数组长度
- 参数位置是多个数值时,表示数组中的元素。
数组的类型其实也是属于对象。
- 方式一:字面量定义。
向数组中添加元素
数组[索引] = 值
获取数组中的元素
数组[索引]
如果读取不存在的索引(比如元素没那么多),系统不会报错,而是返回undefined。
获取数组的长度
数组的长度 = 数组名.length;
对于连续的数组,使用length可以获取到数组的长度(元素的个数);对于非连续的数组,使用length会获取到数组的最大的索引+1。因此,尽量不要创建非连续的数组。
-
修改数组的长度(修改length)
- 如果修改的
length
大于原长度,则多出部分会空出来,置为null
。 - 如果修改的
length
小于原长度,则多出的元素会被删除,数组将从后面删除元素。 - 特例:伪数组
arguments
的长度可以修改,但是不能修改里面的元素。
- 如果修改的
数组的常用方法
概览
四个基本方法:
方法 | 描述 | 备注 |
---|---|---|
push() | 向数组的最后面插入一个或多个元素,返回结果为该数组新的长度 | 会改变原数组 |
pop() | 删除数组中的最后一个元素,返回结果为被删除的元素 | 会改变原数组 |
unshift() | 在数组最前面插入一个或多个元素,返回结果为该数组新的长度 | 会改变原数组 |
shift() | 删除数组中的第一个元素,返回结果为被删除的元素 | 会改变原数组 |
常见方法:
方法 | 描述 | 备注 |
---|---|---|
slice() | 从数组中提取指定的一个或多个元素,返回结果为新的数组 | 不会改变原数组 |
splice() | 从数组中删除指定的一个或多个元素,返回结果为新的数组 | 会改变原数组 |
concat() | 连接两个或多个数组,返回结果为新的数组 | 不会改变原数组 |
join() | 将数组转换为字符串,返回结果为转换后的字符串 | 不会改变原数组 |
reverse() | 反转数组,返回结果为反转后的数组 | 会改变原数组 |
sort() | 对数组的元素,默认按照Unicode编码,从小到大进行排序 | 会改变原数组 |
遍历数组的方法:
方法 | 描述 | 备注 |
---|---|---|
for循环 | ||
forEach() | 和 for循环类似,但需要兼容IE8以上 | forEach() 没有返回值。也就是说,它的返回值是 undefined |
map() | 对原数组中的每一项进行加工,将组成新的数组 | 不会改变原数组 |
filter() | 对数组中每一项运行回调函数,该函数返回结果是true的项,将组成新的数组,返回结果为新的数组。可以起到过滤的作用 | 不会改变原数组 |
every() | 如果有一项返回false,则停止遍历,此方法返回 false | 一假即假。要求每一项都返回true,最终的结果才返回true |
some() | 只要有一项返回true,则停止遍历,此方法返回true | 一真即真。要求每一项都返回false,最终的结果才返回false |
reduce | 为数组中的每一个元素,依次执行回调函数 |
其他方法:
方法 | 描述 |
---|---|
indexOf(value) | 从前往后索引,获取 value 在数组中的第一个下标 |
lastIndexOf(value) | 从后往前索引,获取 value 在数组中的最后一个下标 |
find(function()) | 找出第一个满足「指定条件返回true」的元素。 |
findIndex(function()) | 找出第一个满足「指定条件返回true」的元素的index |
Array.from(arrayLike) | 将伪数组转化为真数组 |
Array.of(value1, value2, value3) | 将一系列值转换成数组。 |
详解
基本方法
-
push()
向数组的最后面插入一个或多个元素,返回结果为该数组新的长度。
数组的新长度 = 数组.push(元素);
-
pop()
删除数组中的最后一个元素,返回结果为被删除的元素。
被删除的元素 = 数组.pop();
-
unshift()
在数组最前面插入一个或多个元素,返回结果为该数组新的长度。插入元素后,其他元素的索引会依次调整。
数组的新长度 = 数组.unshift(元素);
-
shift()
删除数组中的第一个元素,返回结果为被删除的元素。
被删除的元素 = 数组.shift();
常见方法
-
slice()
从数组中提取指定的一个或者多个元素,返回结果为新的数组(不会改变原来的数组)。
备注:该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回。新数组 = 原数组.slice(开始位置的索引, 结束位置的索引); //注意:包含开始索引,不包含结束索引
-
splice()
从数组中删除指定的一个或多个元素,返回结果为新的数组(会改变原来的数组)。
备注:该方法会改变原数组,会将指定元素从原数组中删除;被删除的元素会封装到一个新的数组中返回。新数组 = 原数组.splice(起始索引index, 需要删除的个数, 第三个参数, 第四个参数...);
第三个及之后的参数,表示:向原数组中添加新的元素,这些元素将会自动插入到开始位置索引的前面。
concat()
连接两个或多个数组,返回结果为新的数组。(不会改变原数组)。
新数组 = 数组1.concat(数组2, 数组3 ...);
join()
将数组转换为字符串,返回结果为转换后的字符串(不会改变原来的数组)。
补充:join()
方法可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符;如果不指定连接符,则默认使用,
作为连接符,此时和toString()
的效果是一致的。
新的字符串 = 原数组.join(参数); // 参数选填
reverse()
反转数组,返回结果为反转后的数组(会改变原来的数组)。
反转后的数组 = 数组.reverse();
-
sort()
对数组的元素进行从小到大来排序(会改变原来的数组)。无参时
使用sort()
方法时不带参,则默认按照Unicode编码,从小到大进行排序。
排序后的数组 = 数组.sort();
-
带参时
- 在
sort()
方法中带参,我们就可以自定义排序规则。 - 可以在sort()添加一个回调函数,来指定排序规则。回调函数中需要定义两个形参,浏览器将会分别使用数组中的元素作为实参去调用回调函数
- 浏览器根据回调函数的返回值来决定元素的排序:(重要)
- 如果返回一个大于0的值,则元素会交换位置
- 如果返回一个小于0的值,则元素位置不变
- 如果返回一个0,则认为两个元素相等,则不交换位置
- 在
-
冒泡排序
// 自定义排序规则 var result = arr.sort(function(a, b) { return a - b; // 升序排列 // return b - a; // 降序排列 });
遍历方法
遍历数组即:获取并操作数组中的每一个元素。在我们的实战开发中,使用得非常频繁。
遍历数组的方法包括:every()
、filter()
、forEach()
、map()
、some()
备注:这几个方法不会修改原数组。
数组/boolean/无 = 数组.every/filter/forEach/map/some(function(item, index, arr){
程序和返回值;
})
-
forEach()
forEach()方法需要一个函数作为参数。这种函数,是由我们创建但是不由我们调用的,我们称为回调函数。
数组中有几个元素,该回调函数就会执行几次。执行完毕后,浏览器会将遍历到的元素。
回调函数中传递三个参数:- 第一个参数,就是当前正在遍历的元素
- 第二个参数,就是当前正在遍历的元素的索引
- 第三个参数,就是正在遍历的数组
注意,
forEach()
的返回值是undefined
。也就是说,它没有返回值。如果你尝试tempArry = arr.forEach()
这种方式来接收,是达不到效果的。
-
map()
对数组中每一项运行回调函数,返回该函数的结果,组成的新数组(返回的是加工之后的新数组)。const arr2 = arr1.map(item => item.name); // 将数组 arr1 中的 name 属性,存储到 数组 arr2中
-
filter()
对数组中每一项运行回调函数,该函数返回结果是true的项,将组成新的数组(返回值就是这个新的数组)。const arr1 = [1, 3, 6, 2, 5, 6]; const arr2 = arr1.filter(item => item > 4); //将arr1中大于4的元素返回,组成新的数组
every()
对数组中每一项运行回调函数,如果都返回true
,every
就返回true
;如果有一项返回false
,则停止遍历,此方法返回false
。
注意:every()
方法的返回值是boolean
值,参数是回调函数。
some()
对数组中每一项运行回调函数,只要有一项返回true
,则停止遍历,此方法返回true
。
注意:some()
方法的返回值是boolean
值。
-
reduce()
为数组中的每一个元素,依次执行回调函数。
需要对每个内容进行遍历,并且最终输出1个结果的时候,可以使用reduce()
arr.reduce( function(previousValue, item, index, arr) { }, initialValue)
参数解释:
-
previousValue
:上一次调用回调函数时的返回值,或者初始值 -
currentValue
:当前正在处理的数组元素 -
currentIndex
:当前正在处理的数组元素下标 -
array
:调用reduce()
方法的数组 -
initialValue
:可选的初始值(作为第一次调用回调函数时传给previousValue
的值)
举例:
var arr = [2, 0, 1, 9, 6]; sumValue = arr.reduce(function(total, item) { // 计算 arr 数组中,所有元素项的综合 return total + item; }, 0); console.log('sumValue:' + sumValue); // 打印结果:18
-
其他方法
-
indexOf()
和lastIndexOf()
获取数据的索引。索引值 = 数组.indexOf(value); 索引值 = 数组.lastIndexOf(value);
解释:
-
indexOf(value)
:从前往后索引,获取value
在数组中的第一个下标。 -
lastIndexOf(value)
:从后往前索引,获取value
在数组中的最后一个下标。
作用:
- 利用这个方法,我们可以判断某个值是否在指定的数组中。如果没找到则返回
-1
。
-
find()
找出第一个满足「指定条件返回true」的元素。
find(function(item, index, arr){return true})
备注:一旦找到符合条件的第一个元素,将不再继续往下遍历。
findIndex()
找出第一个满足「指定条件返回true」的元素的index。
findIndex(function(item, index, arr){return true})
-
Array.from()
将伪数组或可遍历对象转换为真数组。
array = Array.from(arrayLike)
伪数组与真数组的区别:- 伪数组的原型链中没有 Array.prototype,而真数组的原型链中有 Array.prototype。因此伪数组没有 pop、join等属性。
- 伪数组的原型链中没有 Array.prototype,而真数组的原型链中有 Array.prototype。因此伪数组没有 pop、join等属性。
Array.of()
将一系列值转换成数组。
Array.of(value1, value2, value3)
isArray()
判断是否为数组。
布尔值 = Array.isArray(被检测的值);
toString()
把数组转换成字符串,每一项默认用,
分割。
字符串 = 数组.toString();
valueOf()
返回数组本身。
数组本身 = 数组.valueOf();
这个方法的意义不大。因为我们指直接写数组对象的名字,就已经是数组本身了。
包装类
JS为我们提供了三个包装类:
-
String()
:将基本数据类型字符串,转换为String对象。 -
Number()
:将基本数据类型的数字,转换为Number对象。 -
Boolean()
:将基本数据类型的布尔值,转换为Boolean对象。
通过上面这这三个包装类,我们可以将基本数据类型的数据转换为对象。
需要注意的是:我们在实际应用中不会使用基本数据类型的对象。如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果。
方法和属性只能添加给对象,不能添加给基本数据类型。
当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法;调用完以后,在将其转换为基本数据类型。
垃圾回收(GC)机制
程序运行过程中会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢。所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
当一个对象没有任何的变量或属性对它进行引用时,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
上面这句话,也可以这样理解:如果堆内存中的对象,没有任何变量指向它时,这个堆内存里的对象就会成为垃圾。
JS拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁。我们不需要也不能进行垃圾回收的操作。我们仅仅需要做的是:如果你不再使用该对象,那么,将改对象的引用设置为 null 即可。