01-06js基础
JS-JSbasejs基础第一部分:变量、数据类型、对象、函数、数组、字符串以及作用域
写在前面:本篇是对之前我的txt版笔记的总结汇总,基于b站课程黑马程序员前端JavaScript入门到精通(例子中使用const
和let
的部分)以及尚硅谷JavaScript基础&实战(例子中使用var
的部分)
基础知识
vscode中创建HTML文件后,先输入一个!
,之后按tab即可生成标准格式
js注释:
-
//
ctrl+/ -
/* */
shift+alt+a
html注释:<!-- -->
ctrl+?
css注释:/* */
alt+b在默认浏览器中执行HTML文件
js代码的编写位置:
-
写在HTML控件中(不推荐),如
<button onclick="alert('点击了按钮')">一个按钮</button> <a href="javascript:alert('点击了超链接');">一个超链接</a> <a href="javascript:;">一个点击后没有任何效果的超链接</a>
-
HTML文件的
<script>
标签,其中放js代码,如<script>alert("xxx")</script>
-
在HTML文件中引用已编写的js文件,如
<script src="01.js"></script>
js中严格区分大小写,每一条语句以分号结尾,分号可省略(不写分号时浏览器也会自动添加)
变量与数据类型
声明变量
使用var
/let
/const
声明变量,如var a;
区别:
-
var可以先使用再声明(未声明时值为undefined)
-
let必须先声明再使用,且不允许重复声明(将同一变量声明多次)
-
const同let,区别是声明的变量无法更改(同c中的const);且必须在声明时初始化,不能以后再赋值
注意:对于数组和对象这种变量保存内存地址的类型,const只保证变量名指向的地址不变,并不保证该地址的数据不变
标识符规则:可含有字母、数字、下划线和$,不能有其它符号,不能以数字开头,不能是js中的关键字/保留字。js底层保存标识符时采用Unicode编码,所以理论上utf-8中含有内容都可作为标识符(包括中文,但不建议使用)
数据类型
使用typeof a
来检查变量a
的类型
string
用单引号或双引号,一对引号括起来的内容里面不能出现一对同类型引号(引号不能嵌套),可以外双内单或外单内双
转义字符为\
\n
表示换行,\t
表示tab制表符,\\
表示\
,\"
表示"
,…
使用Unicode编码输出字符:4位16进制编码前加\u
,如"\u0031"
可以输出0031对应的字符;HTML中在10进制编码前加&#
后加分号,如<h1>☠</h1>
number
包括整数和浮点数
数字的最大值:Number.MAX_VALUE
,若number超过这个值,则返回infinity
正无穷;最小值就是-Number.MAX_VALUE
,同理还有-infinity
;最小的正小数是Number.MIN_VALUE
正负无穷都是number类型
NaN类型:即not a number的缩写,如若a = “abc" * "abc"
,则a的值就是NaN,它也是number类型
js的小数运算可能不准确
16进制数字以0x开头,如0x10=16 0xff=255
;8进制数字以0开头,如070=56
; 2进制数字以0b开头(不是所有浏览器都支持)
Boolean
即true和false
Null
只有一个值null
,表示一个为空的对象 typeof null
返回object
Undefined
只有一个值undefined
,表示未定义
当声明一个变量但不给其赋值时,它的值就是undefined
强制类型转换
转为string
-
调用toString方法,该方法不会改变原变量,而是将改变后的值返回。null和undefined不能使用这种方法转换
var a = 123; var a_str = a.toString();
-
调用
String()
函数,该方法可以将null和undefined转成”null”和”undefined”var a_str = String(a);
转为number
-
调用
Number()
函数var a = "123"; var a_num = Number(a);
字符串->数值:
-
纯数字的字符串->数字;
-
有非数字内容的字符串->NaN;
-
空字符串/只有空格的字符串->0
-
-
parse系列函数:专门用于字符串
-
parseInt()
:将一个字符串中有效的整数内容取出(从前往后读取字符串,将数字取出,直至读到不是数字的字符)var a = "123px"; var a_num = parseInt(a); //a_num为123
“1b23px”->1
“b123px”->NaN
-
parseFloat()
:获得小数,规则同parseInt
“123.456px”->123.456
“123.4.56px”->123.4
-
用指定的进制将字符串转为数字:
var a = "070"; var a_10 = parseInt(a,10); //70 var a_8 = parseInt(a,8); //56
-
注意:若对非字符串类型使用parse系列函数,会先将其转换为字符串,再执行parse操作。用此特性可以对小数进行取整:parseInt(123.456)
->123
转为Boolean
使用Boolean()
函数
数字->bool:除了0和NaN其它都转成true
字符串->bool:除了空字符串都是true
null->false
undefined->false
object->true
运算符
字符串的+
string+string/其它类型值
:会先将其它类型值转换为字符串(使用String()
函数),之后再拼接
一元运算符
-
+
不会对数字产生影响,-
取相反数(都是转成number取反,因此可以用a = +a
将a变为number) -
自增自减:
a++;a--;
,前置和后置自增的区别同c(表达式结果不同:a++
为a++a
为a+1) -
!非:先转为Boolean再运算(因此可以用
a = !!a
将a转为Boolean) -
&&和
||
:如果第一个值为false/true就不会算第二个值(同c)。当其两边的值有非Boolean值的时候,会先将其转为Boolean再运算,并返回原值返回值的判断:返回的是检查的最后一个值–
1&&2
判定为true,要检查1和2,所以返回2;0&&NaN
判定为false,只需检查0,所以返回0,同理1&&2
->2、0&&NaN
->0、1||0
->1、0&&2
->0 -
> < >= <=
在非数值性的比较中,会先转化成数值型再比较;任何值和NaN作任何比较结果都是false特殊情况:如果符号两侧的值都是字符串,则不会将其转换成数值比较,而会比较字符的编码(一位一位的进行比较,即先比两个string的第一个字符,如果它们相同再比较下一位)
-
==和!=:当两个值类型不同,会转换成相同类型再比较是否相等(大部分情况下都转为数字)
特殊情况:
null==0
->falseundefined==null
->true(undefined衍生自null)NaN不和任何值相等(包括NaN),可以通过
isNaN(a)
函数判断一个值a是否为NaN(该函数会先将a转成数值类型,再判断其是否为NaN,因此isNaN("abc")
->true) -
===和!==:不会自动进行类型转换,当类型不同时就返回false/true,如
undefined===null
->false
三元运算符
条件表达式?语句1:语句2
:若条件表达式为true执行语句1,反之执行语句2。如果条件表达式的值是非bool值,会先转换成bool值再判断
求最大值:
var max_2 = a > b ? a : b;
var max_3 = a > b ? (a > c ? a : c) : (b > c ? b : c);
基础语段和常用函数
基础语段
if (条件) {语句}
else if (条件) {语句}
else if (条件) {语句}
else {语句}
switch (a) {
case 1: 语句1 break;
case 2: 语句2 break;
default: 语句
}
while (条件) {
循环体
}
do {
循环体
} while (条件)
for (var i = 0; i < 10; i++) {
循环体
}
为循环语句创建label标识当前循环:
outer: //标识下面的外层循环为outer
for(var i=0;i<10;i++)
{
inner: //标识下面的内层循环为inner
for(var j=0;j<10;j++)
{
break outer;//终止外层循环
continue outer;//跳过此次外层循环
}
}
常用函数
-
alert("弹出警告窗")
注意:js中代码顺序执行,alert弹出警告窗时,需点击“确定”按钮后才算执行完这条语句,并接着执行之后的代码
-
document.write("在页面上显示")
,如document.write("<br/>");
换行 -
console.log("在控制台中输出")
,在页面中按f12进入控制台 -
prompt("xxx");
可以弹出一个提示框,该提示框中会带有一个文本框,用户可以在文本框中输入一段内容,传入的参数”xxx”为提示框的提示文字,该函数返回输入的值(以string形式),如var input = prompt("请输入:");
使用
var input_number = + prompt("请输入:");
可直接得到数值型输入当有多个
prompt()
时会顺序执行(输入完一个后再弹出下一个提示框) -
parseInt(a)
不仅可以字符串转数值,还可以将double转为int,如parseInt(a/100);
获得a的百位数 -
Math.sqrt(a)
对a开方 -
计算某段代码运行时间:
console.time("test") /*要计时的代码*/ console.timeEnd("test");//在浏览器控制台中可以看到运行时间
对象
类似于python中的字典
创建
-
var obj = new Object();
-
var obj = {};
向对象中添加属性:
-
obj.name = "abc";
键为name,值为”abc” -
obj["name"] = "abc";
属性名不强制要求遵守标识符规范,但如果使用特殊属性名,需用这种方式创建 -
var obj = {name:"abc", age:20};
在创建对象的同时指定对象的属性(常用),键名可以加引号也可以不加,但使用特殊变量名时必须加引号
删除对象属性:delete obj.name;
注意:对象中的值可以是基本数据类型,也可以是一个对象/函数等
获取对象值
-
obj.键名
var obj = {test:{name:"abc"}}; //对象中的值也可以是一个对象 console.log(obj.test.name); //"abc"
-
obj[键名]
(更加灵活)var a = "name"; console.log(obj[a]); //等效于console.log(obj.name) console.log(obj.a); //是错误的
遍历对象中的属性:
for (var n in obj) {
console.log("属性名:" + n + " 属性值" + obj[n]);
}
引用数据类型
js中变量都保存到栈内存中
基本数据类型(string number boolean null undefined):他们的变量都是在栈内存内直接存储,变量之间全部独立,修改其中一个变量对其它变量无影响
引用数据类型(object):对象保存到堆内存中,每创建一个新对象,就在堆中开辟出一个新空间,object类型的变量只在栈中保存相应对象的内存地址(对象的引用),若两个变量都指向一个对象,改变其中一个的属性值,会改变另一个的属性值
var obj1 = new Object();
obj1.name = "abc";
var obj2 = obj1;
obj2.name = "ABC"; //此时obj1.name也变为"ABC",相当于改变obj2指针指向的值
obj2 = null; //相当于指针置空,不影响obj1的值
当比较两个对象(obj1 == obj
)时,比较的时它们的内存地址
类与对象
构造函数:就是一个普通的函数,创建方式与普通函数无差别,习惯上首字母大写;使用new关键字来调用
function Person(name, age) {
this.name = name;
this.age = age;
}
执行流程:
-
new立即创建一个新对象
-
将新建的对象设为构造函数中的this
-
执行构造函数中代码
-
将新建对象this返回
var obj = new Person("abc", 20);
console.log(obj); //Person {name: 'abc', age: 20}
使用同一个构造函数创建的对象称为一类对象,构造函数称为一个类;通过类构造函数创建的对象称为该类的实例。
如上面的Person就是一个类,obj就是它的实例
检查一个对象是否为一个类的实例:对象 instanceof 构造函数名
是则返回true,反之为false
所有对象都是object子类,任何对象 instanceof Object
都返回true
Date对象
用于表示时间
如果直接使用构造函数创建Date对象,则会封装为当前代码执行时间
要创建一个指定的时间对象,就需要在构造函数中传递一个表示时间的字符串为参数,格式:"月/日/年 时:分:秒"
注意,使用中文系统,这样写就是中国标准时间
var date1 = new Date();
console.log(date1); //Mon Jan 29 2024 14:49:45 GMT+0800 (中国标准时间)
var date2 = new Date("12/13/2011 11:12:13");
console.log(date2); //Tue Dec 13 2011 11:12:13 GMT+0800 (中国标准时间)
Date对象的方法:
-
getDate()
几号 -
getDay()
星期几(0表示周日,1表示周一,…) -
getMonth()
月份(0表示一月,1表示2月,…) -
getFullYear()
年份(4位数形式) -
getHours()
小时 -
getMinutes()
分钟 -
getSeconds()
秒数 -
getMilliseconds()
毫秒数 -
toLocaleString()
以"2022/4/1 09:08:07"
的形式得到表示日期和时间的字符串 -
toLocaleDateString()
以"2022/4/1"
的形式得到表示日期的字符串 -
toLocaleTimeString()
以"09:08:07"
的形式得到表示时间的字符串 -
getTime()
/+new Date()
获取日期对象的时间戳(从格林威治时间的1970年1月1日0时0分0秒到现日期经过的毫秒数),底层保存时间都是使用的时间戳 -
now();
获取该行代码执行时的时间戳
获取指定代码的执行时间:
var start_time = Date.now();
/*要测量的代码*/
var end_time = Date.now();
console.log(end_time-start_time);
Math类
和其它的对象不同,它没有构造函数,属于一个工具类,不需创建对象,封装了数学运算相关的属性方法
-
Math.PI
圆周率 -
Math.E
e -
Math.LN2
ln2 -
Math.LN10
ln10 还有很多 -
Math.abs(a)
a的绝对值
还有cos sin tan acos asin atan log求对数 exp求e的x次幂 sqrt开方等等
比较特殊的:
-
Math.ceil(a)
向上取整(小数位只要有值就自动进1–1.0->1 1.1->2) -
Math.floor(a)
向下取整(只取整数位,小数部分会被舍掉) -
Math.round(a)
四舍五入取整 -
Math.random()
生成(0,1)的随机数(真随机) -
Math.max(a,b,...)
多个数中的最大值(同理还有min最小值) -
Math.pow(x,y)
x的y次幂
生成从min到max范围的随机数:
Math.floor(Math.random() * (max - min + 1) + min)
函数
函数也是一个对象
方法:当函数作为对象的属性保存时,这个函数是这个对象的方法。函数和方法只有名称上的区别
创建
三种方式:
-
new Function
var func = new Function("console.log('调用函数');")
-
function 函数名
(常用)function func() { console.log("调用函数"); }
-
给匿名函数起名
var func = function() { console.log("调用函数"); }
调用函数:
func();
传参
传递形参:function func(a,b){}
其中a和b可以是任意的数据类型
在调用函数传参时,不会检查实参的类型和数量(多余的实参会被忽略,少的形参会是undefined)
对于基本数据类型都是值传递,对于对象类型是引用传递
function setName(obj) {
obj.name = "小明";
}
let person = {};
setName(person);
console.log(person); // {name: "小明"}
函数也可以作为参数进行传递:
function func(a) {
a("func");
}
function outer_func(content) {
console.log(content);
}
func(outer_func); //相当于outer_func("func");即console.log("func");
对象属性用作实参
当一个函数包含的形参有多个时,可以通过传入对象的方式,以对象中的属性作为真正的实参,此时便无需关注参数的顺序
function foo(obj) {
console.log(obj.name, obj.sex, obj.age);
}
foo({ sex: '男', age: 18, name: '小明' }); //小明 男 18
函数默认值
设置默认值的参数应放在尾部
function sayHi(name = 'everyone') { //定义函数时,直接给形参赋值
console.log( 'Hello ' + name + '!');
}
sayHi(); //'Hello everyone!'
sayHi('Tony'); //'Hello Tony!'
sayHi(undefined); //'Hello everyone!'
默认值可以是一个值、表达式以及函数调用
返回值
return;
、return undefined;
和不写return语句都是返回undefined
return后面的语句不会被执行
函数的内部可以再声明函数,函数对象也可作为返回值
匿名函数
函数定义完,立即被调用,这类立即执行函数往往只执行一次
function(){alert("匿名函数");} //这样写会报错,需在两边加上括号来进行标识
(function(){alert("匿名函数");}) //正确写法
(function(){alert("匿名函数");})(); //调用匿名函数
(function(a,b){console.log(a+b);})(10,20); //还可以传递参数
自执行函数:将一段程序封装进一个会自动执行的匿名函数,防止变量污染
语法:(function(){/*程序段*/})();
this
解析器每次在调用函数时都会向函数内部传进一个隐含的参数this,它指向一个对象,称为函数指向的上下文对象;在函数中可以直接使用this
根据函数调用方式不同,this会指向不同的对象。以函数形式调用时,this永远是window;以方法的形式调用时,this就是调用方法的对象;可以说,谁调用了这个函数,this就是谁
var name = "全局"; //即window.name
function say_name() {
console.log(this.name);
}
var obj1 = {
name: "obj1",
say_name: say_name
}
var obj2 = {
name: "obj2",
say_name: say_name
}
say_name(); //"全局"
obj1.say_name(); //"obj1"
obj2.say_name(); //"obj2"
函数的call()
和apply()
方法:可以改变函数中this指向
func.apply/call(指定的this,函数参数)
使用指定的this调用函数
function func() {
alert(this);
}
func(); //[object Window]
obj = {};
func.apply(obj); //[object Object]
func.call(obj); //[object Object]
区别:对于有参数的函数,call()
可以将实参放在对象之后依次传入函数,而apply()
需要将实参放到一个数组中统一传递
function func(key1, key2) {
console.log(key1 + ":" + this[key1]);
console.log(key2 + ":" + this[key2]);
}
obj = { name: "abc", age: 20 };
func.call(obj, "name", "age"); //name:abc age:20
func.apply(obj, ["name", "age"]); //name:abc age:20
arguments
函数调用时,浏览器除了this,还好传递进一个隐含的参数arguments,用于封装实参;它是一个类数组对象,不是数组,但也可以通过索引来操作数据并获取长度;在调用函数时,传递进的实参在arguments中保存;即使不定义形参,也可以通过它来使用传进来的实参
function func() {
console.log(arguments[0]);
console.log(arguments[2]);
console.log(arguments.length);
}
func(0, 1, 2); //0 2 3
arguments可以跟形参一起使用,并且arguments对象中的值会和对应的形参保持同步
function foo(a) {
arguments[0] ++;
console.log(a);
}
foo(10); //11
function foo2(a) {
a++;
console.log(arguments[0]);
}
foo2(10); //11
如果缺少传参,那这个形参的值就不会和arguments对象中的对应值进行同步
function foo(a,b) {
arguments[1] = 2;
console.log(b);
}
foo(1); //undefined
//形参b没有传入实参,它的值会默认为undefined
foo(1, undefined); //2
//手动传入undefined时,arguments数组中会出现一个值为undefined的元素
arguments的callee属性:正在执行的函数对象
function func() {
console.log(arguments.callee);
}
func(); //ƒ func() {console.log(arguments.callee);}
数组
数组也是一个对象,用于存储一些值。
不同的是:普通对象使用字符串作属性名,而数组使用数字作为索引操作元素,索引从0开始;数组的储存性能比普通对象好
创建
-
new数组对象
Array
var arr = new Array(); var arr = new Array(10,20,30);
-
字面量
[]
var arr = []; var arr = [1,2,3];
区别:
var arr=[10]; //创建一个只有元素10的数组
var arr =new Array(10); //创建一个长度为10的数组
注意:数组中的元素可以是任意的数据类型(包括对象、函数、数组),且同一数组中可以有不同的元素类型
var arr = [[1,2,3],[10,20,30]]; //二维数组
赋值与获取
js数组中索引从0开始
-
使用
arr[index]
获得/修改index位置上的元素。若不存在index,则返回undefined(不报错) -
使用
arr.length
获得/修改数组中元素个数。对非连续数组,length为数组最大的索引+1,不连续的部分用null补位
var arr = new Array();
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
console.log(arr); //(3) [10, 20, 30] 表示有3个元素,值为10 20 30
console.log(arr[0]); //10
console.log(arr[3]); //undefined
console.log(arr.length); //3
arr[10] = 110;
console.log(arr.length); //110
console.log(arr); //(11) [10, 20, 30, 空 ×7, 110]
console.log(arr[arr.length-1]); //110 获取最后一个元素
arr[arr.length] = 40; //向数组最后一个位置添加元素
length可被修改:
如果修改的length大于原长度,则多余的部分为空;小于原长度,则多余部分被删除
var arr =new Array(10,20,30);
arr.length = 5;
console.log(arr); //(5) [10, 20, 30, 空 ×2]
arr.length = 2;
console.log(arr); //(2) [10, 20]
添加元素
-
arr.push()
向数组末尾添加1个或多个元素,返回数组新的长度var arr = [1, 2, 3]; var res = arr.push(4, 5); console.log(arr); //(5) [1,2,3,4,5] console.log(res); //5
-
arr.unshift()
向数组开头添加1个或多个元素,返回数组新的长度var arr = [1, 2, 3]; var res = arr.unshift(-1, 0); console.log(arr); //(5) [-1,0,1,2,3] console.log(res); //5
-
arr.splice(start,0,新值1,新值2,...)
在start位置插入新值var arr = [0, 1, 2]; var res = arr.splice(1, 0, "new_num1", "new_num2"); //在a[1]的位置插入"new_num1", "new_num2" console.log(res); //[] console.log(arr); //(5) [0, 'new_num1', 'new_num2', 1, 2]
-
arr1.concat(arr2,arr3,...)
将arr2,...
追加到arr1后,返回新数组,不会改变原数组。其中arr2不仅可以是数组,也可以是单个元素var a1 = [1, 2, 3]; var a2 = [10, 20, 30]; var a3 = [100, 200, 300]; var res = a1.concat(a2, a3); console.log(res); //(9) [1, 2, 3, 10, 20, 30, 100, 200, 300]
var a1 = [1, 2, 3]; var a2 = [10, 20, 30]; var res = a1.concat(a2, 100, 200, 300); console.log(res); //(9) [1, 2, 3, 10, 20, 30, 100, 200, 300]
删除元素
-
arr.pop()
删除数组的最后一个元素,并将删去的那个元素返回var arr = [1, 2, 3]; var res = arr.pop(); console.log(arr); //(2) [1,2] console.log(res); //3
-
arr.shift()
删除数组的第一个元素,并将删去的那个元素返回var arr = [1, 2, 3]; var res = arr.shift(); console.log(arr); //(2) [2,3] console.log(res); //1
-
arr.splice(start,number)
删除数组中指定元素,从索引为start的元素开始删number个元素,传递的索引也可以为负数(表示倒数第几个数)。直接改变原数组,将被删除的元素返回var arr = [0, 1, 2, 3, 4, 5]; var res = arr.splice(1, 2); //从arr[1]开始删除2个元素,arr[1]也算其中1个 console.log(res); //(2) [1, 2] console.log(arr); //(4) [0, 3, 4, 5]
替换元素
arr.splice(start,number,新值1,新值2,...)
表示将arr[start]开始的number个元素替换为新值,替换后元素个数可以大于替换前个数
var arr = [0, 1, 2, 3, 4, 5];
var res = arr.splice(1, 1, "new_num1", "new_num2"); //将"new_num1", "new_num2"替换到a[1]位置
console.log(res); //[1]
console.log(arr); //(7) [0, 'new_num1', 'new_num2', 2, 3, 4, 5]
切片
arr.slice(start,end)
提取[start,end)
索引的元素,end参数可省略,默认一直取到数组结尾;注意这个方法不会改变原数组,而是将切片后新数组返回
var arr = [0, 1, 2, 3, 4, 5];
var res = arr.slice(1, 3);
console.log(res); //(2) [1, 2]
res = arr.slice(2);
console.log(res); //(4) [2, 3, 4, 5]
传递的索引也可以是负值,-n代表倒数第n个元素
console.log(arr.slice(0, -1)); //(5) [0, 1, 2, 3, 4]
console.log(arr.slice(-1, -4)); //[]空数组,注意slice只能正向切片,无法从后往前输出元素
console.log(arr.slice(-4, -1)); //(3) [2, 3, 4]
console.log(arr.slice(-2)); //(2) [4, 5]
遍历
-
正常for循环
var arr = [1, 2, 3]; for (var i = 0; i < arr.length; i++) { console.log(arr[i]); }
-
arr.forEach(function (value, index, obj){...})
该方法需要一个函数作为参数,像这种由我们创建但不由我们调用的函数称为回调函数
数组中有几个元素,函数就执行几次,每次执行时,浏览器会将遍历到的元素以实参形式传递进函数中
浏览器会传递进回调函数3个参数:当前正在遍历的元素值、当前正在遍历的元素索引、正在遍历的数组对象
var arr = ["a", "b", "c"]; arr.forEach(function (value, index, obj) { console.log("arr[" + index + "]=" + value); //arr[0]=a arr[1]=b arr[2]=c console.log(obj); //(3) ['a', 'b', 'c'] });
其它方法
-
arr.join(seq)
将数组转换为字符串,以seq为分隔符,默认为逗号;返回新数组,不会改变原数组var a1 = [1, 2, 3]; var res = a1.join() console.log(res); //1,2,3 res = a1.join("|"); console.log(res); //1|2|3 res = a1.join(""); console.log(res); //123
-
arr.reverse()
反转数组;注意该方法会直接改变原数组var a1 = [1, 2, 3, 4]; a1.reverse(); console.log(a1); //(4) [4, 3, 2, 1]
-
arr.sort()
对数组元素排序,默认按照Unicode编码从小到大排序;即使对于纯数字数组,默认也是按编码排,可能得到错误结果(如11<2<3<4);注意该方法会直接改变原数组var letters = ["a", "e", "c", "a", "b"]; var number = [11, 3, 2, 6, 5, 4]; letters.sort(); number.sort(); console.log(letters); //(5) ['a', 'a', 'b', 'c', 'e'] console.log(number); //(6) [11, 2, 3, 4, 5, 6]
可以自己指定排序规则:在sort中添加回调函数,在函数中需定义两个形参,浏览器将会分别使用数组中元素作为实参调用回调函数,并根据回调函数返回值决定因素顺序:若返回大于0的值,则元素会交换位置;返回<=0的值不换位置
var number = [11, 3, 2, 6, 5, 4]; number.sort(function (a, b) { if (a > b) { return 1; } else if (a < b) { return -1; } else { return 0; } }); console.log(number); //(6) [2, 3, 4, 5, 6, 11]
调换上面if与elseif中返回值,就可以实现从大到小排列
一种更简便的方法:
number.sort(function (a, b) { return a - b; });
-
arr.map(function (ele, index){...})
遍历数组,传入一回调函数,按该函数对数组中的每个元素进行处理,将处理结果存入新数组并返回const arr = ['red', 'blue', 'pink']; const new_arr = arr.map(function (ele, index) { //ele表示当前元素值,index为当前元素索引 return index + ":" + ele; }); console.log(new_arr); //['0:red', '1:blue', '2:pink']
-
arr.filter(function (ele, index){...})
根据条件筛选数组元素,将符合条件的元素整合成新数组返回const arr = [10,20,30]; const res = arr.filter(function(item,index) { //item表示当前元素值,index为当前元素索引 return item>=20; //item>=20即为条件 }); console.log(res); //(2)[20,30]
注意:map方法是将原数组的每个元素处理后返回到新数组,filter是将筛选后结果返回;它们都不改变原数组
-
arr.reduce(function(上一次值,当前值){},初始值)
-
当前值:reduce也是遍历数组元素,当前值就是当前遍历到的数组元素值
-
上一次值:reduce回调函数内需返回一个值,上一次值就是上次循环返回的那个值
-
初始值:如果有,第一次遍历时,上一次值被赋成初始值,当前值为数组的第一个元素;没有,第一次遍历,上一次值是数组第一个元素,当前值是第二个元素
例:数组元素的递加
const arr = [1,2,3,4,5]; const total1 = arr.reduce(function(prev,current){ return prev+current; }); //无初始值 console.log(total1); //15 const total2 = arr.reduce(function(prev,current){ return prev+current; }, 20); //有初始值 console.log(total2); //35
有些时候必须写初始值,比如累加对象中的某属性:
const arr=[{ 'name':'abc', 'salary':1000 },{ 'name':'bcd', 'salary':2000 },{ 'name':'cde', 'salary':3000 }]; const total = arr.reduce(function(prev,current){ return prev+current.salary }, 0); //如果不加初始值,第一次遍历的prev将是一个对象,无法累加 console.log(total); //6000
-
-
arr.find(function (ele, index){...})
返回符合条件的第一个数组元素,与它类似的有findIndex()
是返回索引const res = arr.find(function(item, index){ //item表示当前元素值,index为当前元素索引(可选) return item.name == 'bcd'; }); console.log(res); //{name: 'bcd', salary: 1000}
-
arr.every(function (ele, index){...})
检测数组内所有元素是否都符合条件,若是返回true,否则false。若传入空数组则直接返回trueconst res = arr.every(function(item){ return item.salary>=1000; }); console.log(res); //true
-
arr.some(function (ele, index){...})
只要数组内有1个符合条件就是true -
静态方法
Array.from(伪数组)
将伪数组转换成真数组并返回,也可使用展开运算符...
const li_list = document.querySelectorAll('ul li'); const li_array = Array.from(li_list); //或者 const li_array = [...li_list]; li_array.pop(); //伪数组没有该方法,但真数组可以使用
一些简单应用
例1:将age不同的Person对象存入数组中,获取age>=18的person对象
function Person(name, age) {
this.name = name;
this.age = age;
}
var person = [new Person("p1", 18), new Person("p2", 10), new Person("p3", 19), new Person("p4", 30), new Person("p5", 1)];
function get_adult(arr) {
var new_arr = []; //创建结果数组
for (var i = 0; i < arr.length; i++) {
if (arr[i].age >= 18) { //进行条件判断
new_arr.push(arr[i]);
}
}
return new_arr;
}
var adult = get_adult(person);
例2:利用splice生成随机不重复数组
设生成长度为3的不重复数组,元素在[0,4]范围内:
let range = [0, 1, 2, 3, 4]; //元素范围(可能取值)
let res = [];
for (let i = 0; i < 3; i++) {
let random _index = Math.floor(Math.random() * range.length);
res.push(range[random_index]); //将随机数添加到结果中
range.splice(random_index, 1); //将该数从可能取值中删除
}
console.log(res);
字符串与正则表达式
字符串
在底层,字符串是以字符数组的形式保存的,可以使用类似数组的属性
var str = "hello";
console.log(str.length); //5
console.log(str[1]); //e
字符串的方法:大部分都不会改变原字符串
char系列
-
str.charAt(n)
,返回指定位置的字符,相当于str[n]
-
str.charCodeAt(n)
返回str[n]
字符的Unicode编码 -
String.fromCharCode(n)
获取编码为n的字符
查找子串
-
str.indexOf(substr,start=0)
从start位置开始,检索字符串中是否含有指定内容,如果有就返回其第一次出现的索引;没有则返回-1var str = "hello"; console.log(str.indexOf("el")); //1 console.log(str.indexOf("h", 2)); //-1 从str[2](包括str[2])开始查找
同理有
lastIndexOf()
返回最后一次出现的索引 -
str.includes(substring,startindex=0)
从startindex开始查找substring是否在str内,是则返回true,反之为false
切片
-
str.slice(m,n)
取[m,n)的字符串,n默认到字符串结束;m n也可以取负数,表示倒数第几个位置(与数组中slice相同)var str = "hello"; var res = str.slice(1, 1); console.log(res); //空([m,m)取不到)
-
str.substring(m,n)
取[m,n)的字符串,n默认到字符串结束;注意该方法与slice的不同是它不能接收负值,若传了负值,则默认为0;还可以自动调整参数位置,若传入的m < n,则自动交换(slice没有这项功能,该情况下返回空)var str = "hello"; var res = str.substring(1, -4); //相当于substring(1,0),又相当于substring(0,1) console.log(res); //h
-
str.substr(m,n)
从str[m]开始截取n个字符(包括str[m])var str = "hello"; var res = str.substr(1, 2); console.log(res); //el
-
str.split(seq)
按seq将字符串拆分成数组,若不传seq就返回将整个字符串放入数组的第一个元素中返回var str = "hello,js,world"; var res = str.split(","); console.log(res); //(3) ['hello', 'js', 'world']
还可以分离字符串中的每个字母:
var str = "Hello World"; var res = str.split(""); console.log(res); //(11) ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']
大小写转换
-
str.toLowerCase()
所有字符变小写 -
str.toUpperCase()
所有字符变大写
var str = "Hello World";
console.log(str.toLowerCase()); //hello world
console.log(str.toUpperCase()); //HELLO WORLD
检测是否以某字符开头
str.startsWith(substring,startindex=0)
判断str是否以substring开头,startindex为开始检测的位置(默认为开头)
const str = 'abc ABC';
console.log(str.startsWith('abc')); //true
console.log(str.startsWith('ABC')); //false
console.log(str.startsWith('ABC',4)); //true
同理还有str.endswith(substring[, start[, end]])
是否以substring结尾
字符串拼接
str1.concar(seq,str2)
连接两个或多个字符串(与数组中相同)
var res = str.concat(" ", str1) //相当于str+" "+str1
ES6里面增加了`${变量}`
新语法,用处是将字符串变量与字符串进行拼接,与python中的f-string类似。
注意:使用这种方法拼接的字符串两侧用`
包裹,而不是引号
var a = 1;
console.log(`a的值是:${a}`); //a的值是:1
正则表达式
创建
创建正则表达式对象:
-
正则表达式对象RegExpd的构造函数
var reg = new RegExp("xxx"); //xxx应为正则表达式 var reg = new RegExp("xxx","i"); //第二个参数"i"表示忽略大小写 var reg = new RegExp("xxx","g");/ /第二个参数"g"表示全局匹配模式
-
字面量:
var 变量 = /正则表达式/匹配模式
var reg = /xxx/i; //相当于RegExp("xxx","i") var reg = /[a-z]/ig; //相当于RegExp("[a-z]", "ig"),即忽略大小写又全局匹配
两种创建方式的区别:
-
使用构造函数创建可以传入一个字符串变量作为正则表达式,而字面量创建不行
-
用构造函数创建,因为传入的是字符串,需要进行转义。
如正则表达式标准里面,用
\.
来代表.
这个符号,使用字面量可以直接reg = /\./
,而构造函数中就需将\
进行转义(”\\”->\),写成RegExp("\\.")
的形式;同理
reg = /\\/
等效于RegExp("\\\\")
^
$
同时出现:精确匹配,表示整个字符串都必须符合指定规则,如/^a$/
只能匹配"a"
,表示整个字符串就是一个"a"
,"aa"
都不行。一般情况下都使用这种精确匹配
使用
-
reg.test/exec(str)
其中reg为正则表达式,str为待检测字符串-
test
返回true/false,检测str是否含符合正则表达式的子串; -
exec
返回数组,里面包含符合正则表达式的子串的索引等,若没有则返回空
var reg = /[a-z]/ig; var res = reg.test(str);
-
-
str.split(reg)
将正则表达式作为分隔符来拆分字符串var str = "1q2w3e4r5t6y"; var res = str.split(/[A-z]/); //以所有字母为分隔符进行拆分 console.log(res); //(7) ['1', '2', '3', '4', '5', '6', '']
-
str.search(reg)
搜索字符串中是否含指定内容,返回该内容第一次出现的索引,不出现则返回-1var str = "abc acc adc"; var res = str.search(/a[cd]c/); //是否含有abc或adc console.log(res); //4
-
str.match(reg)
根据正则表达式,将符合条件的内容提取出;默认情况下只会提取第一个符合条件的内容,可以设置正则表达式为全局匹配模式来提取所有内容,都会封装到一个数组中返回
var str = "1q2w3e4r1Q2W3E4R"; var res1 = str.match(/[a-z]/); var res2 = str.match(/[a-z]/ig); console.log(res1); //['q', index: 1, input: '1q2w3e4r1Q2W3E4R', groups: undefined] console.log(res2); //(8) ['q', 'w', 'e', 'r', 'Q', 'W', 'E', 'R']
-
str.replace(reg,new_val)
将字符串中指定内容替换成新的内容,返回替换后的新串,不会改变原串;也是默认只替换第一个,设置全局匹配模式替换所有内容;将要替换的值写成空串可以删除指定内容
var str = "1q2w3e4r1Q2W3E4R"; var res1 = str.replace(/[A-z]/g, "|"); var res2 = str.replace(/[a-z]/ig, ""); console.log(res1); //1|2|3|4|1|2|3|4| console.log(res2); //12341234
注意这4个方法中,split
不用设置全局匹配,在找到所有符合正则的位置后进行拆分;search
不能设置全局匹配,只能找到第一次出现位置
作用域
全局作用域
指编写在script标签内的代码。在页面打开时创建,关闭时销毁,在页面的任意部分都可以访问
其中有一个全局对象window,可以直接使用,它代表浏览器的窗口;创建的变量都会作为window对象的属性保存
函数作用域
调用函数时创建的作用域,函数执行完后销毁;
每调用一次就创建一个新的函数作用域,它们之间相互独立;
在函数作用域中可以访问并修改全局作用域中的变量(不需传参,可以直接用变量名取到),但全局不能访问函数作用域的变量;
使用变量时,函数内会优先寻找函数作用域内变量,要是没有才向上一级作用域中找,直到全局作用域中也没有就报错
在函数中访问全局变量a:window.a
声明提前
ES5
变量的声明提前:使用var关键字声明的变量,会在所有的代码执行前被声明(不是被赋值,赋值还是在var处)
如果声明变量时不使用var关键字,则不会被提前
函数的声明提前:使用函数声明创建的函数(function xxx(){}
)会在所有的代码执行前被创建(不只是声明,也包括其中的内容),可以在函数调用之后写函数内容(对变量赋值);
而使用函数表达式创建的函数(var xxx=function(){};
),只会将xxx变量声明,不会提前创建函数内容,如果在函数调用之后写会报错
函数作用域中也有声明提前的特性,即用var声明的变量会在函数中所有代码执行前被声明,使用函数声明创建的函数会在函数中所有的代码执行前被创建;
函数内声明新变量时不用var,就会自动将其声明为全局变量
ES6
let
不存在变量提前,且作用在块级作用域
解决ES5中,变量提前与函数作用域可能造成的不合理后果。例如:
var tmp = '你好';
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
此代码原意是if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量,应该输出'你好'
。但if内的tmp因为变量声明提前,覆盖了外层的tmp变量,且因为if不可能执行,无法赋值
function f1() {
let n = 20;
if (true) {
let n = 10;
}
console.log(n); //20
}
使用let,外层代码块不受内层代码块的影响,该思想与c里面的变量作用域相似,都是哪里声明哪里使用,不会越级
const的作用域与let命令相同:
-
只在声明所在的块级作用域内有效
-
声明不提前,只能在声明的位置后面使用
-
不可重复声明
程序调试
对程序的调试:f12->源代码->js文件->单击代码前数字,可以设置断点->刷新页面,代码栏右侧可以控制执行下一步等等
监视某个变量:代码栏右侧的监视
->点击添加+
->输入变量名