文章目录
11.21
虽然还没学完H5和CSS,怀着好奇心,先来了解一下JS。
ECMAScrpt是JavaScript标准,一般情况下我们认为这两个词是一个意思。
但一个完整的JavaScrpt由三部分构成:ECMAScript(标准/浏览器端)、DOM(文档对象模型,提供操作对象使我们操纵网页)、BOM(浏览器对象模型,提供操作对象使我们操纵浏览器)
11.22
JS 写Hello World/引入
-
JS按顺序执行。
-
语句:
- alert控制浏览器弹出一个警告框。
- document.write()可以向body内输出一个内容。
- console.log()可以在控制台内输出一个内容,控制台在右键“检查”里可以看。
alert("警告:Hello World");
/*打开页面时弹出警告框,内容是"Hello World"*/
document.write("我出现在body中");
/*括号内的话出现在主页面内*/
console.log("我出现在控制台中");
11.23
- JS书写的位置
- script标签内写JS,也可以引入外部文件,引入仍然用script。
- 可以将JS写到标签的onclick属性中 ,比如点击按钮执行js。
- 可以点击超链接执行js,写在href中。
<script type="text/javascript" src=""></script>
/*引入外部js文件,src内写文件路径*/
<button onclick="alert('讨厌,点我干嘛啦!');">点我一下</button>
/*按钮,注意符号*/
<a href="javascript:alert('让你点你就点?!');">你也点我一下</a>
/*超链接*/
数据类型
- var声明变量,变量必须要声明和赋值。
其实声明变量也可以不用var,至于为什么要用var,请看“作用域”部分。
- JS数据类型:
- String 字符串
- Number 数值
- Boolean 布尔值
- Null 空值
- Undefined 未定义
- Object 对象
- (前五个属于基本数据类型,最后一个属于引用数据类型)
- 转义字符:
- \" 表示 "
- \’ 表示 ’
- \n 表示换行
- \t 制表符
- \\ 表示\
- 输出字面量和输出变量:
//输出字面量 字符串str
//alert("str");
//输出变量str
//alert(str);
-
在JS中所有的数值都是Number类型,包括整数和浮点数(小数)。
-
JS中可以表示的数字的最大值。
Number.MAX_VALUE=1.7976931348623157e+308 -
Number.MIN_VALUE 大于0的最小值=5e-324
-
如果使用Number表示的数字超过了最大值,则会返回一个Infinity 表示正无穷,-Infinity 表示负无穷。
-
使用typeof检查Infinity也会返回number
-
NaN 是一个特殊的数字,表示Not A Number,使用typeof检查一个NaN也会返回number。
Boolean 布尔值
- 布尔值只有两个,主要用来做逻辑判断:
- true 表示真
- false 表示假
- 使用typeof检查一个布尔值时,会返回boolean
其他类型:
-
Null(空值)类型的值只有一个,就是null。null这个值专门用来表示一个为空的对象。
-
使用typeof检查一个null值时,会返回object。
-
Undefined(未定义)类型的值只有一个,就undefind。当声明一个变量,但是并不给变量赋值时,它的值就是undefined。
-
使用typeof检查一个undefined时也会返回undefined
数据类型转换
将其他的数据类型转换为String
- 方式一: 调用被转换数据类型的toString()方法。
- 该方法不会影响到原变量,它会将转换的结果返回
- 但是注意:null和undefined这两个值没有toString()方法,如果调用他们的方法,会报错。
调用xxx的yyy()方法,就是xxx.yyy()。
- 方式二:调用String()函数,并将被转换的数据作为参数传递给函数。
- 使用String()函数做强制类型转换时,对于Number和Boolean实际上就是调用的toString()方法。但是对于null和undefined,就不会调用toString()方法,它会将 null 直接转换为 “null”,将 undefined 直接转换为 “undefined”。
将其他的数据类型转换为Number
-
转换方式一:使用Number()函数
-
字符串 --> 数字
1.如果是纯数字的字符串,则直接将其转换为数字。
2.如果字符串中有非数字的内容,则转换为NaN。
3.如果字符串是一个空串或者是一个全是空格的字符串,则转换为0。 -
布尔 --> 数字
true 转成 1
false 转成 0 -
null --> 数字 0
-
undefined --> 数字 NaN
-
转换方式二:专门用来对付字符串
-
parseInt() 把一个字符串转换为一个整数。
-
parseFloat() 把一个字符串转换为一个浮点数。
-
备注:
parseInt遇到非数字停止,如果一开始的字符非数字,则转换为NaN。parseFloat可以遇到一个小数点。
如果对非String使用parseInt()或parseFloat(),它会先将其转换为String再操作。所以用这种方法,布尔会转换为NaN。
转换为其它进制的数字
- 可以在parseInt()中传递一个第二个参数,来指定数字的进制:
a = parseInt(a,10);
将其他的数据类型转换为Boolean
-
调用Boolean()函数来将其他类型转换为布尔值。
-
数字 —> 布尔
除了0和NaN,其余的都是true。 -
字符串 —> 布尔
除了空串,其余的都是true。 -
null和undefined都会转换为false。
-
对象也会转换为true。
11.26
运算符
算数运算符
-
当对非Number类型的值进行运算时,会将这些值转换为Number再运算。
-
任何值和NaN做运算都得NaN。
-
+ 可以对两个值进行加法运算,并将结果返回。
如果对两个字符串进行加法运算,则会做拼串。
会将两个字符串拼接为一个字符串,并返回。 -
'+'两侧只要有一侧是字符串,另一侧的数字则会自动转换成字符串,因为其中存在隐式转换
-
- 可以对数字进行符号的取反。
逻辑运算符
- 符号和C中相同,非 在下方↓
- JS中的“与”属于短路的与,如果第一个值为false,则不会看第二个值。
- JS中的“或”属于短路的或,如果第一个值为true,则不会检查第二个值。
&& || 非布尔值的情况
-
对于非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,并且返回原值。
-
与运算:
- 如果第一个值为true,则返回第二个值。
(两个值都为true,也返回第二个值,即靠后的true) - 如果第一个值为false,则返回第一个值。
(两个值都为flase,也返回第一个值,即靠前的false) -
And:true后false前。
- 如果第一个值为true,则返回第二个值。
-
或运算
- 如果第一个值为true,则直接返回第一个值。
(两个值都为true,也返回第一个值,即靠前的true) - 如果第一个值为false,则返回第二个值。
(两个值都为flase,也返回第一个值,即靠后的false) -
Or:true前false后。
- 如果第一个值为true,则直接返回第一个值。
关系运算符
- 非数值的情况
- 对于非数值进行比较时,会将其转换为数字再比较。
- 如果符号两侧的值都是字符串时,不会将其转换为数字进行比较,而会分别比较字符串中字符的Unicode编码。比较字符编码时是一位一位进行比较。如果两位一样,则比较下一位,所以借用它来对英文进行排序。
比如console.log(“abcd”>“abcc”),会返回true。
而console.log(“abc”>“abd”),会返回flase。 - 比较中文没有意义。
- 任何值和NaN做任何比较都是false。
注意:在比较两个字符串型的数字时,一定要转型。
相等运算符
- 当使用==来比较两个值时,如果值的类型不同,则会自动进行类型转换,将其转换为相同的类型,然后再比较。使用!=来比较时,也存在类型转换。
=== (全等)
- 用来判断两个值是否全等,它和相等类似,不同的是它不会做自动的类型转换,如果两个值的类型不同,直接返回false。
!==(不全等)
- 用来判断两个值是否不全等,和不等类似,不同的是它不会做自动的类型转换,如果两个值的类型不同,直接返回true。
其它:
- undefined 衍生自 null,所以这两个值做相等判断时,会返回true。
- NaN不和任何值相等,包括它本身。
- 可以通过 isNaN() 函数来判断一个值是否是NaN,如果该值是NaN则返回true,否则返回false。
条件运算符
条件运算符也叫三元运算符:
- 语法:条件表达式?语句1:语句2;
- 执行的流程:
- 条件运算符在执行时,首先对条件表达式进行求值,如果该值为true,则执行语句1,并返回执行结果。
- 如果该值为false,则执行语句2,并返回执行结果。
- 如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值然后在运算。
隐式类型转换
任何值和字符串相加都会转换为字符串,并做拼串操作。
- 我们可以利用这一特点,来将一个任意的数据类型转换为String。
- 我们只需要为任意的数据类型 + 一个 “” 即可将其转换为String。
- 隐式的类型转换,由浏览器自动完成,实际上它也是调用String()函数。
任何值做- * /运算时都会自动转换为Number。
- 可以通过为一个值 -0 *1 /1来将其转换为Number。
- 原理和Number()函数一样。
如果对非布尔值进行元素,则会将其转换为布尔值,然后再取反。
- 所以我们可以利用该特点,来将一个其他的数据类型转换为布尔值。
- 可以为一个任意数据类型取两次反,来将其转换为布尔值,原理和Boolean()函数一样。
编码
-
在字符串中使用转义字符输入Unicode编码:\u四位编码(编码为16进制)
console.log("\u2620"); -
在网页中使用Unicode编码:&#编码(编码为10进制)
<h1 style="font-size: 200px;">☠</h1> <h1 style="font-size: 200px;">⚀</h1>
(用win10自带计算器转换进制)
11.27
代码块
- 在JS中可以使用{}来为语句进行分组,同一个{}中的语句称作是一组语句。
- 一个{}中的语句我们也称为叫一个代码块。
- JS中的代码块,只具有分组的的作用,没有其他的用途。代码块内容的内容,在外部是完全可见的。
输入
- 使用prompt()函数:
如var money = prompt(“请输入你的财富(万):”)
条件分支语句switch
比C语言多了 default。
num = "hello";
switch(num){
case 1:
console.log("壹");
//使用break可以来退出switch语句
break;
case 2:
console.log("贰");
break;
case 3:
console.log("叁");
break;
default:
console.log("非法数字~~");
break;
}
for循环
练习——九九乘法表
要点:在document.write中加入html标签< span>;注意拼串的用法。
for(var i=1 ; i<=9 ; i++ ){
//创建一个内层循环来控制图形的宽度
for(var j=1 ; j<=i ; j++){
document.write("<span>"+j+"*"+i+"="+i*j+"</span>");
/*注意span和拼串*/
}
//输出一个换行
document.write("<br />");
}
练习——素数(+计时器)
console.time("test");
//console.time("计时器的名字")可以用来开启一个计时器
//它需要一个字符串作为参数,这个字符串将会作为计时器的标识
for(var i=2 ; i<=1000 ; i++){
var flag = true;
for(var j=2 ; j<=Math.sqrt(i) ; j++){
if(i%j == 0){
flag = false;
break;
}
}
if(flag){
console.log(i);
}
}
//终止计时器
//console.timeEnd()用来停止一个计时器,需要一个计时器的名字作为参数
console.timeEnd("test");
备注:Hbuilder中无法查看运行时间,打开Chrome浏览器的控制台,会在最下方输出运行时间(ms)。
对象
如果使用基本数据类型的数据,创建的变量独立,不能成为一个整体。
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。
对象的分类
- 1.内建对象
由ES标准中定义的对象,在任何的ES的实现中都可以使用。
比如:Math String Number Boolean Function Object… - 2.宿主对象
由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象。
比如 BOM DOM(console和document……) - 3.自定义对象
由开发人员自己创建的对象。
创建对象/添加/修改/删除属性
var 对象名 = new Object();
对象名可以随意。
- 在对象中保存的值称为属性。
向对象添加属性:
语法:对象.属性名 = 属性值
var obj = new Object();
//向obj中添加一个name属性
obj.name = "孙悟空";
//向obj中添加一个gender属性
obj.gender = "男";
//向obj中添加一个age属性
obj.age = 18;
- 读取整个对象:console.log(对象名);
在Hbuilder的控制台中看不到对象中的内容,要打开浏览器。 - 读取对象中的属性:
语法:console.log(对象.属性名);
如果读取对象中没有的属性,不会报错而是会返回undefined。
console.log(obj.gender);
console.log(obj.hello);
- 修改对象的属性值:
语法:对象.属性名 = 新值
obj.name = "Tom";
delete obj.name;
- 删除对象的属性:
语法:delete 对象.属性名 ↑
11.28
- 如果要使用特殊的属性名(比如"var"),不能采用.的方式来操作,需要使用另一种方式:语法:对象[“属性名”] = 属性值,读取时也需要采用这种方式。
- 使用[]这种形式去操作属性,更加灵活,在[]中可以直接传递一个变量,这样变量值是多少就会读取那个属性。
obj["var"] = 233;
obj["nihao"] = "你好";
var n = "nihao";
console.log(obj["var"]);
console.log(obj[n]);
属性值
JS对象的属性值,可以是任意的数据类型,甚至也可以是一个对象。
//属性可以是布尔、null、undefined
obj.test = true;
obj.test = null;
obj.test = undefined;
//创建一个对象
var obj2 = new Object();
obj2.name = "猪八戒";
//将obj2设置为obj的属性
obj.test = obj2;
console.log(obj.test.name);
in运算符
通过in运算符可以检查一个对象中是否含有指定的属性,如果有则返回true,没有则返回false。
- 语法:“属性名” in 对象
console.log("name" in obj);
//控制台显示"true"
基本数据类型和引用数据类型
- 基本数据类型:String Number Boolean Null Undefined
- 引用数据类型:Object
- JS中的变量都是保存到栈内存中的,基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量不会影响其他的变量。
b的值复制a的,a再自增。
- 对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响。
由于obj2=obj,obj2的值直接复制obj1的,而obj1的值又是地址,所以obj2和obj1是一个地址,指向堆内存中相同的区域。
但是如果把一个对象的值变为null时,相当于它自己主动断开了连接,并不会影响另一个对象。
- 当比较两个基本数据类型的值时,就是比较值。
- 而比较两个引用数据类型时,它是比较的对象的内存地址,如果两个对象是一模一样的,但是地址不同,它也会返回false。
对象字面量
- 使用对象字面量,可以在创建对象时,直接指定对象中的属性。
(类似数组)
- 语法:{属性名:属性值,属性名:属性值…}
- 对象字面量的属性名可以加引号也可以不加,建议不加,如果要使用一些特殊的名字,则必须加引号。
- 属性名和属性值是一组一组的名值对结构,名和值之间使用:连接,多个名值对之间使用 , 隔开。
var obj = {};
console.log(typeof obj);
obj.name = "孙悟空";
var obj2 = {
name:"猪八戒",
age:13,
gender:"男",
test:{name:"沙僧"}
};
console.log(obj2.test);
枚举对象中的属性
枚举对象中的属性:使用for … in 语句
-
语法:
for(var 变量 in 对象){
} -
for…in语句 对象中有几个属性,循环体就会执行几次。每次执行时,会将对象中的一个属性的名字赋值给变量。
var obj = {
name:"孙悟空",
age:18,
gender:"男",
address:"花果山"
};
for(var n in obj){
console.log("属性名:"+n);
console.log("属性值:"+obj[n]);
}
//属性名为冒号前的部分,属性值为冒号后的部分。
函数
函数 function:
- 函数也是一个对象。
- 函数中可以封装一些功能(代码),在需要时可以执行这些功能(代码)
- 函数中可以保存一些代码在需要的时候调用。
- 使用typeof检查一个函数对象时,会返回function。
11.30
创建函数
- 创建一个函数对象:
可以将要封装的代码以字符串的形式传递给构造函数。 - 调用函数 语法:函数对象()
var fun = new Function("console.log('Hello 这是我的第一个函数');");
fun();//调用
-
封装到函数中的代码不会立即执行,会在函数调用的时候执行。
-
当调用函数时,函数中封装的代码会按照顺序执行。
-
使用函数声明来创建一个函数
- 语法:
function 函数名([形参1,形参2…形参N]){
语句…
}
- 语法:
function sum(a,b){
console.log("a = "+a);
console.log("b = "+b);
console.log(a+b);
}
- 使用 函数表达式 来创建一个函数
- 语法:
var 函数名 = function([形参1,形参2…形参N]){
语句…
}
- 语法:
var fun3 = function(){
console.log("我是匿名函数中封装的代码");
};
fun3();
强调形参
function fun2(){
console.log("这是我的第二个函数~~~");
alert("哈哈哈哈哈");
document.write("~~~~(>_<)~~~~");
console.log(fun2);//可以在控制台输出函数内的所有内容
fun2();//调用函数
}
- 调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查。
- 函数的实参可以是任意的数据类型。
比如上方的sum函数,如果一个参数为数字,一个为字符串,就会输出“NaN”。
- 调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值。如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined。
返回值
可以使用 return 来设置函数的返回值
- 语法:return 值
- return后的值将会会作为函数的执行结果返回,可以定义一个变量,来接收该结果。
- 在函数中return后的语句都不会执行。如果return语句后不跟任何值就相当于返回一个undefined;如果函数中不写return,则也会返回undefined。
- return后可以跟任意类型的值。
- 函数遇到return就结束了。
function sum(a , b , c){
var d = a + b + c;
return d;
//return undefined;
//调用函数
//变量result的值就是函数的执行结果
//函数返回什么result的值就是什么
var result = sum(4,7,8);
console.log("result = "+result);
}
返回值可以是任意的数据类型,可以是一个对象,也可以是一个函数。
- 返回对象:
function fun2(){
//返回一个对象
return {name:"沙和尚"};
//这种写法相当于 var obj={name:"沙和尚"}; return obj;
}
var a = fun2();
console.log("a = "+a);//控制台输出整个对象
console.log(a.name);//控制台输出“沙和尚”
- 返回函数:
function fun3(){
//在函数内部再声明一个函数
function fun4(){
alert("我是fun4");
}
//将fun4函数对象作为返回值返回
return fun4;
}
a = fun3();//此时a是fun4对象
a();//相当于调用fun4函数
//上方两行代码,作用相当于fun3()()
小练习:判断奇偶
function isEven(num){
return num % 2 == 0;
//该式子有一个返回值,是偶数返回true,不是返回false。
//如果不加== 0,可以返回0和1,但不是布尔。
}
var result = isEven(15);
console.log("result = "+result)
将参数封装到对象中
实参可以是任意的数据类型,也可以是一个对象。当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递。
function sayHello(o){
//console.log("o = "+o);
console.log("我是"+o.name+",今年我"+o.age+"岁了,"+"我是一个"+o.gender+"人"+",我住在"+o.address);
}
//创建一个对象
var obj = {
name:"孙悟空",
age:18,
address:"花果山",
gender:"男"
};
sayHello(obj);
函数作实参
实参也可以是一个函数。
function fun(a){
console.log("a = "+a);
}
fun(sayHello);
会把函数sayHello的内容输出到控制台。
开发常用,将匿名函数作为实参传递给函数。
function fun(a){
a(obj);
}
fun(sayHello);
相当于sayHello(obj); 和上方孙悟空的例子是同理的。
调用函数/函数对象:
fun()
- 调用函数,相当于使用的函数的返回值。
(相当于冰淇淋机做的冰淇淋)
fun
- 函数对象,相当于直接使用函数对象。
(相当于冰淇淋机本身)
立即执行函数(IIFE)
全称: Immediately-Invoked Function Expression
- 函数定义完,立即被调用,这种函数叫做立即执行函数。
- 立即执行函数往往只会执行一次
(function(){
alert(“我是一个匿名函数~~~”);
})();
(function(a,b){
console.log("a = "+a);
console.log("b = "+b);
})(123,456);
//控制台输出: a=123 b=456
作用
- 隐藏实现
- 不会污染外部(全局)命名空间
- 用它来编码js模块
对象的属性可以是函数
- 函数也可以称为对象的属性,如果一个函数作为一个对象的属性保存,那么我们称这个函数时这个对象的方法。
- 调用这个函数就说调用对象的方法(method)
- 但是它只是名称上的区别没有其他的区别。
//创建一个对象
var obj = new Object();
//向对象中添加属性
obj.name = "孙悟空";
obj.age = 18;
//对象的属性值可以是任何的数据类型,也可以是个函数
obj.sayName = function(){
console.log(obj.name);
};
function fun(){
console.log(obj.name);
};
//在控制台输出函数对象
console.log(obj.sayName);
//调方法(带括号)
obj.sayName();
//调函数
fun();
作用域
作用域指一个变量的作用的范围。在JS中一共有两种作用域:
全局作用域
- 直接编写在script标签中的JS代码,都在全局作用域。
- 全局作用域在页面打开时创建,在页面关闭时销毁。
- 在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用.
- 在全局作用域中:
- 创建的变量都会作为window对象的属性保存,创建的函数都会作为window对象的方法保存。
- 全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问得到。
<script type="text/javascript">
var c = "hello";
console.log(c);
//相当于console.log(window.c);
//变量作为window对象的属性保存
//console.log作为window对象的方法保存,
//所以也可以是window.console.log(window.c);
function fun(){
console.log("我是fun函数");
}
window.fun();
//相当于直接fun();
//函数作为window对象的方法保存
window.alert("hello");
//相当于直接alert("hello");
声明提前
变量的声明提前
- 使用var关键字声明的变量,会在所有的代码执行之前被声明 (但是不会赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前。
console.log("a = "+a);
var a = 123;
//控制台输出a=undefined,因为a只是被提前声明,但是没有被赋值。
//如果删去下边的var,会报错。
函数的声明提前
- 使用函数声明形式创建的函数 function 函数(){}。它会在所有的代码执行之前就被创建, 所以我们可以在函数声明前来调用函数。
- 使用函数表达式创建的函数 var 函数=function(){},不会被声明提前,所以不能在声明前调用。
fun();
//函数声明,会被提前创建
function fun(){
console.log("我是一个fun函数");
}
//函数表达式,不会被提前创建
var fun2 = function(){
console.log("我是fun2函数");
};
fun2();
函数作用域
- 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁。
- 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。
- 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。在函数中要访问全局变量可以使用window对象。
- 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用。如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError。
var a = 10;
function fun(){
var a = "我是fun函数中的变量a";
var b = 20;
console.log("a = "+a);
//此处a="我是fun函数中的变量a"
function fun2(){
console.log("a = "+window.a);
//此处调用全局变量,a=10
}
fun2();
}
fun();
console.log("b = "+b);
//会报错:ReferenceError
提前声明(补充):
- 在函数作用域也有声明提前的特性。
- 使用var关键字声明的变量,会在函数中所有的代码执行之前被声明。
- 函数声明也会在函数中所有的代码执行之前执行。
function fun3(){
fun4();
//先调用fun4,然后在控制台输出a(undefined)。
console.log(a);
var a = 35;
function fun4(){
alert("I'm fun4");
}
}
fun3();
12.13
Debug in Chrome
- 右键→检查
- Sources
- 左侧,点击数字,小蓝条表示断点
- 添加监视:右键变量/函数,选择Add Selected text to watches(也可以右侧Watch +)
- 点击右侧弯箭头(或F10),一步步执行。
注意刷新来清除浏览器缓存。
Hbuilder的使用
- 如果不慎弄没了控制台和web浏览器,参考:https://jingyan.baidu.com/article/c85b7a64b9667b003aac9549.html
- 如何更新控制台的内容:修改HTML文件,然后ctrl+s
- ctrl+r 可以在浏览器运行该文件。
this
何为this
- 解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象。
- 这个对象称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象。
- 以函数的形式调用时,this永远都是window。
- 以方法的形式调用时,this就是调用方法的那个对象。
- 构造函数的情况(见下文)
由于在全局作用域中:
创建的变量都会作为window对象的属性保存,创建的函数都会作为window对象的方法保存。
所以fun()是全局的方法,在控制台打印的name,也必然是全局的name。
而object.sayName是局部对象的方法,在控制台打印的name,也即为局部的name。
function fun(){
console.log(this.name);
// 是
}
var obj = {
name:"孙悟空",
sayName:fun
// sayName此时和fun是一样的
};
var obj2 = {
name:"沙和尚",
sayName:fun
};
var name = "全局的name属性";
fun();
// 以函数的形式调用,this就是window,在控制台打印“全局的name属性”
obj.sayName();
// 在控制台打印“孙悟空”
obj2.sayName();
// 在控制台打印“沙和尚”
this的好处
仅仅修改调用方式,就可以得到同一个变量的不同的值。
使用工厂方法创建对象
(它是下边构造函数的铺垫,有很多相似的地方)
通过该方法可以大批量地创建对象,优于使用对象字面量:
// 比如创建“造人”的对象:
function createPerson(name , age ,gender){
//创建一个新的对象
var obj = new Object();
//向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function(){
alert(this.name);
};
//将新的对象返回
return obj;
}
var obj2 = createPerson("猪八戒",28,"男");
var obj3 = createPerson("白骨精",16,"女");
var obj4 = createPerson("蜘蛛精",18,"女");
// 这样即直接创建了含有三个属性和一个方法的对象
构造函数
- 构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。
- 构造函数和普通函数的区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
// 创建函数
function Person(name , age , gender){
this.name = name;
// 注意这里是写this.属性名
this.age = age;
this.gender = gender;
this.sayName = function(){
alert(this.name);
// 关于此处的this,请看下一段文字解释——
};
}
function Dog(){
}
// 调用时,构造函数要加“new”
var per = new Person("孙悟空",18,"男");
var per2 = new Person("玉兔精",16,"女");
var per3 = new Person("奔波霸",38,"男");
构造函数的执行流程:
- 立刻创建一个新的对象
- 将新建的对象设置为函数中的this(在构造函数中可以使用this来引用新建的对象)
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
this的情况
- 当以函数的形式调用时,this是window
- 当以方法的形式调用时,谁调用方法this就是谁
- 当以构造函数的形式调用时,this就是新创建的那个对象
instanceof
使用instanceof可以检查一个对象是否是一个类的实例。
- 语法:对象 instanceof 构造函数
- 如果是,则返回true,否则返回false
- 所有的对象都是Object的后代,所以任何对象和Object做instanceof检查时都会返回true
构造函数修改
在Person构造函数中,为每一个对象都添加了一个sayName方法,目前方法是在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法。
执行10000次就会创建10000个新的方法,而10000个方法一模一样,这样没有必要。可以使所有的对象共享同一个方法。
可以在全局定义函数,但是这样有缺陷,会污染全局作用域的命名空间,而且定义在全局作用域中也很不安全。
有好的解决方案,请接着往下看。
原型对象
原型 prototype
- 创建的每一个函数,解析器都会向函数中添加一个属性prototype。这个属性对应着一个对象,这个对象就是我们所谓的原型对象。
- 如果函数作为普通函数调用prototype没有任何作用。
- 当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性。
- 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
- 当访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。
- 以后创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
函数对象,实例,以及原型对象之间的关系:
function MyClass(){
}
// 添加原型对象并不在函数内,而是在花括号外,用.prototype添加
// 原型对象就像一个“公共区域”,但又
//向MyClass的原型中添加属性a
MyClass.prototype.a = 123;
//向MyClass的原型中添加一个方法
MyClass.prototype.sayHello = function(){
alert("hello");
};
var mc = new MyClass();
var mc2 = new MyClass();
// 二者都会在屏幕上弹窗hello
mc.a = "我是mc中的a";
console.log(mc.a);
// 可以在对象自身内找到,在控制台打印“我是mc中的a”
console.log(mc2.a);
// 无法在对象自身内找到,去原型对象中找,找到123
hasOwnProperty
-
使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
-
可以使用对象的hasOwnProperty()来**检查对象自身中是否含有该属性。**使用该方法只有当对象自身中含有属性时,才会返回true
原型链
补充刚才的图:
之前的原型对象,还存在原型对象。
- 原型对象也是对象,所以它也有原型,当我们使用一个对象的属性或方法时,会现在自身中寻找,自身中如果有,则直接使用
- 如果没有则去原型对象中寻找,如果原型对象中有,则使用
- 如果没有则去原型的原型中寻找,直到找到Object对象的原型
- Object对象的原型没有原型(有__proto__属性,对应的值是null),如果在Object原型中依然没有找到,则返回undefined
function MyClass(){
}
var mc = new MyClass();
console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));
//在mc的原型即MyClass中寻找hasOwnProperty方法。由于没有该方法,返回false。
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));
//在mc原型的原型(即Object对象)中寻找hasOwnProperty方法,返回true。
console.log(mc.__proto__.__proto__.__proto__);
//由于Object对象的原型是null,如果此时使用hasOwnProperty方法,会报错。
toString 补充
直接在页面中打印一个对象时,实际上是输出的对象的toString()方法的返回值。如果希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法。
[object Object]是object对象里的toString方法,现在需要给对象的原型对象添加该方法,便可覆盖Object对象里的方法。
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("孙悟空",18,"男");
var per2 = new Person("猪八戒",28,"男");
console.log(per2);
console.log(per);
Hbuilder控制台内运行结果:
12.16
垃圾回收(GC)
程序运行过程中会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
- 当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,必须进行清理。
- 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,不需要也不能进行垃圾回收的操作。
- 需要做的只是要将不再使用的对象设置null即可。
数组(Array)
- 数组也是一个对象,它和普通对象功能类似,也是用来存储一些值的。
- 不同的是普通对象是使用字符串作为属性名。而数组时使用数字来作为索引(从0开始的整数)操作元素。
- 数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据。
创建数组对象、添加/读取元素
创建数组对象:
- 语法:var 数组名 = new Array();
向数组中添加元素: - 语法:数组[索引] = 值
读取数组中的元素:
- 语法:console.log(数组[索引]) (可以不用console.log,纯粹为了演示)
- 如果读取不存在的索引,不会报错而是返回undefined
读取整个数组:
- 语法:console.log(数组名)
length属性
获取数组的长度(元素的个数)
- 语法:数组.length
- 对于连续的数组,使用length可以获取到数组的长度(可借助console.log)
- 对于非连续的数组,使用length会获取到数组的最大的索引+1,尽量不要创建非连续的数组
修改数组的长度
- 语法:数组名.length = 某个数
例如: arr.length = 10 - 如果修改的length大于原长度,则多出部分会空出来
- 如果修改的length小于原长度,则多出的元素会被删除
向数组的最后一个位置添加元素
- 语法:数组[数组.length] = 值
例如:
arr[arr.length] = 70;
arr[arr.length] = 80;
arr[arr.length] = 90;
数组字面量
使用字面量来创建数组:
- 语法:[ ]
var arr = [ ]; - 使用字面量创建数组时,可以在创建时就指定数组中的元素
var arr = [1,2,3,4,5,10]; - 使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作文构造函数的参数传递
var arr2 = new Array(10,20,30);
但是使用字面量和构造函数创建数组时有区别:
//创建一个数组数组中只有一个元素10
arr = [10];
//创建一个长度为10的数组
arr2 = new Array(10);
数组中的元素可以是任意的数据类型:
-
arr = [“hello”,1,true,null,undefined];
-
可以是对象
var obj = {name:“孙悟空”};
arr[arr.length] = obj;
arr = [{name:“孙悟空”},{name:“沙和尚”},{name:“猪八戒”}]; -
可以是一个函数(函数也是对象)
arr = [function(){alert(1)},function(){alert(2)}]; -
数组中也可以放数组,如下这种数组称为二维数组
arr = [[1,2,3],[3,4,5],[5,6,7]];
console.log(arr[1]);
常用数组方法
push()
该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
- 可以将要添加的元素作为方法的参数传递,这样这些元素将会自动添加到数组的末尾
- 该方法会将数组新的长度作为返回值返回
var arr = ["孙悟空","猪八戒","沙和尚"];
var result = arr.push("唐僧","蜘蛛精","白骨精","玉兔精");
console.log(arr);
// "孙悟空,猪八戒,沙和尚,唐僧,蜘蛛精,白骨精,玉兔精"
console.log("result = "+result);
// result = 7
其它
- pop():该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
- unshift():向数组开头添加一个或多个元素,并返回新的数组长度
向前边插入元素以后,其他的元素索引会依次调整 - shift():可以删除数组的第一个元素,并将被删除的元素作为返回值返回
12.17
slice和splice(推荐)
slice()可以用来从数组提取指定元素
-
该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回
-
参数:
1.截取开始的位置的索引,包含开始索引
2.截取结束的位置的索引,不包含结束索引 -
第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
-
索引可以传递一个负值,如果传递一个负值,则从后往前计算
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
var result = arr.slice(1,4);
// 此时result == "猪八戒","沙和尚","唐僧"
result = arr.slice(3);
// result == "唐僧","白骨精"
result = arr.slice(1,-2);
// result == "猪八戒","沙和尚"
splice 删除指定元素/替换/增加 一网打尽
使用splice()会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
- 参数:
第一个,表示开始位置的索引
第二个,表示删除的数量
第三个及以后,可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
var result = arr.splice(3,0,"牛魔王","铁扇公主","红孩儿");
// result == "孙悟空","牛魔王","铁扇公主","红孩儿","猪八戒","沙和尚","唐僧","白骨精"
// 如果删除的元素数量等于新增的元素数量,可以看作发挥了替换功能
// 如果删除元素数量为0,可以看作发挥了增加元素的功能
// 我认为splice是一个万能的方法
console.log(arr);
concat / join / reverse / sort
concat()可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响
var arr = ["孙悟空","猪八戒","沙和尚"];
var arr2 = ["白骨精","玉兔精","蜘蛛精"];
var arr3 = ["二郎神","太上老君","玉皇大帝"];
var result = arr.concat(arr2,arr3,"牛魔王","铁扇公主");
console.log(result);
//"孙悟空,猪八戒,沙和尚,白骨精,玉兔精,蜘蛛精,二郎神,太上老君,玉皇大帝,牛魔王,铁扇公主"
join()可以将数组转换为一个字符串
- 该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回
- 在( )中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用,作为连接符
arr = ["孙悟空","猪八戒","沙和尚","唐僧"];
result = arr.join("@-@");
console.log(result);
// "孙悟空@-@猪八戒@-@沙和尚@-@唐僧"
reverse()方法用来反转数组
该方法会直接修改原数组
sort()可以用来对数组中的元素进行排序
该方法会影响原数组,默认会按照Unicode编码进行排序
- 即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序,所以对数字进排序时,可能会得到错误的结果
可以自己来指定排序的规则
- 可以在sort()添加一个回调函数,来指定排序规则,浏览器会根据回调函数的返回值来决定元素的顺序,如果返回一个大于0的值,则元素会交换位置,返回一个小于等于0的值,则元素位置不变
- 如果需要升序排列,则返回 a-b,如果需要降序排列,则返回b-a
arr = [5,4,2,19,3,11,6,8,7];
arr.sort(function(a,b){
// 降序排列
return b - a;
});
// 注意回调函数写在sort的括号内,所以最后是}),不要漏掉圆括号
// 排序结果为"19,11,8,7,6,5,4,3,2"
12.16
遍历数组
for循环即可,同C:
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
for(var i=0 ; i<arr.length ; i++){
console.log(arr[i]);
}
提取元素练习
将perArr中的满18岁的Person提取出来,然后封装到一个新的数组中并返回。
function Person(name , age , gender){
this.name = name;
this.age = age;
}
//修改Person原型的toString
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+"]";
};
//创建一个Person对象
var per = new Person("孙悟空",18);
var per2 = new Person("猪八戒",28);
var per3 = new Person("红孩儿",8);
var per4 = new Person("蜘蛛精",16);
var per5 = new Person("二郎神",38);
/*
* 将这些person对象放入到一个数组中
*/
var perArr = [per,per2,per3,per4,per5];
/*
* 创建一个函数,可以将perArr中的满18岁的Person提取出来,
* 然后封装到一个新的数组中并返回
* arr
* 形参,要提取信息的数组
*/
function getAdult(arr){
//创建一个新的数组
var newArr = [];
//遍历arr,获取arr中Person对象
for(var i=0 ; i<arr.length ; i++){
var p = arr[i];
//判断Person对象的age是否大于等于18
if(p.age >= 18){
//如果大于等于18,则将这个对象添加到newArr中
//将对象放入到新数组中
newArr.push(p);
}
}
//将新的数组返回
return newArr;
}
var result = getAdult(perArr);
console.log(result);
12.17
foreach
- JS中还提供了一个方法,用来遍历数组,即forEach()
- 这个方法只支持IE8以上的浏览器E8及以下的浏览器均不支持该方法,所以如果需要兼容IE8,则不要使用forEach,还是使用for循环来遍历。
forEach()方法需要一个函数作为参数
- 像这种函数,由我们创建但是不由我们调用的,称为回调函数
- 数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容
浏览器会在回调函数中传递三个参数:
- 第一个参数,是当前正在遍历的元素
- 第二个参数,是当前正在遍历的元素的索引
- 第三个参数,是正在遍历的数组
var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
arr.forEach(function(value , index , obj){
console.log(value);
/*将数组内所有元素输出在控制台,每个元素占据一行*/
});
数组去重练习
j自减这一步非常重要,而且需要写在if内部,否则会陷入死循环。
//创建一个数组
var arr = [1,2,3,2,2,1,3,4,2,5];
//去除数组中重复的数字
//获取数组中的每一个元素
for(var i=0 ; i<arr.length ; i++){
//console.log(arr[i]);
/*获取当前元素后的所有元素*/
for(var j=i+1 ; j<arr.length ; j++){
//console.log("---->"+arr[j]);
//判断两个元素的值是否相等
if(arr[i] == arr[j]){
//如果相等则证明出现了重复的元素,则删除j对应的元素
arr.splice(j,1);
//当删除了当前j所在的元素以后,后边的元素会自动补位
//此时将不会再比较这个元素,需要在比较一次j所在位置的元素
//使j自减
j--;
}
}
}
console.log(arr);
函数的方法call&apply(this补充)
call()和apply()
- 这两个方法都是函数对象的方法,需要通过函数对象来调用
- 当对函数调用call()和apply()都会调用函数执行
- 在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this
- call()方法可以将实参在对象之后依次传递
- apply()方法需要将实参封装到一个数组中统一传递
this的情况:
- 1.以函数形式调用时,this永远都是window
- 2.以方法的形式调用时,this是调用方法的对象
- 3.以构造函数的形式调用时,this是新创建的那个对象
- 4.使用call和apply调用时,this是指定的那个对象
function fun(a,b) {
console.log("a = "+a);
console.log("b = "+b);
console.log(this.name);
}
var obj = {
name: "obj",
sayName:function(){
alert(this.name);
}
};
var obj2 = {
name: "obj2"
};
fun.call(obj,2,3);
//相当于fun.apply(obj,[2,3]);
//传入obj对象,因此此时this为obj,this.name=obj
//此时在控制台输出:a=2,b=3,obj
fun.call(obj2,2,3);
//此时this为obj2,在控制台输出:a=2,b=3,obj2
12.18
arguments
在调用函数时,浏览器每次都会传递进两个隐含的参数:
- 函数的上下文对象 this
- 封装实参的对象 arguments
arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
- 在调用函数时,传递的实参都会在arguments中保存
- arguments.length可以用来获取实参的数量
- 即使不定义形参,也可以通过arguments来使用实参,但比较麻烦
- arguments[0] 表示第一个实参,arguments[1] 表示第二个实参 …
- 它有一个属性叫做callee,这个属性对应当前正在执行的函数的对象
function fun(a,b){
console.log(arguments instanceof Array);
//false,因为arguments不是数组
console.log(Array.isArray(arguments));
//false,同上
console.log(arguments[1]);
//"hello"
console.log(arguments.length);
//2
console.log(arguments.callee == fun);
//true,arguments.callee就是当前函数的对象
}
fun("hello",true);
Date对象
创建
创建一个Date对象
-
如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间
var d = new Date(); -
创建一个指定的时间对象,需要在构造函数中传递一个表示时间的字符串作为参数
日期的格式 月份/日/年 时:分:秒
var d2 = new Date(“2/18/2011 11:10:30”);
方法
- getDate() 获取当前日期对象是几日
- getDay() 获取当前日期对象时周几,会返回一个0-6的值,0 表示周日,1表示周一
- getMonth() 会返回一个0-11的值,0 表示1月,11 表示12月
- getFullYear() 获取当前日期对象的年份
- getTime() 获取当前日期对象的时间戳
时间戳,指的是从格林威治标准时间的1970年1月1日,0时0分0秒,到当前日期所花费的毫秒数(1秒 = 1000毫秒)计算机底层在保存时间时使用都是时间戳它和时区有关,如果在中国的电脑上,默认为东八区。
利用时间戳测试代码执行性能
var start = Date.now();
for(var i=0 ; i<100 ; i++){
console.log(i);
}
var end = Date.now();
console.log("执行了:"+(end - start)+"毫秒");
Math
Math和其他的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法
它的属性和方法都可以在JS手册中查找,此处列出一些常用方法:
- abs()可以用来计算一个数的绝对值
//console.log(Math.abs(-1)); - Math.ceil() 可以对一个数进行向上取整,小数位只有有值就自动进1
- Math.floor() 可以对一个数进行向下取整,小数部分会被舍掉
- Math.round() 可以对一个数进行四舍五入取整
- max() 可以获取多个数中的最大值
- min() 可以获取多个数中的最小值
- Math.pow(x,y) 返回x的y次幂
- Math.sqrt() 用于对一个数进行开方运算
任意范围的随机数
Math.random() 可以用来生成一个0-1之间的随机数
生成一个x-y之间的随机数
- Math.ceil(Math.random()*(y-x)+x-1)
- 或 Math.floor(Math.random()*(y-x+1)+x)
根据b站弹幕所言,使用round会影响概率,不能使用。
包装类
JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
- String() 可以将基本数据类型字符串转换为String对象
- Number() 可以将基本数据类型的数字转换为Number对象
- Boolean() 可以将基本数据类型的布尔值转换为Boolean对象
- 但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果
方法和属性之能添加给对象,不能添加给基本数据类型,对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法,调用完以后,再将其转换为基本数据类型
var s = 123;
//s为基本数据类型
s = s.toString();
//s是不能添加toString方法的,故浏览器先将它临时转换为对象
s.hello = "你好";
//s是不能添加hello属性的,故浏览器先将它临时转换为对象,之后又立刻删除该对象
console.log(s.hello);
//之前的对象已被删除,现在浏览器再次将s转换为对象,此时a属性未定义,所以控制台打印"undefined"
字符串的相关方法
由于字符串都是以数组的形式存储的,每个字符为一个元素,所以这些方法会和之前数组的方法有高度重复性。
注:此处的方法都是不改变原字符串的,也就是都需要var result…然后打印result,并不在括号内写原字符串,括号内可以为空,有时写一些参数。
-
length属性(略)
-
charAt( ):可以根据索引返回字符串中指定位置的字符
str = "嘿Hello Atguigu";
var result = str.charAt(3);
console.log(result);
//结果为l
console.log(str[3]);
//结果为l
-
charCodeAt( ):获取指定位置字符的字符编码(Unicode编码)
result = str.charCodeAt(0); -
String.formCharCode( ):可以根据字符编码去获取字符
result = String.fromCharCode(0x2692); -
concat( ):可以用来连接两个或多个字符串,作用和+一样
result = str.concat(“你好”,“再见”);
结果为:嘿Hello Atguigu你好再见 -
indexof( ) 该方法可以检索一个字符串中是否含有指定内容,如果字符串中含有该内容,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回-1。可以指定第二个参数,指定开始查找的位置。
-
lastIndexOf():该方法的用法和indexOf()一样,但它从前往后找,而lastIndexOf是从后往前找,它也可以指定开始查找的位置。
str = "hello hatguigu";
result = str.indexOf("h",1);
//从索引1开始往后找,结果为6
result = str.lastIndexOf("h",7);
//从索引7开始往前找,结果为6
-
slice( ):可以从字符串中截取指定的内容,不会影响原字符串,而是将截取到内容返回
参数:- 第一个,开始位置的索引(包括开始位置)
- 第二个,结束位置的索引(不包括结束位置)
- 如果省略第二个参数,则会截取到后边所有的,也可以传递一个负数作为参数,负数的话将会从后边计算。
-
substring( ):可以用来截取一个字符串,可以slice()类似(目测用处不大)
参数:- 第一个:开始截取位置的索引(包括开始位置)
- 第二个:结束位置的索引(不包括结束位置)
- 不同的是这个方法不能接受负值作为参数,如果传递了一个负值,则默认使用0,而且它还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
-
substr( ):用来截取字符串(好处是可以输入截取长度,并且不像之前的splice一样影响原来的)
参数:- 第一个:截取开始位置的索引
- 第二个:截取的长度
-
split( ):可以将一个字符串拆分为一个数组
参数:需要一个字符串作为参数,将会根据该字符串去拆分数组,如果传递一个空串作为参数,则会将每个字符都拆分为数组中的一个元素
str = "abcbcdefghij";
result = str.split("d");
console.log(result);
//"abcbc,efghij"
result = str.split("");
console.log(result);
//"a,b,c,b,c,d,e,f,g,h,i,j"
- toUpperCase( ):将一个字符串转换为大写并返回
- toLowerCase( ):将一个字符串转换为小写并返回
正则表达式
正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,或者将字符串中符合规则的内容提取出来。
构造函数创建正则表达式的对象
- 语法:var 变量 = new RegExp(“正则表达式”,“匹配模式”);
- 在构造函数中可以传递一个匹配模式作为第二个参数,可以是
- i 忽略大小写
- g 全局匹配模式
- 使用typeof检查正则对象,会返回object
- 正则表达式var reg = new RegExp(“a”); 可以来检查一个字符串中是否含有a
test()
使用这个方法可以用来检查一个字符串是否符合正则表达式的规则,如果符合则返回true,否则返回false。
var reg = new RegExp("ab","i");
console.log(reg.test("Ac"));
//检测字符串"Ac"中有没有"ab",且忽略大小写,因为没有,打印false
12.19
使用字面量创建正则表达式
语法:var 变量 = /正则表达式/匹配模式,这种方式创建更加简单,但是如果要动态创建正则表达式,必须要用构造函数创建(p137的20分钟处)
例如:var reg = /a/i;
逻辑运算符在这里可以使用, | 表示或,[^ ] 非
创建一个正则表达式,检查一个字符串中是否有a或b:reg = /a|b|c/;
[ ]里的内容也是或的关系:
- [ab] == a|b
- [a-z] 任意小写字母
- [A-Z] 任意大写字母
- [A-z] 任意字母
- [0-9] 任意数字
检查一个字符串中是否含有 abc 或 adc 或 aec:
reg = /a[bde]c/;
字符串和正则表达式相关的方法
split()
split()可以将一个字符串拆分为一个数组
- 可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串
- 这个方法即使不指定全局匹配,也会全都插分
var str = "1a2b3c4d5e6f7";
var result = str.split(/[A-z]/);
/*根据任意字母来将字符串拆分*/
console.log(result);
search
search( )可以搜索字符串中是否含有指定内容
- 如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1
- 它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串
- search( )只会查找第一个,即使设置全局匹配也没用
str = "hello abc hello aec afc";
result = str.search(/a[bef]c/);
/*搜索字符串中是否含有abc 或 aec 或 afc*/
console.log(result);
/*6*/
match()
match( )可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
- 默认情况下match只会找到第一个符合要求的内容,找到以后就停止检索
- 可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
- 可以为一个正则表达式设置多个匹配模式,且顺序无所谓
- match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果
str = "1a2a3a4a5e6f7A8B9C";
result = str.match(/[a-z]/ig);
/*提取出所有字母,存在数组内,虽然前面写的是小写,但是匹配模式中有i忽略大小写
如果不加g,则只提取一个a*/
console.log(result);
/* "a,a,a,a,e,f,A,B,C" */
replace()
replace( )可以将字符串中指定内容替换为新的内容
参数:
- 被替换的内容,可以接受一个正则表达式作为参数
- 新的内容
(默认只会替换第一个)
str = "1a2a3a4a5e6f7A8B9C";
result = str.replace(/[a-z]/gi , "");
console.log(result);
/* "123456789" */
量词
设置出现次数
通过量词可以设置一个内容出现的次数,量词只对它前边的一个内容起作用(注意加括号)
- {n} 出现n次而且必须得挨着
- {m,n} 出现m-n次
- {m,} m次以上
- + 至少一个,相当于{1,}
- * 0个或1个或多个,相当于{0,},有没有都行
- ? 0个或1个,相当于{0,1}
备注:只要一串数/字符中有完整的一段符合正则表达式,就可以为true。
比如 var reg = /a{3}/,如果是aaaa,前面三个a检查后,即便再来第四个a,仍然为true。
再比如 reg = /ab{1,3}c/,如果是ccccababcc,可以断开看cccc ababc c,中间有一段符合,所以为true。
var reg = /a{3}/;
// 出现3次a,并且三个a必须挨着才是true
reg = /(ab){3}/;
// 出现3次以上的ab,并且三个ab必须挨着才是true
reg = /b{3}/;
reg = /ab{1,3}c/;
// 必须是前边是a,紧挨着出现1~3次b,后面连着c
reg = /ab{3,}c/;
// a紧挨着出现三次以上b连着c
reg = /ab+c/;
//a紧挨着出现至少一个b再连着c
reg = /ab*c/;
//a后有或无b再连着c
reg = /ab?c/;
//a后有0或1个b再连着c
设置出现位置
- ^ 表示开头
- $ 表示结尾
- 如果在正则表达式中同时使用^ $则要求字符串必须完全符合正则表达式
reg = /^a/; //匹配开头的a
reg = /a$/; //匹配结尾的a
reg = /^a$/;//字符串要求只有一个a
console.log(reg.test("aa"));//false
检查手机号是否合法
规则:
- 以1开头
- 第二位3-9任意数字
- 三位以后任意数字9个
var phoneStr = "13067890123";
var phoneReg = /^1[3-9][0-9]{9}$/;
console.log(phoneReg.test(phoneStr)); //true
prompt接收输入
var str = prompt(“请输入你的用户名:”);
转义字符/其他字符
在正则表达式中使用\作为转义字符
. 表示任意字符
用. 来表示.
用\ 表示\
注意:使用构造函数时,由于它的参数是一个字符串,而\是字符串中转义字符,如果要使用\则需要使用\来代替
// 注意构造函数和字面量创建的区别:
var reg = /\./;
reg = /\\/;
reg = new RegExp("\\.");
reg = new RegExp("\\\\");
其它字符:
- \w 任意字母、数字、_
- \W 除了字母、数字、_ (大写字母表示非 小写字母)
- \d 任意的数字 [0-9]
- \D 除了数字 [^0-9]
- \s 空格
- \S 除了空格
- \b 单词边界
- \B 除了单词边界
/*创建一个正则表达式检查一个字符串中是否含有单词child*/
reg = /\bchild\b/;
/*表示child必须是独立的单词,两侧不能连着其它字母或数字*/
console.log(reg.test("hello child ")); //true
删除多余空格
有时候用户在输入信息时会不小心在前面或后面多输入空格,我们去掉前后的空格,保留中间的空格,去除空格就是使用""来替换空格
var str = " he llo ";
//去除开头的空格
//str = str.replace(/^\s*/, "");
//去除结尾的空格
//str = str.replace(/\s*$/, "");
// /^\s*|\s*$/g 匹配开头和结尾的空格,必须在最后加全局匹配g
str = str.replace(/^\s*|\s*$/g,"");
12.20
邮件的正则
任意字母数字下划线 .任意字母数字下划线(可有可无) @ 任意字母数字 .任意字母(2-5位) .任意字母(2-5位,可有可无)
先分段写出:
\w{3,} (.\w+)* @ [A-z0-9]+ (.[A-z]{2,5}){1,2}
再删除空格
注意中间[A-z0-9]表示任意字母和数字,最后的{1,2}表示出现一次到两次。因为最后两段的结构相同,而最后一段可有可无,故简写成这种形式。
var emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;
var email = "abc.hello@163.com";
console.log(emailReg.test(email));