JavaScript
JavaScript特点
javascript是弱类型,解释型,脚本语言。
1.弱类型:数据类型不固定,可以随时改变。(不用去声明数据类型)
2.解释型:相对于编译型来说。
1编译型语言:程序在运行之前就需要整体的进行编译(翻译)。
2解释型:程序运行的时候不会编译,拿一行代码,解释一行代码。
3.脚本语言:
脚本:是一种程序,它不能独立运行,需要一个载体。
JavaScript的构成
-
核心,ECMAScript,负责核心语法部分(简称ES,主要讲的是ES5然后讲ES6、ES7…)
-
文档对象模型,DOM 一套提供给程序员操作浏览器功能的API
-
浏览器对象模型,BOM 一套提供给程序员操作页面元素的API
JS命令
1.控制浏览器弹出警告框
alert(" ");
2.计算机在页面中输出一个内容
document.write(" ") ;可以向body中输出一个内容
3.向控制台输出一个内容
console.log(" ");
4.在页面中弹出一个输入框,给用户提供输入信息位置
prompt(" ");
5.弹出一个确认框。有确认和取消按钮;
confirm(" ");
代码从上往下执行
js命令的编写位置
1.行内写法(局限性大,代码分离性差,可读性不强)
<button onclick="alert(' ');">点我一下</button> 编写到标签的onclick属性中
<a href="javascript:alert(' ');"> 你也点我一下 </a> 编写到超链接的href属性中
2.内嵌式(通常在body的最下面写,因为代码从上往下执行)
<script type="text/javascript">
</script>
3.外链式(项目会把JS代码和HTML CSS进行分离)
<script type="text/javascript" src="js/xxx.js"> </script> 一旦引入外部文件,中间不能再写命令,
如要写再创建一个script标签
字面量和变量(变量用来做运算)
-
字面量都是些不可变值例如1 2 3 4 5
-
变量可以用来保存字面量 例如 x=1 2 3 且变量的值是可变的。还可以通过变量对字面量进行描述 ,例如 var age(年龄)=80
声明变量: var a;
变量赋值: a=123;
合并成 var a=123;
一个变量声明一次就好。
-
一次定义多个变量:
1.第一种情况
var d = 1, e = 2;
2.第二种情况
var d = e =30; 1.声明了一个变量e(不带var),将30放进去 2.声明了一个变量d(带var),然后等于e ,相当于var d=e; 3.拿到了e中的1,将值放到d中。
-
变量之间相互赋值:
var a=123;
var b=345;
a=b;此时a的值为345
交换变量的第一个思路:使用第三方的变量进行交换;
var a=10;
var b=20;
var c;
c=a; //c=10;
a=b; //a=20;
b=c; //b=10;
交换变量的第二个思路:做加法,一般适用于数字的交换;
var a=10;
var b=20;
a=a+b; //a=30
b=a-b; //b=10
a=a-b; //a=20
-
变量起名有一定的规则:
1.数字,字母,下划线和$符。
2.不能以数字开头。
3.不能用关键字,保留字。
4.变量名要有意义。不要用拼音不要用中文。
5.驼峰命名法
数据类型
1.String:字符串
2.Number:数值
3.Boolean:布尔值
4.Null:空值
5.Undefined:未定义
6.Object:对象
其中String Number Boolean Null Undefined属于基本数据类型,而Object属于引用数据类型(对象)。
String:字符串
在JS中字符串声明变量时需要使用引号引起来,使用双引号和单引号都可以,但是不要混着用。
引号不能嵌套,一单一双可以。
在字符串中我们可以使用 \ (斜杠)作为转义字符,当表示一些特殊字符时可以使用\进行转义
\ “ 表示 ”(双引号)
\ ’ 表示 ’ (单引号)
\ n 表示换行
\ t 表示制表符 (tab)
加引号输出字面量 字符串str
alert("str")
不加引号输出变量 str
alert(str)
Number:数值
在js中所有的数值都是Number类型,包括整数和浮点数(小数)
声明变量时不需要用引号引起来。
//数值123
var a = 123
//字符串123
var b = "123"
可以使用一个运算符 typeof 来检查一个变量的类型 语法:typeof 变量/值
例如: console.log(typeof a);
检查为字符串时,会返回 string
检查为数值时,会返回number
计算机对于小数运算会有偏差。
Boolean类型
- 用来做开关
- 只有两个固定的值:true和false。
undefiend类型
-
只有一个值undefiend
-
通常表示变量声明但是未初始化。
1.声明了变量未赋值
null类型
- 只有一个值null
获得类型
格式:typeof 值/变量(可以在后面写声明的变量 ,也可以直接写需要获取类型的值)
console.log(typeof a);
得到的结果是数据类型名的小写形式。
-
boolean,布尔
-
string,字符串
-
number,数值
-
undefined,值未定义(定义变量未初始化)返回underfined两种情况,一是声明未赋值,二是未声明。
-
object,当是对象或null时将会得到object
null是基本类型的值。但是typeof null的时候返回的是object。
-
其他的值:到时候再说
类型转换的类型
弱类型语言:弱在哪里:
- 强类型语言必须在声明变量之前指定对应的数据类型。
- 弱类型语言定义了什么类型之后,变量里面的类型可以随着值的变化而变化。而且值可以自动的进行转换。
类型转换分为两种类型:
- 强制类型转换,自己强制让某个值转换为什么。
- 隐式类型转换,变量配合后面讲的运算符进行运算时发生的类型转换。
强制类型转换(转布尔,转数值,转字符串)
1.将值转为布尔值(Boolean) 使用Boolean()小工具(得到的结果就是true和false)
- 字符串转布尔值:任何非空字符串(空格也算非空)都转换为true,空字符串(什么都没有)转换为false。
- 数值转布尔值:0 和NaN的会被转换为false,剩下非零值的转换为true。
- undefined:会转换为false。
- null:会转换为false。
- object(对象):都转换为true。
var str= ;
console.log(Boolean(str));
2.将值转为数值(Number) 使用Number()小工具。
-
布尔值转数值:true转换为1,false转换为0.
-
null转数值:转换为0.
-
undefined:会转换为NaN.
var str= ; console.log(Number(str));
-
字符串转数值的话:
1.字符串中只包含数字,包含浮点数 原样转换出来。
2.空字符串,空白串转换为0.
3.其他情况(数字字母结合之类)转换为NaN.
-
parseInt()
,将字符串转换为整数(如果其中的值不是字符串那么先转换为字符串)。它会忽略前面的空格,找到第一个非空格的字符。如果这个字符是数字或负号,将会看第二位是否是数字如果是就转换为数值,一直向下找。一直找到不是数字的。
var str="25px"; console.log(parseInt(str));
-
parseFloat()
小工具:将字符串转换为小数。(用的比较少)
3.将值转为字符串(String)
- to String(),该工具要做的就是返回一个相应类型的值的字符串表现。
1.数值、布尔、字符串有toString()
值会原样转换。
2.undefined和null是没有toString()的,所以调用会报错。
var str= 25;
console.log(str.toString())
3.转换后的类型获取
var str= true;
console.log(str.toString())//布尔转字符
console.log(typeof String(str));//获取到类型为 string
4.原本的类型不改变
var str= true;
console.log(str.toString())//布尔转字符
console.log(typeof str.toString()); //toString()获取类型写法
console.log(typeof String(str));//获取到类型为 string
console.log(typeof str);//str 还是Boolean类型
- String()工具,将变量转换为字符串,你如果不知道变量中存的是什么类型的值就用它。
1.如果能调用toString()就自己调用toString()。
2.是null,则返回null。
3.如果是undefined,则返回undefined。
var str= null; //null转字符串
console.log(String(str)); //返回null
隐式类型转换
隐士类型转换和上面的规则相同,它通常和运算符相关联。(自己偷偷转换)
举例
Undefined+1 自己将underfined偷偷转为了数值类型 输出NaN NaN+1输出还是NaN
非数字NaN
NaN:not a namber;非数字,属于number类型, 用来表示数值的一种不正常的状态,一般情况下会在计算出现错误,无法计算的时候出现;
用数值和一个不是数值的数据进行算数运算,就会返回NaN;
console.log(11-"a");//NaN
NaN与任何数值计算都会得到NaN
console.log ( NaN + 7 ); // NaN
NaN与任何数字都不等,包含它本身;特殊到NaN不等于NaN;(和任何数比较结果都是false)。
console.log ( NaN == 0 ); // false
console.log ( NaN == NaN ); // false
关于NaN
不是数字,但属于数值类型
用数值和一个不是数值的数据进行算数运算,就会返回NaN;
NaN与任何数值计算都会得到NaN
NaN与任何数字都不等,包含它本身;特殊到NaN不等于NaN;
NaN不是数字,所以isNaN返回的是false
console.log(typeof(NaN));效果是在控制台输出’number‘;
特例NaN,和任何值都不相等,包括都不等于自己,只能用isNaN做判断
运算符
-
表达式:将数据(变量、数值)用运算符按照对应的规则连接起来的式子叫表达式。可以被求值。
-
分类
- 算术运算符
- 赋值运算符
- 复合运算符
- 比较运算符
- 相等运算符
- 逻辑运算符
- 三元运算符
算术运算符( + - * / %(%是余数))
- typeof也是运算符,可以获得一个值的类型,它会将该值的类型以字符串的形式返回
var a=123;
var result=typeof a; //获取a的数据类型
console.log(typeof result); // 以string形式返回
-
当对非Number的值进行运算时,会将这些值先转为Number值再运算,最后得到一个Number类型的值。(除了字符串加法 )
-
任何值做减 乘 除 运算时都会转为Number**(隐式类型转换)** ,原理和Number()函数一样
var d="123"; //字符串类型123
d=d-0;
d=d*1;
d=d/1; 都可以得到Number类型 123
- 任何值和NaN做运算都得NaN(字符串和NaN相加还是会拼串)
var result;
result=true+false; //1+0=1
result=NaN+true; //NaN+1=NaN
- (加法)任何值和字符串做加法运算的话,都会先转为字符串,然后再和字符串做拼串,最后得到一个string类型的值。
var result;
result="你好"+123; // 得到 你好123
result=123+"1"; // 拼接得到 1231
result=1+2+"3"; // 先1+2得到3 然后再拼串 得到33
result="1"+2+3; // 先1+2得到12 然后再拼串 得到123
- 所以可以得到简易的转字符串类型的方法**(隐式类型转换)**
var result=123; //Number类型123
result=result+"";//加空串得到 String类型123
除法(/)
被除数/除数=商
1.被除数为0,结果为0。
2.除数为0,结果为Infinity
。
3.任何数和Infinity运算结果还是Infinity
4.被除数 和除数都为0,结果为NaN。
求模,取余(%)
-
第一个数小于第二个数,结果是第一个数。
console.log(3 % 5) // 得到3
-
第二个数不能为0,如果为0结果为:NaN。
console.log(3 % 0) // 得到NaN
-
求余数。可以通过(
n% 2
余数是否为0 就知道是否是偶数,1就是奇数)。 -
求一个范围内的数。
console.log(x % 8); //它的结果只能是0~7之间的数
-
求出一个数字每一位上的数字是多少。
例如:
678 我的需求:我想知道个位、十位、百位分别是多少。
var v1 = 678 / 1 % 10; //个位除以1 然后求模10
console.log(v1);
var v2 = parseInt(678 / 10 % 10); //十位除以10 然后求模10,用parseInt()输出整数
console.log(v2);
var v3 = parseInt(678 / 100 % 10); //百位除以100 然后求模10,用parseInt()输出整数
console.log(v3);
一元运算符(+ -)
只需要一个操作符
- 正号 +
- 负号 -
- 对于非Number 的值会转换为Number 值再运算
var a= true;
a=-a; //true转数值为1
console.log(a) // 输出值为-1
可以对一个其他数据类型使用 +号来将其转为number类型 ,原理和Number()函数一样**(隐式)**
注意:并不会改变原变量的数据类型
a=1+"2"+3; //输出a为string类型 123
a=1++"2"+3; //输出a为number类型 6
自增自减运算
自增++
- 通过自增可以使变量在自身的基础上增加1
- 对于一个变量自增以后,原变量会立即自增1
- 自增分两种:后++(a++) 和 前++(++a),无论是a++ 还是++a,都会使原变量的值自增1.
- 不同的是a++ 和 ++a的值不同
a++的值等于原变量的值(自增前的值) //先用后加
++a的值等于新值(自增后的值) //先加后用
- 顺序从上到下,从左到右,每一次值的运算切记都是在前一个a的基础之上运算.
举例1
var c=10;
c++; //这里c++等于原值10,但是c自增了一次,c变成11了.
console.log(c++) //c变成11了,c++在11上维持原值,所以输出11.
举例2
var a=3;
console.log(++a) //++a为4 a为4
console.log(++a) //++a为5 a为5
console.log(++a) //++a为6 a为6
console.log(a++) //a++为6 (因为前面a为6了,所以a++在6的基础上等于原值6) 但是a已经加了4次 a的值这时为7
console.log(a++) //a++为7 是因为前面a已经变为7 在7的基础上等于原值所以为7 但是a这时已经是8了.
举例3
var d=20;
var result= d++ + ++d +d
//这里 d++维持原值为20 但是d自增1次变成了21
//所以这里 ++d 是在d变成21的基础上自增1,等于22了
//最后d已经经过两次自增了,变成了22
//所以最后result输出得到20+22+22等于64.
自减(同自增一样)
复合运算符
a=a+b 简写为 a+=b;
a=a-b 简写为 a-=b;
*=
/=
%=
赋值运算符
=
,将等号右边的值或表达式的结果放到变量中。
左边肯定是容器(变量、对象属性),右边肯定是值或有值的内容。
比较运算符
比较运算符最后的结果应该是布尔值,true或false。
>
<
>=
<=
比较规则:
-
如果两个操作数都是数值,执行数值比较。
-
如果两个操作数都是字符串,比较两个字符串对应的字符编码。(不常用)
从第一个字符的编码开始比较,如果第一个字符的编码能够比较出大小则后面的不比较,如果不能比较出大小则继续比较第二位。一直向后比较。
如果到最后都没有比较出来,则谁的位数多谁大。
console.log('234' > '123456789');//输出true '2'编码值为50 ;'1'编码值为49.
-
如果是其他类型比较则使用
Number()
进行转换。 -
NaN和任何数比较结果都是false。
相等操作符
-
相等和不相等 (会自动转换数值类型再做比较)
==
相等 ,如果两个操作数相等,返回true。!=
不相等,如果两个操作数不相等,返回true。-
如果类型相同比较值是否相等。
-
类型不同,只要有一个操作数是数值就先调用
Number()
-
基本类型number、string、boolean和基本类型number、string、boolean。会先转换为数值。
-
undefined和null是相等的。
console.log(null == undefined);// true 其是为相等的(这里并没有调用Number()) console.log(null >= undefined);// Number(null) >= Number(undefined) 0 >= NaN false
-
NaN和NaN不相等。
console.log(NaN != NaN);//true
-
填坑
1.console.log(null == 0);//false null在做判断的时候,不进行类型转换
2.console.log(null >= 0);//true null>=0,null 是会进行类型转换的 Number(null) >= 0 0>=0 true
3. console.log(null == undefined);//true (没有理由记住就好)
console.log(null >= undefined);//false Number(null) >= Number(undefined) 0 >= NaN//false
-
全等和不全等 (不进行类型转换且数据类型和值相等的情况下全等)
===
,全等,两个操作数未经过类型转换相等的情况下返回true。!==
,不全等。在两个操作数为经过转换就不相等的情况下返回true。
=
是赋值,==
是进行判断是否相等,===
是进行判断是否全等(没有经过类型转换值就得相等)
逻辑运算符
相等操作符、比较操作符只能比较两个值,但是有时候我们需要多个比较(需要有多个条件),这个时候我们就需要逻辑运算符。
&&
,逻辑与(与,and),第一个操作数和第二个操作数都为true时候返回true,否则返回false。(一假则假)
console.log(true && true);//true
console.log(true && false);//false
console.log(false && true);//false
console.log(false && false);//false
-
逻辑运算符的返回值:操作数如果不是布尔值那么经过
Boolean()
进行转换,返回时还是返回原来的值。console.log(null && 1);//null 这里就是返回原来的null,而不是返回布尔值false
-
如果第一个操作数经过Boolean()工具转换为true,那么将会返回第二个操作数。如果第一个操作数经过Boolean工具转换为false,则返回第一个操作数。(已经得到结果,后面的第二操作数压根就不执行)
例如:console.log(1 && 2);//2 1转布尔值为true ,所以返回第二个操作符2 console.log(null && 1);//null null转布尔值为false,直接返回null。后面的1都不执行了。
-
短路操作:逻辑与操作属于短路操作,如果第一个操作数能够决定结果(第一个操作数为false时),那么就不对第二个操作数求值。
-
||
,逻辑或(或,or),如果两个操作数都为false时返回false,否则返回true。(一真则真)console.log(true || true);//true console.log(false || true);//true console.log(true || false);//true console.log(false || false);//false
- 如果第一个数经过Boolean()后是true,就返回第一个数,如果第一个数经过Boolean转换为false就返回第二个数。
- 短路操作:如果第一个数的求值结果为true,就不会对第二个数操作求值。
console.log(1 || 2);// 1转布尔值为true,直接返回1
console.log(0 || 2);// 0转布尔值为false ,返回第二个操作符2
console.log(0 || null);// 0转布尔值为false ,返回第二个操作符null
题目1:
需求:有一个年份,我想知道是平年还是闰年。
//1. 已知的条件 年份
//2. 什么是平年、什么闰年 计算公式是什么。
4年一闰,百年不润。四百年一闰。
//能被4整除不能被100整除 或者 能够被400整除的才是闰年。
var year = 2021;
console.log((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
-
!
,取反(非,not),用来获得指定布尔值的取反的结果。(最终的结果是布尔值)。该操作符可以应用任何值,变量如果不是布尔值那么逻辑非会先将它的值转换为布尔值,然后再求反。
var a = 0;
var b = 10;
console.log(!(a || b)); /// (0 || 10) 返回的是10 !(10) //不是布尔值 调用Bollean()得到true ,true取反为 false 。
隐式转换为相应的布尔值:!!a(a可以是其他数据类型)
var a = "6";
console.log(!!a); //!!a 可以将a转换为自身对应的布尔值.
取反还可以取相反的结果
三元运算符:(条件运算符 )
格式:运算元1? 运算2: 运算3;
运算元1为条件。运算元2是表达式。运算元3也是表达式。(如果运算元1不是布尔值也会转换为布尔值再运算)
运算过程:如果运算元1为true,得到的结果是运算元2,为false的情况下得到的结果是运算元3。
举例:
var year = 2000;
var con = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);//一真为真
var res = con ? '闰年' : '平年';
console.log(res);
var a = 0;
var b = 10;
var c = a > b ? a : b; // 0>10 为false,所以c=b(运算元3)=10.
console.log(c);
-
运算元
1 + 2 ,1和2分别代表运算元(操作数)
-
运算符按照运算元来进行分类:
- 一元运算符
+
、-
、!
- 二元运算符
+
、-
、&&
、||
- 三元运算符。
- 一元运算符
代码块
在js中可以使用{}来为语句进行分组,同一个{}中的语句我们称为一组语句。
它们要不都执行,要不都不执行
一个{}中的语句我们也称为一个代码块
{
alert();
console.log();
} //代码块后面可以不用加;(分号)
运算符的优先级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hgB120GR-1610018155588)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200705101513459.png)]
简要的规则:一元运算符优先级通常比较高,然后是二元然后是三元。如果你是在不确定运算符的优先级那就加括号。
流程控制语句
条件判断语句
使用条件判断语句可以在执行某个语句之前进行判断
if语句:
语法一:
if(条件表达式){
语句...
}
//if语句在执行时,会先对条件表达式进行求值判断,
如果条件表达式值为true,则执行if后的语句
如果条件表达式值为false,则不会执行if后的语句
if语句只能控制紧随其后的语句,如果希望if语句可以控制多条语句,可以将这些语句统一放到代码块中。
if…else…语句:
语法二:
if(条件表达式){
语句...
}else{
语句... //里面可以继续嵌套 if...else if... else语句
}
//当该语句执行时,会对if后的条件表达式进行求值判断,
如果条件表达式值为true,则执行if后的语句
如果条件表达式值为false,则会执行else后的语句
只执行if或else中的一个代码块。
if…else if … else语句:(通常用作条件判断)
- 当该语句执行时,会从上倒下依次对条件表达式进行求值判断
- 如果条件表达式值为true,则执行当前语句。
- 如果值为false,则继续向下判断找到值为true的,并执行其当前语句。
- 如果所有的条件都不满足,则执行最后else的语句。
- 该语句中只有一个代码块会被执行,一但代码块执行了,则直接结束语句。
- 最后的else看你需求,可以有也可以不要。
语法三:
if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else if(条件表达式){
语句...
}else{
语句...
}
swith case语句(一般用来判断精确的值)
执行过程:
- 先确定switch中小括号的值是什么。
- 用确定出来的值分别和case后面的值进行匹配(这里的匹配是全等于匹配
===
)。 - 找到第一个能够匹配上的case的值之后,执行对应代码块中的代码。
- 执行完对应代码块中的代码之后调用break;退出整个switch语句。
- 如果到最后都没有匹配到,那么执行default中的语句。(相当于else)
注意:
-
switch语句在比较值的时候使用的是全等操作,不会发生类型转换。
-
default语句可以省略.(看需求)
-
break关键字可以省略。
:匹配到case并执行完对应语句后如果没有break;的话,会向下继续寻找直到找到break退出switch,过程中的语句也会被执行出来。
var month=parseInt(prompt("请输入月份"))
switch (month) {
case 1:
case 2:
case 3:
alert("是春季呢");
break;
case 4:
case 5:
case 6:
alert("是夏季呢");
break;
case 7:
case 8:
case 9:
alert("是秋季呢");
break;
case 10:
case 11:
case 12:
alert("是冬季呢");
break;
default:
alert("你怕不是来着火星");
break;
}
//合理穿透:当存在多种值需要执行相同代码时使用穿透可以节省代码,只写一个break;如上。
switch和if语句
switch也可以作条件判断,if也可以做精确(全等)判断,考虑合适与否。
switch也可以作条件判断:
switch(true){
case(条件判断表达式): //表达式的值true或false
语句;
break;
}
循环结构
while循环语句:
格式:
while(条件表达式){
重复执行的代码
}
执行过程:
- 第一步先检查条件表达式是否为true
- 如果为true才执行循环体中的代码。
- 执行完后再次检查条件表达式,如果为true再执行循环中的代码。
- 直到条件表达式为false就不执行循环了。
使用while循环要注意三个要素:
- while外部的起始值。(初始化表达式)
- while中的条件。(条件表达式)
- while内部要改变起始值,让while中的条件有为false的时候(否则则会死循环)。(更新表达式)
循环中可以嵌套任意我们学过的代码,包括循环。这样就形成了循环的嵌套。
:如果while内部嵌套了while,运行到内部while时会在内部while循环直到false跳出循环才会接着执行下面的代码。
//1.打印1~100的整数。
var a=1;
while(a<=100){
console.log(a);
a++;
}
//2.打印1~100之间的偶数。
var a=1;
while(a<=100){
if(a%2==0){
console.log(a)
}
a++;
}
//3.1~100的和。
var a=1;
var num=0;
while(a<=100){
num=a+num;
a++;
}
console.log(num);
//4. 1-100偶数的和
var a=1;
var num=0;
while(a<=100){
if(a%2==0){
num=a+num;
}
a++;
}
console.log(num);
//5.5行6列的矩形
var a=1;
while(a<=5){
b=1;
while(b<=6){
document.write("*")
b++;
}
document.write("<br>")
a++;
}
//6. 5行三角形
var a=1;
while(a<=9){
b=1;
while(b<=a){ //这个while直到false跳出才会去执行下面的 document
document.write("*")
b++;
}
document.write("<br>")
a++;
a++;
}
//7. 99乘法表
var a=1
while(a<=9){ //这里有9行
b=1
while(b<=a){
document.write(b+"*"+a+"="+a*b+" ")
b++
}
document.write("<br>")
a++;//这里决定内部while执行几遍,也就是这行有几列。
}
for循环语句:(可以嵌套for)
语法:其实和while差不多,只是把3个条件写在一起了
for(var i=1(初始化表达式);i<10(条件表达式);i++(更新表达式)){
循环语句;
}
执行顺序:
1.执行初始化表达式,初始化变量
2.执行条件表达式,判断是否执行循环
如果为true。则执行循环语句,如果为false,则不执行循环。
3.执行完循环语句后,就执行更新表达式,然后再重复 2(第二步)
注意:1.for循环中的三个部分都可省略,也可写在外部
2.如果ror循环中不写任何表达式,只写2个;(分号),此时是死循环。
//打印1-100内 7的倍数的个数和和
var num=0;
var sum=0;
for(i=1;i<=100;i++){
if(i%7==0){
num++;
sum=sum+i;
}
}
console.log(num);
console.log(sum);
//打印水仙花数(水仙花是三位数,个位的3次方+十位的3次方+百位的3次方=本身)
for(var i=100; i<1000;i++){
var bai=parseInt(i / 100 % 10);
var shi=parseInt(i / 10 % 10);
var ge=(i / 1 % 10);
if(bai*bai*bai+shi*shi*shi+ge*ge*ge==i){
console.log(i);
}
}
质数练习
//1.遍历1-100之间的质数并输出(质数只能被1和本身整除,1不是质数也不是合数,质数必须是比1大的自然数)
for( var i=1;i<=100;i++){ //1-100遍历
var flag=true; //先假设是i是质数先。
for(var b=2;b<i;b++){ //质数是只能被1和本身整除,所以i要保证不能被2-i区间的数给整除,区间以外比i大,坑定不能整除啊(b可以是2~i区间的某个数)
if(i%b==0){
flag=false;//反向思维 i被区间内的某个数整除了,断定i不是质数,flag变成false了,下面if后的语句不执行
break; //一但为false,得到结果了,不需要再循环了,直接跳出,可以改善效率
}
}
if(flag && i!=1){ //1不是质数
console.log(i);
}
}
还有一种方法var count=0
if count===2 就为质数,死死把控只能被自己和1整除2种情况。
//2.在页面中接受一个用户输入的数字,并判断该数是否为质数。
var num =parseInt(prompt("请输入一个大于1的整数:"))
if(num<=1){
alert("你这不按要求来啊");
}else{
var flag=true;
for(var i =2; i<num;i++){ //i是2-num区间内的某个数
if(num%i==0){ //num被区间数给整除了,断定不是质数,flag为false了。
flag=false;
}
}
if(flag){
alert(num+"是质数啊");
}else{
alert(num+"不是质数啊");
}
}
do while语句:(至少执行一次)
语法:
do{
循环代码;
}while(条件表达式)
执行顺序:先执行do中的语句,然后检查条件表达式是否成立,如果成立true继续执行do内的代码。
break和continue:
break:退出循环(碰到直接退出,下面的都不执行了)
-
break关键字可以用来退出switch或循环语句
-
不能在if语句中使用,但是如果if语句是在循环语句中的话则可以
-
break只对最近的循环语句起作用,如果内层循环有break的话只结束内层循环,而不会影响外层循环。
-
可以为循环语句创建一个label(任意取名),来标识当前的循环,然后使用break时,可以在break后跟着label,这样break将会结束指定的循环,而不是最近的。(外层循环结束了,内层自然也就结束了)
outer:(取名) for(var i=0;i<5; i++){ console.log("@外层循环"+i); for(var j=0;j<5; j++){ break outer; //label用法 console.log("@内层循环"+j); } }
continue:跳过当次循环(碰到直接跳过当次循环,下面也不执行了,直接进行下一轮循环)
-
continue关键字可以用来跳过当次循环
-
同样continue也是默认只会对离他最近的循环起作用,内层写的影响内层,不影响外层。
-
如果只写一个continue的话(没有条件的情况),每一次都会跳过。
-
也可以和break一样用label来标识当前循环,使用continue时后跟label,来跳过指定循环,而不是最近的。
outer: for(var i=1;i<5; i++){ console.log("@外层循环"+i); for(var j=1;j<=5; j++){ if(j==2){ continue outer; } console.log("@内层循环"+j); } }
计时器:
console.time("计时器的名字") //以字符串命名 然后开始
需要测试运行时间的语句放在中间
console.timeEnd("计时器的名字") //结束
数组
数组可以用来存储一组值。
数组的好处就是可以对里面的一组值进行批量操作。
基本概念
-
数组:一组值,数组可以存储多个值(每一项可以保存任何的数据类型,但是一般情况下都是相同的数据类型),有序的集合。
-
下标(索引、键、key),为了能够拿到对应位置的数据,所以给它标识。
下标都是从0开始。
-
元素:下标和值的组合叫元素。
-
类和对象的概念
数组是对象类型的(引用类型)的一种。
ES中没有类,有对象。
- 类:具有相同特征的事物的总称
- 每个类都有共同的特征,这些特征我们叫属性。 牌子、功率。
- 每个类都有一些共有的行为,这些行为我们叫方法。 制冷、制热。
- 对象:是类的具体化。一个具体的实实在在存在的东西。
- 对象和类是啥关系:根据类(模具)创造出来的实体(对象)。
- 类:具有相同特征的事物的总称
数组创建
1.使用Array来创建
var colors = new Array();//现在我就创建了一个新的空数组。
console.log(colors); //控制台输出数组
console.log(typeof colors);//返回数组类型
new
是操作符,它表示调用后面的内容(Array
,你可以暂时将Array
理解为类,但实际上Array不是类。)
2.在Array中如果只有一个元素并且是数值,将会创建数值个数的元素的数组,如果只有一个元素值是其他类型则会创建只有一个元素的数组。
console.log(new Array(4));//里面是值都是undefined;
var color =Array("red","blue") //声明数组时也可以省略new操作符,结果一样(内部也会调用`new Array())
3.字面量方式
var color=["red","blue"]; //直接中括号就好了
数组的lenght属性:
数组.length 可以获取数组里的元素的数量(数组长度);
console.log(arr.length)
数组.length 可以用来修改数组长度 arr.length = 5;
a.增加长度:设置更大的length来增加数组的空间
arrStus.length = 5;
b.减少长度:设置更小的length来减少数组的空间
arrStus.length = 2;
数组长度 = 数组最大下标 + 1;
数组最大下标 = 数组长度 - 1;
数组的基本操作
查看元素的值
操作数组时首先要干的事就是找下标。
var color2 = ['red', 'blue', 'green', 'pink', 'yellow'];
console.log(color2[2]); // 输出green
-
读取时候使用 数组[下标] 的方式。
-
如果读取时超过了最大的下标,那么值为
undefined
。 -
中括号中可以放置的是任何返回数值或数值型字符串的表达式。如
var color2 = ['red', 'blue', 'green', 'pink', 'yellow']; console.log(color2['0']); console.log(color2[25 - 25]); console.log(color2[1 + 1]);
4.拿取数组中的最后一个元素(数组中最后一个元素下标就是color2.lenght-1)
var color2 = ['red', 'blue', 'green','pink'];
//如何拿到最后一个元素。
console.log(color2[color2.length - 1]);
5.遍历:将数组中的所有值拿出来。(配合lenght属性)
//顺序遍历
var color2 = ['red', 'blue', 'green','pink'];
for (var i = 0; i < color2.length ; i++) {
console.log(color2[i]);
}
//倒序遍历
for (var i = color2.length - 1; i >= 0 ; i--) {
// console.log(i);
console.log(color2[i]);
}
数组增加删减元素
1.在末尾增加
使用lenght属性始终向数组末尾添加元素
var colors = ['red', 'blue'];
colors[colors.length] = 'green'; //向末尾加了一个green;
console.log(colors); //变成[red , blue , green]
colors[colors.length] = 'pink'; //又向末尾加了一个pink;
console.log(colors); //变成[red,blue,green,pink]
2.使用下标增加的话,设置时超过了最大下标的话,那么数组的最大下标为最后设置的下标,同时数组的长度变为 设置后的下标+1
var colors = ['red', 'blue', 'green'];
colors[5] = 'pink';
console.log(colors); //此时的数组长度为6了。
3.在数组的开始处进行添加
var col=["red","blue","yellow","pink"];
for(var i=col.length;i>=0;i--){ //这里i=col.length 最大下标为4 数组长度增加了一位;0~4长度为5
col[i]=col[i-1]; //一波操作下来变成了[r,r,b,y,p]
}
col[0]="black"; //这里重新为col[0]赋值,值为你想要在开始处添加的元素。
console.log(col);
4.数组指定位置处添加
例如:将pink插入到第二个下标的位置。(0 1 所以其实是下标为1的位置)
var colors = ['red', 'blue', 'green'];
for (var i = colors.length; i >= 0; i--) { //老规矩i=colors.lenght 最大下标为3 ,数组长度加1为4(0~3)
if (i === 1) { i=1,其实是下标为1的位置,如果想要插入到哪个下标的位置,只需更改i===?;下面的colors[1]改成相应的。
break;
}
colors[i] = colors[i - 1];//第一步colors[3]=colors[2] [r,b,g,g]
}
colors[1] = 'pink'; //到这里已经是[r,b,b,g],需要重新为下标1的元素赋值,值为你要插入的元素值
console.log(colors);
5.删除数组末尾的元素
lenght属性是可以修改的,如果lenght修改的值比原来的小,后面的值会自动删除。
var col=["red","blue","yellow","pink"];
col.length--;
console.log(col);
6.删除数组开头的元素
var col=["red","blue","yellow","pink"];
for (var i = 1; i < col.length ; i++) { // i=1-3,数组长度为3
col[i - 1] = col[i]; //操作下来得出[b,y,p,p]
}
col.length --; //长度减一,末尾元素删除 变成了[b,y,p]
console.log(col);
7.删除指定位置的元素
var col=["red","blue","yellow","pink"];
for (var i = 1; i < col.length ; i++) { i=1 col[i-1]=col[0]所以删除下标为0的元素,所以改变i的值来调整[i-1]的值(即你要删除的元素的下标)
col[i - 1] = col[i];
}
col.length --;
console.log(col);
8.数组求和
var arr=[1,2,3,4,5,6,7,8,9,10];
var num=0;
for(var i=0;i<arr.length;i++){
num=num+arr[i];
}
console.log(num);
案例最大值最小值
最大值
1.默认arr[0]为最大值赋值给max
2.从arr[1]开始大于max(也就是arr[0]),然后arr[1]被赋值给max,成为目前最大
3.继续遍历arr[i] 并与max (现在是arr[1]了)比较,如果比arr[1]大的话,又重新赋值给max
4.循环比较赋值得出最大值
这里其实max相当于一个容器,储存当前最大值,有比它大的就重新赋值,没有就它最大。
var arr = [1,2,3,4,5,6,7,8,9,10,88,22,-1,100];
var max = arr[0]; //默认arr[0]为最大值赋值给max
for (var i = 0 ; i < arr.length ; i++) {
if (arr[i] > max) { //从arr[1]开始大于max ,然后被赋值给max,直到下面遍历的数超过max,然后又重新赋值给max ,这样重复比较赋值得出最大值.
max = arr[i];
}
}
console.log(max);
最小值(原理和最大值一样)
var arr= [1,2,3,4,5,6,7,8,9,10,88,22,-1,100];
var min = arr[0];
for (var i = 0 ; i < arr.length ; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
console.log(min);
数组冒泡排序
var arr=[10,1,35,61,89,36,55];
for(i=1;i<arr.length;i++){ //外层控制比较几轮 i=1 意为第一轮 比较几轮取决于数组的长度,轮数=lenght-1
for(j=0;j<arr.length-i;j++){ //内层控制每轮比较的次数,第一轮比较 length-1次,
if(arr[j]>arr[j+1]){
var temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
console.log(arr);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qj8FkanP-1610018155592)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200707180551100.png)]
数组去重
var arr=[1,2,3,4,3,4,2,1,3,4,5];
var newArr=[]; //创建空数组来装非重复值
for(var i=0;i<arr.length;i++){
var val=arr[i] //先把arr[i]赋值给val
var flag=true; //假设arr[i]不是重复值
for(var j=0;j<newArr.lenght;j++){
if(newArr[j]===val){ //如果newArr中某个数等于arr[i],就退出循环,这个数也不必再装到newArr中去了。
flag=false;
break;
}
}
if(flag){
newArr[newArr.lenght]=val; //把arr[i]给到newArr数组。
}
}
console.log(newArr);
反转数组
1.利用空数组
var arr = [1,2,3,4,5,6,7,8,9,10,88,22,-1]; // 变成arr = [-1,22,88,10,9,8,7,6,5,4,3,2,1]。
var newArr = []; //创建空数组
for (var i = arr.length -1 ; i>= 0; i--) {
newArr[newArr.length] = arr[i]; // 倒序遍历给newArr数组
}
console.log(newArr);
2.获得对调元素的下标然后交换位置
var arr = [1, 2, 3, 4];//4,3,2,1
for (var i = 0; i < arr.length / 2; i++) { 获得到要对调的元素的下标。
console.log(i);
console.log(arr.length - 1 - i);
//交换它们的位置
var tmp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = tmp;
}
console.log(arr); */
数组合并
两个循环遍历一遍
var arr1 = [1,2,3,4];
var arr2 = [5,6,7,8,9];
var arr = []; //建arr[]空数组来装合并后的数组
for (var i = 0; i < arr1.length ; i++) {
arr[arr.length] = arr1[i];
}
for (var j = 0; j < arr2.length ; j++) {
arr[arr.length] = arr2[j];
}
console.log(arr);
遍历表格
var performeres = [
//编号,演员姓名,饰演角色,性格
[1, '孙红雷', '何辅堂', '为人正直不阿,欺强怜弱,善于机变。'],
[2, '巍子', '魏正先', '鹰视狼顾,心狠手黑。'],
[3, '陈数', '程立雪', '气质如兰,观之可亲,思想卓荦,才华压众。'],
[4, '郭珍霓', '刘二泉', '动不便,但若论手段心机,十个健全人也不是她的对手。'],
[5, '陈祉希', '朱彩灵', '刀马旦出身,水般柔美,火样性格。含威不露,顾盼神飞。']
];
document.write("<table border='1'>");
var str = "<tr><th>编号</th><th>演员姓名</th><th>饰演角色</th><th>性格</th></tr>";//表头
for (var i = 0; i < performeres.length ; i++) {
str += "<tr>";
str += "<td>"+performeres[i][0]+"</td>"; //字符串拼接
str += "<td>"+performeres[i][1]+"</td>";
str += "<td>"+performeres[i][2]+"</td>";
str += "<td>"+performeres[i][3]+"</td>";
str += "</tr>";
}
document.write(str);
document.write("</table>");
多维数组:
维:维度。
数组每个元素都可以存储任意类型的数据,数组中元素的值还为数组。
数组一般情况下就到3维,一维和二维用的多,三维数组几乎不用。但是不管是多少维的数组都是一层一层的往里面找。
var arr= [
[1,2], //arr数组下标0的元素
[3,4] //arr数组下标1的元素
];
console.log(arr[0][1]); //得到[1,2]中的2
console.log(arr[1][0]); //得到[3,4]中的3
函数
函数:完成指定任务并且已经命名的代码块。可以进行反复的使用。
函数本质上也是一种数据,是属于对象类型的。函数的也是值,与其他变量的数据类型不同的是,函数中包含的是代码,可执行的代码。
函数的作用
- 解决代码的冗余问题,形成代码的复用。
- 封装代码,让函数内部的代码对外部不可见。
- 可以把整个代码项目,通过函数模块化。
函数的种类:
1.系统函数,系统提供的(Blooen() String() Number())
2.自定义函数,如果系统不能满足需求就自己写
函数的声明方式
函数表达式声明:
var variable = function(){
语句
}
variable() //调用
-
这种声明方式是函数表达式方式,既外围语句是表达式。(不能以function开头 )
-
这种方式声明的函数,调用必须在创建之后(执行到这个表达式)才行。
函数声明式:
printTable(); //调用
function printTable(){
console.log('闫海静真英俊!');
}
这种声明方式调用可以在函数前面,这种声明方式在代码真正运行之前这个函数就已经被声明了。
这种函数名可以重复,一但重复后面的覆盖前面的。
函数构造器声明:
var a = new Function("console.log('abc')")
是使用Function构造函数来声明函数。给你说这个就是为了说明函数是一个引用类型。因为使用了new
不建议使用该方法声明函数,因为该方法会导致代码解析两次(一次常规解析ES代码,第二次是解析传入到函数中的字符串)。
函数的其他注意点
函数是数据,函数名是这个数据的标识。
-
如果要访问函数对应的数据而不是执行函数的话,需要去掉函数名后面的那对圆括号。
圆括号表示的是
函数调用符
-
函数是一个数据,函数名是标识,一个函数可以有多个标识。
函数的参数
设计自定义函数的时候:基本原则:用户(调用函数的人)可以参与控制自定义函数,可以对自定义函数进行一些微调。
function printTable(r,c) {
document.write('<table border="1">');
for (var row = 0; row < r; row++) {
document.write('<tr>');
for (var col = 0; col < c; col++) {
document.write('<td>哈哈哈哈@!@#</td>');
}
document.write('</tr>');
}
document.write('</table>');
}
printTable(3,3);
printTable(5,5);
- 形参:形式上的参数,声明函数时,提供的参数。(r c是形参)
- 实参:在调用时
()
里面实际使用的参数。(下面的3,3是实参)
注意:
- 形参和实参传值时是一一对应的。
- 如果有多个形参和实参使用逗号进行分隔。
- 函数的形参就相当于在函数内部定义了一个变量。
- ES中函数的形参、实参的个数可以不相等。
- 实参比形参少。那么多出来的形参将自动被赋值为undefined。
- 实参比形参多。多出来的实参被放在了
arguments对象
中。
arguments对象
-
arguments对象只在函数内部才有。
-
arguments对象实际上是一个类数组的对象。我们可以访问这个对象来获得传递给函数的每一个实参。
array-like
,像数组,但不是数组。因为它有下标、有length属性。如何区分是数组还是类数组 我们以后再说。
-
函数中的形参可写可不写,即使不写形参也可以使用arguments对象获得。因为使用形参比较方便。
-
arguments对象的length属性用来确定传递进来多少个实参。
可以通过length属性的值的不同(根据传入的参数个数的不同),进行不同的操作。
-
arguments
对象中保存着一个叫callee的属性,这个属性是一个指针,它指向拥有这个arguments对象的函数。arguments.callee指的就是这个函数。
函数的返回值
-
返回值。
-
return 要返回的值
-
return语句将return后面的值返回到函数调用处。
//定义一个函数,求1到n的和。
function he(n) {
var sum = 0;
for (var i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
he(10)
console.log(he(10)); //sum返回到函数调用处,函数的结果就是sum,这里就是打印sum的意思
- 什么时候该用return什么时候不用return。完全取决于你的需求。
注意:
- 函数在定义时不一定非得有返回值。如果没有指定返回值这个时候返回的是
undefined
。 - 函数会在执行完return语句之后立即停止执行并且退出(return语句之后的内容永远不会执行);
- return语句后可以不带返回值,函数在停止之后直接返回的是
undefined
通过以上的特点:return的作用:1.返回函数中要返回的值。2终止执行函数(return后面没有内容就只是单纯的终止函数执行。)
函数体、参数、返回值:是组成一个完整函数的三大件。
- 无参数无返回值。
- 有参数无返回值。
- 无参数有返回值。
- 有参数有返回值。
返回值的情况:
1.没有return,写再多函数的返回值还是undefined
2.有return,后面不跟东西,返回值也是undefined
3.return后跟的就是函数的返回值,也可以理解为函数的执行结果。
4.return只会返回函数结果,并不会打印,所以需要声明一个变量来接收,再打印变量。
var rel=he(10);
console.log(rel)
自执行函数(IIFE)
IIFE:
Immediately Invoked Function Expression
,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。
在规范当中函数表达式语句
,1不能以大括号开头({
),2不能以function关键字开头。
(function(){ //就是外面用一个括号把函数包起来,然后直接用括号调用。
console.log('foo');
})();
IIFE函数只能运行一次,因为引用类型没有标识符,执行完成之后会被JS的垃圾回收机制释放。
特点:
- IIFE函数只能调用一次。
- 定义之后马上调用,调用完成马上释放。
- 抓住特点:什么时候只用调用一次,后面就不用了。
- 初始化。
- 有些库、框架 JQuery。
递归函数(函数的递归调用)
- 函数的递归调用就是自己调用自己。
- 多次调用同一个函数他们的执行环境是不相同的,他们的执行环境中的变量也是互不影响的。
function say(a){ //递归推演。递是进去,归是出来。
console.log(a);
if(a>=1){
say(a-1); //此处又重新调用函数say
}
console.log(a); //输出3,2,1,0,0,1,2,3
}
say(3)
思路:这里每次调用的函数虽然是同一个函数,但是每次调用的时候产生的执行环境是不同的
say(3) console.log输出 3 3>=1 执行say(2),又返回去调用函数say(2),记住这里下面还有一个console.log(3)没执行呢
say(2) console.log输出 2 2>=1 执行say(1),又返回去调用函数say(1),记住这里下面还有一个console.log(2)没执行呢
say(1) console.log输出 1 1>=1 执行say(0),又返回去调用函数say(0),记住这里下面还有一个console.log(1)没执行呢
say(0) console.log输出 0 0>=1 false;不执行if ,直接执行下面的console.log(0);这时候say(0)执行完了,执行环境销毁了,接着归回去执行刚刚未执行的。
每归回去执行完以后都会销毁对应的执行环境。
感觉跟循环有点像,都是执行完再去执行下面的。
2.1-5累加求和(递归方式)
function say(a){
if(a>=1){
a+=say(a-1); //这里a=a+say(a-1) ,只执行到右边的say(a-1),左边的a=a+未执行
}
return a;
}
console.log(say(5));// 这里既调用又打印
思路:
say(5) 5>1 然后a=a+say(5-1),去执行say(4)去了,这里的a为5 还有a=a+未执行
say(4) 4>1 然后a=a+say(4-1),去执行say(3)去了,这里的a为4 还有a=a+未执行
say(3) 3>1 然后a=a+say(3-1),去执行say(2)去了,这里的a为3 还有a=a+未执行
say(2) 2>1 然后a=a+say(2-1),去执行say(1)去了,这里的a为2 还有a=a+未执行
say(1) 1>1 然后a=a+say(1-1),去执行say(0)去了,这里的a为1 还有a=a+未执行
say(0) 0>1 false 这时执行下面返回a ,所以say(0)返回值为0,这里执行完了销毁,返回去执行未执行的
say(1):a=a+say(0)=1+0 say(1)返回值为1
say(2):a=a+say(1)=2+1 say(2)返回值为3
say(3):a=a+say(2)=3+3 say(3)返回值为6
say(4):a=a+say(3)=4+6 say(4)返回值为10
say(5):a=a+say(4)=5+10 say(5)返回值为15,最后值为15
注意:
- 你自己就是这个函数,是你自己调用自己。当你做完一件事之后,会去做上一次没做完的事。
- 递归调用一定要有一个条件限制,否则你会一直调用自己,永不翻身。这就是无限递归。
- 递归调用适合用在不确定具体调用多少次的情况。
回调函数
什么是回调函数
回调函数的精髓:函数是对象类型的值,可以被当做参数传入到函数中。
函数传参时如果传入的是基本类型的值那是赋值,如果传入的是引用类型的值赋的是地址。
一个函数本身也是值,是一个对象,所以可以被当做实参传入到函数中,并且被执行。
回调函数被认为是一种高级的函数,一种被作为参数传入给另外一个函数(OF)的高级函数,回调函数会在OF内被执行或调用。
回调函数是什么:本质是一种模式,解决问题的套路。
回调函数的用处
- 通用化:简洁代码
- 事件监听和处理
- 设置超时和时间间隔。
- Ajax请求数据
- 框架中的各种生命周期函数
举例:
1.
function foo(fn){
console.log('!!!!');
console.log(fn); //这里fn为bar,所以打印的是bar函数
fn(); //这里实则是bar(),调用了bar函数。
}
function bar(){
console.log('is bar');
}
foo(bar); //调用foo函数,把bar函数当作实参赋给foo函数的形参fn(这里实则是把地址赋过去)
2.
function foo(){
function bar(){
console.log('foo --------------> bar');
}
return bar; //foo函数的返回值是bar函数。
}
var res = foo(); //调用函数foo,并将他赋给变量res,因为foo函数的返回值是bar,实则是将bar赋给了 res
res(); //这里即等于bar(),调用了bar函数
3.回调函数的实例
//循环0~指定的数,然后用 各种过滤规则 来进行过滤。
//和5取余为0的都去掉。
//最后一位为3的都去掉。
function getNumber(num,fn,tn){
for (var i = 1 ; i <= num ; i++) {
if (fn(i)&&tn(i)) {//1 //这里通过形参调用函数,fn(i)为rules2(i) tn(i)为rules1(i)
console.log(i);//1
}
}
}
//1. 定义和5取余为0的规则
function rules1(n){//1
var yu=n%5; //定义变量接收结果
if (yu == 0) {//1
return false;
} else {
return true;//true 函数的返回值将返回调用处 ,也就是上面的tn(i)
}
}
//2. 定义 最后一位为3的都去掉。 的规则
function rules2(n){
var pos = n / 1 % 10;//获得最后一位的值
if (pos == 3) {
return false;
} else {
return true;
}
}
getNumber(100,rules2,rules1); //这里调用函数getNumber,并把函数rules2,rules1作为实参传给形参
程序执行过程
堆和栈
堆、栈,数据结构中的内容。堆(链表结构)、栈(栈结构)。
程序运行的时候按照逻辑分为了堆内存和栈内存。
-
栈:栈结构中开辟的内存,比较小,速度比较快,操作系统分配并且自动回收。
特点:先进后出。
-
堆:堆结构中开辟的内存,比较大,速度比较慢。一般都是由程序员自己用的时候去申请,系统分配。如果要让内存回收,必须手动释放回收。
特点:先进先出
JavaScript中我们所有的内存几乎都是自动分配、自动回收的。JavaScript中封装了垃圾回收机制。
执行环境
JavaScript中运行、执行环境分为两种:
Global
,全局执行环境,JavaScript代码开始运行的时候默认的环境。Function
,函数执行环境。调用一个JavaScript函数时将会进入到一个JavaScript的函数执行环境。
每个执行环境我们也通常称为执行上下文。
代码在执行的时候会开辟出来一段栈空间
用来说明代码的执行顺序,这个栈叫执行栈。
- 当JavaScript代码执行时会先将全局执行环境压入到栈底。当执行(调用)到函数时会创建对应的函数执行环境,并将函数执行环境压入到栈底。
- 在函数执行完成之后将函数执行环境弹出并且销毁(和该执行环境相关的一切内容都会被销毁。)
- 一直到所有代码执行完成时才将全局执行环境销毁。
执行环境的阶段
每个执行环境分为两个阶段:
- 创建阶段(做准备工作)做预解析,变量提升
- 全局执行环境:一上来就进入了创建阶段。
- 函数执行环境:当函数被调用但是代码还没有真正执行之前。(创建变量对象,创建作用域链,确定this指向)
- 执行阶段(真正的解释、执行代码)
作用域及作用域链
-
什么是作用域?
作用域:变量起作用的区域范围。(变量能够访问的范围)
-
作用域的作用?
隔离变量(函数外部定义的变量和函数内部定义的变量即使同名也没关系)
-
作用域的确定时机?
作用域在进入到全局执行环境或函数执行环境时就已经确定好了。(函数定义时产生)
-
作用域种类?
ES5中作用域分为两种,全局作用域和函数作用域(局部作用域)。
函数作用域:就是指函数{}中间的范围。
全局作用域:就是指整个页面的范围
一执行JavaScript就进入到了全局执行环境就有了全局作用域。执行(调用)某个函数时进入到了函数执行环境就有了某个函数的作用域链。
同一个函数多次调用会产生不同的执行环境,每次调用都会产生一个。
解释:前面第一次调用函数生成的函数执行环境执行完已经被销毁了,后面再调用这个函数生成的是新的函数执行环境,所以函数作用域不一样了,既然函数作用域不一样,那么作用域链自然也不一样
全局变量和局部变量
- 全局变量:定义在全局执行环境中的就是全局变量(函数外部定义的,全局作用域中)
- 在整个程序所有的地方都可以起作用(整个程序中任何地方都可以操作全局变量)
- 全局变量可以被函数内部进行操作。但是全局中没有办法操作局部变量。
var c = 3;
function test(){
var a = 1;
var b = 2;
console.log(c);//输出3 ,这里打印了全局变量c
}
test();
console.log(b);//打印不了函数中的 局部变量b
- 局部变量:在局部环境当中的就是局部变量(函数内部定义的,函数作用域中)
- 只能在自己的作用域中起作用,局部变量只能在函数内部使用,外部不能使用局部变量。
function test(){
var a = 1;
var b = 2;
console.log(a);
}
test();
console.log(a);//调用不了test函数中的a,打印显示变量没定义。
-
在函数内部声明变量时,如果不使用var,那么默认声明了一个新的全局变量,外部也能访问到。如果函数有同名形参的话,则赋给形参。(形参还是局部变量)
var c = 3; function test(){ var a = 1; var b = 2; c = 30; //默认声明了全局变量c =30 } test(); console.log(c);// 先执行函数,函数中的声明了全局变量c=30所以这里打印的结果是30
作用域链
函数内部是可以嵌套函数的
function test(){
function test1(){
console.log('test1');
}
test1();
console.log('test');
}
test();
作用域是变量起作用的范围,作用域链是用来查找变量的这样一系列的过程。
一个函数在定义时会生成一个属性[[scope]]
,这个属性里面存储的是函数定义时的作用域的层级关系。
1.函数没被函数嵌套的话:[[scope]]
即为全局作用域
2.函数被函数嵌套的话:[[scope]]
等于外层函数的作用域(外面有几层函数就有几层作用域)+全局作用域
var a = 0;
function test(){
var a = 1;
function test1(){
console.log(a); //找到test中的a=1打印
}
test1();
console.log('test');
}
test();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F4bAJhXI-1610018155595)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200708190020388.png)]
-
作用域链是函数执行时创建的作用域链是由
当前的函数自身的作用域+[[scope]]属性
中的作用域组成的。 -
函数当前执行的作用域是作用域链的顶端,全局作用域是作用域链的最后端。访问顺序从顶端到最后端。
-
执行环境被销毁,作用域链也会被销毁。
-
函数不调用不执行,但是函数一定义就有了
[[scope]]
属性,作用域是在声明函数的时候已经确定了
总结:
作用域链由当前执行环境的作用域和[[scope]]
属性组成,整个作用域链在函数调用时才形成。
- 函数__调用__时生成了对应的执行环境。
[[scope]]
属性:函数__定义__时就已经生成了。函数即使不调用也会有这个属性。它其中包含的是这个函数的各个层级的作用域。
每个执行环境作用域链都是独立的作用域链。
查找过程:
- 先看局部作用域中是否有该变量,如果没有沿着作用域链开始往上层找。
- 如果上层能够找到则使用、设置同名的变量。(使用还是设置看你需求)
- 如果一直找到最后都没有找到,则从全局作用域中看有没有,如果有就使用、设置全局作用域中的同名变量,如果是没有使用时将会报错(
变量未定义
),设置时是向全局作用域中设置新的变量。
不带var也能定义变量,那是在全局,函数中不行。在函数中定义变量不带var就是在全局作用域中定义了一个变量(严格意义上说不加var不是定义变量。)。
内存中的数据储存
-
变量如果是基本数据类型(String、Boolean、Number、undefined、Null)会在栈内存开辟一段空间存储标识符,然后将基本数据类型的值也存储在栈内存中并且和标识符对应起来。
-
引用类型的值存储在堆内存中,存储引用类型的时候回给堆内存一个地址。但是标识符还是存储在栈内存中,为了能够让标识符和真正的数据结合起来在栈内存中存储的值是引用类型的地址。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eiVyWhbr-1610018155598)(D:/笔记案例/JS上课内容/7.8/day6-2/笔记、代码/day6_img/Snipaste_2020-07-08_14-15-07.png)]
查找变量和执行环境有关系,和堆没有关系。堆只是用来存储对象类型数据的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3BnNctYJ-1610018155600)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200708181001643.png)]
总结:
-
程序一开始执行,进入了全局环境,首先会创建全局环境并且压栈(执行栈),全局代码执行的时候依赖的是全局环境当中的东西。
全局变量如果是基本类型,那么这个值直接存在栈当中,如果这个变量的值是对象(函数、数组),那么函数和数组是要在堆内存中开始自己的空间专门存储(数据本身)。然后将堆里面的这块空间的地址存给栈当中对应的变量。
-
当程序执行碰到了函数调用,函数执行时也要有自己的环境去依赖。函数执行也是创建自己的函数环境进行压栈(函数执行环境一定压在全局执行环境的上面)。
局部变量,是函数环境当中存在的,只有函数执行,局部变量才会出现。函数执行完成之后,函数环境要弹出栈(销毁归还内存),局部变量也就不存在了。
-
当函数调用完成之后,会继续执行全局代码,一直到所有的全局代码都执行完成,代表程序执行结束,程序执行结束的时候,全局环境最后出栈(销毁)。
变量提升(预解析)
在执行环境的第一步(创建阶段时),会将带var的变量声明和函数的声明,放到所在的作用域顶端中,在执行阶段时,它的相关逻辑还是放在原来的位置进行赋值和其他的逻辑处理。
console.log(a)
var a=3;
变量提升后:
var a;
console.log(a); //所以输出undefined;
a=3
- 不管是全局执行环境还是函数执行环境每个执行环境都会对变量进行提升。
- 函数表达式不会被提升。函数构造器也不会被提升。
- 函数声明和变量都会被提升,但是函数被会先提升,然后才是变量。
- 变量提升覆盖不了同名函数和同名实参
例子:
alert(a); //全局作用域变量提升 //碰到fn(),fn()作用域变量提升 //第二个fn()变量提升
var a = 0; function fn(); alert(a);//全局0 var a;
alert(a); var a; a=1; //全局1 alert(a);//un
function fn(){ var fn; alert(a);全局1 a=0;
alert(a); alert(a);//undefined alert(a++)//0
a = 1; a=0;
alert(a); alert(a);//0
} fn();
fn(); fn=function()
var fn = function(){ fn()
alert(a); alert //全局1
var a = 0;
alert(a++);
}
fn();
alert(a);
对象
面向过程和面向对象
面向过程
以过程
为编程的中心
面向过程像流水线,一个阶段衔接另外一个阶段,每个阶段都有自己的输入、处理、输出。
最典型的面向过程语言:C语言。
面向对象
以对象作为中心的编程思想。
面向对象语言:Java Python PHP JavaScript
面向对象的优势
-
符合人类的思维习惯。
-
对于软件行业来说尤其是互联网行业,需求变更的比较频繁,而且复杂。
面向对象的思想核心就是
可扩展性
类和对象
-
类:一组相似事物的统称。
- 一组:是多个,不是单个。
- 相似:比较像,但不完全相同。
-
类的组成:
- 属性:类具有的特性(通常是名词)。
- 方法:类具有的功能(通常是动词)。
-
对象就是类的具体化,实物、实体,由类创建(实例)出来。
类就是模板,让多个对象拥有共同的属性、方法。
JavaScript中的对象
JavaScript当中没有具体的类,直接就是对象。而且在JavaScript中一切皆为对象。
对象:无序的键值对儿,它的值可以是基本值、对象、或函数。
数组:有序的键值对儿,它的值可以是基本值、对象、或函数。
对象声明方式
声明方式一:字面量方式
var p = {
};
console.log(p);
声明了一个空对象(所谓的空对象指的是没有我们自己定义的内容)。
字面量方式声明的对象由于多个键:值
对组成的,每个键:值
对之间使用逗号分隔。
属性的值如果是字符串的话要加引号。
//age、name、sex都是名词,可以认为是属性、是对象的特征。
//对象属性的值也可以是函数,因为函数本身也是数据。这个时候可以称该属性为方法。say 动词
//javascript中函数永远不会单纯的属于这个对象,它们只是对于相同函数的引用。
var p = {
name:'yanhaijing',
age:18,
sex:'nan',
say:function(){
console.log('闫海静真英俊!');
}
};
console.log(p);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xy2fFdP8-1610018155603)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200710193830243.png)]
注意:
-
键是字符串并且不加引号(加上也行),键是其他类型也会被转换为字符串。
对象的属性名都是字符串类型
var p = { 1:'hehe', undefined:'hello', null:'world' }; console.log(p);
-
如果键名不符合变量命名规则那就必须得加上引号。
var p = { name:'yanhaijing', 'my Category':'person', '1a':'hello' }
-
对象的属性的值也可以是对象。
var p = { p:{name:'yanhaijing'} } console.log(p);
声明方式二:Object构造函数
写在object后面的括号里
var p = new Object( //object构造函数
{
name: 'yanhaijing',
age: 18,
sex: 'nan',
say: function () {
console.log('闫海静真英俊!');
}
}
);
console.log(p);
console.log(p.say())//调用函数
new是操作符标识调用后面的内容,创建一个对象。 (new Object()
,可以__暂时__将Object
看成一个类,它就创建了一个Object类型的对象),参照Array类型的数组
-
字面量方式创建对象,目的就在于简化创建包含大量属性的对象的过程。(字面量方式在内部也会调用
new Object
) -
我们声明Object类型的对象时候一般使用字面量方式定义。
-
object类型的对象它是一个__万能的对象__它可以根据其中的属性、方法存储任何的东西。
-
Object类型
的实例是个万能的对象,它可以根据其中的属性、方法,存储任何东西。鸭子类型
//鸭子
var duck = {
duckSinging:function(){
console.log('嘎嘎!');
}
}
//鸡
var chiken = {
duckSinging:function(){
console.log('嘎嘎嘎');
}
}
//都加入了合唱团。
var choir = [duck,chiken];
//开始合唱
for (var i = 0 ; i < choir.length ; i++) {
choir[i].duckSinging(); //调用对象方法的值(函数)
}
鸭子类型指导我们只关注对象的行为,不关注对象本身,只要能达到我的需求,管你是什么对象。
工厂模式声明对象(封装)
以Object和对象字面量方式声明对象最大的缺点就是:代码重复,会产生大量重复的代码。
var p1 = {
name:'yangxianpeng',
age:18,
sex:'nan'
}
var p2 = {
name:'pengjinli',
age:73,
sex:'douxing'
}
//这样声明对象就很烦
换种思路:
function createPerson(name,age,sex) {
var o = new Object();//var o={}
o.name = name;
o.age = age;
o.sex = sex;
return o;
}
var p1 = createPerson('yangxianpeng',18,'nan');
var p2 = createPerson('pengjinli',17,'douxing');
console.log(p1);
console.log(p2);
console.log(p1 == p2);
console.log(p1 instanceof Object)
//1.就是声明一个函数,在函数内部声明一个对象以及属性。
//2.设置函数的返回值为这个对象
//3.再把函数的行参设为属性的值
//4.通过函数的实参传参把属性值传进去
//5.函数的返回值是对象,把对象赋给变量p1,p2打印
instanceof操作符
用来检测对象是什么类型的。(现在先这样认为。)
格式:对象 instanceof 类型
//这个对象是 对应的这个类型吗? 返回值是true或false。
我在程序当中有可能需要知道这个对象具体是什么类型的。现在都是Object类型。Object类型是万能的对象。
我就需要想办法知道某个对象的具体类型:
引出下面
构造函数调用方式声明对象
构造函数:就是普通的函数,但是使用了new
操作符调用,这个时候通常称为构造函数,实际上不存在所谓的构造函数,这种方式应该叫函数
的构造调用
。(调用时会生成一个相应类型的对象)
定义要构造调用的函数时一般首字母大写(小写也行,但是大写是惯例)。
通过new关键字可以创建一个与函数类型相同的对象
new Object()会创建一个Object类型的对象
new Person() 会创建一个Person类型对象
new Array() 会创建一个 Array类型的对象
1.创建一个StuInfo类型的对象
function StuInfo(sName,sAge){
this.stuName = sName;
this.stuAge = sAge;
this.sayHi = function(){
console.log('Hi' + this.stuName);
}
}
//调用函数
//var stu1 = StuInfo();undefined;构造函数如果直接调用,返回undefined,因为构造函数中一般都不写return;即没有返回值;
var stu1 = new StuInfo('柳岩',31);
console.log(stu1);
var stu2 = new StuInfo('扎送',34);
console.log(stu2);
console.log(stu1 instanceof StuInfo)//true
1.不用创建对象,new调用时会创建对象
2.this暂时理解为当前正在实例的这个对象
3.要用new调用
注意:其实四种方式都算构造函数生成对象(前三种都有new object())
总结:
-
你每使用构造函数生成一个对象的时候都会在其中包含一个叫
constructor
的属性。这个属性中存储的是一个指针,指向了生成该对象的构造函数
。 -
字面量方式生成的对象,也有
constructor
属性,其指向的是Object
函数。(里面也调用了new object()) -
Object()
、Array()
、Function()
他们都是属于系统内建的。 -
构造函数中可以使用return语句,如果return的是基本类型的数据,那么return后面的值将会被忽略,最终得到的还是新创建的这个对象。
-
构造函数中返回的是其他对象。那么就返回这个对象。
例如:
function Person(name,age){ this.name = name; this.age = age; return { name:'刘谭松', age:12, say:function(){ console.log('我已经不是三岁小孩了。'); } } } var p = new Person('yanhaijing',18); console.log(p); //这里返回的是return后的新对象
函数既是对象又是可执行的代码块:
- 在对象方面来说它也拥有属性和方法。
- 从代码块方面来说,它既可以执行特定的代码块(普通函数),又可以用来创建特定类型的对象(函数的构造调用)
- 普通函数,没有return返回的是undefined。有return返回的是指定的值。
- 函数的构造调用,没有return,返回的是
新生成的对象
。有return如果返回的是基本类型就忽略,还是返回的新生成的对象,如果返回的是引用类型那么得到的就是这个引用类型。
对象的基本操作
访问对象中的属性
-
使用
.
表示法访问对象的属性。 -
使用
[]
表示法访问对象的属性。最大优点就是可以通过变量、表达式来访问属性。([ ]访问要加双引号)console.log(dog['color']);//对象['属性名'] console.log(dog.color);
可以在中括号中放置表达式,中括号中的表达式、变量都会进行运算。
var color = 'cat'; console.log(dog[color]);//dog['cat'] console.log(dog['col' + 'or']);
如果键不符合标识符命名规则需要使用
[]
来进行访问,不能使用使用.
访问。var p = { name: 'yanhaijing', 'my Category': 'person', '1a': 'hello'//键名不符合标识符命名规则。 } console.log(p['1a']);
如果你访问的属性不存在,代码会返回undefined。是不会报错的。
增加、修改属性(有就修改,没有就增加)
如果添加、修改的键名不符合标识符命名规则那么只有使用中括号
。
添加属性:向对象中添加一个不存在的属性,其实就是往对象中添加属性
对象.属性名=属性值
对象["属性名"]=属性值
var dog={
name:"旺财",
sex:"man",
age:20,
"1a":25
}
dog.color="yellow";
dog["color"]="red";
删除属性
格式:delete 对象.属性
delete dog.age;
var 和不带var有什么区别:
-
window对象是JS中的最顶层对象。你在全局中声明的变量(带
var
的)会被作为window对象的属性(变量)。 -
不带var的变量也会被作为window对象的属性。(不是变量)它只是window对象的属性。
-
var声明的全局变量不能使用delete删除,但是不带var声明的
所谓的全局变量
可以使用delete删除。因为它只是属性
原型链
作用域链:描述查找变量的过程。
原型链:用来描述通过隐式原型对象查找属性的过程。
函数的prototype属性
-
每个函数在创建的时候都会创建一个prototype属性,这个属性叫
原型属性
,该属性中存储的是一个对象的引用(默认的时候是一个空对象) -
在原型属性对应的对象中也有一个属性叫constructor属性,指向的是拥有这个原型对象的构造函数。(注意这里指向的函数是构造函数调用的函数,不是object函数)
console.log(Person.prototype.constructor===Person )//true console.log(Person.prototype.constructor===Object )//false
-
prototype属性只有在函数被当做构造函数的时候(函数的构造调用)的时候才会有效。它的作用:由同一个构造函数生成的各个实例对象之间共享属性和方法。
实例对象的__proto__
属性
当调用构造函数创建一个新实例对象之后,该实例内部将包含一个内部属性__proto__
,指向构造函数的原型对象。
构造函数中的prototype属性和构造函数实例化出来的对象的__proto__
指向的是同一个对象。如下:
function Person(){
}
var p = new Person();
var p1 = new Person();
console.log(Person.prototype == p.__proto__); //true;Person.prototype是Person函数的原型对象
console.log(p.__proto__ == p1.__proto__); //true ;都指向同一个原型对象(Person.prototype)
基础原型链图:(图中的object是函数, Person.prototype既是Person的原型对象也是object函数实例化出来的一个对象)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zo8fD2Bw-1610018155605)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200711191606024.png)]
先在本对象中查找对应的属性,如果有就用了,如果没有那么就去该对象的__proto__
指向的对象(构造函数的原型对象)中去查找,如果有就用了。(按着__proto__
的指向一路找)
function Dog(){
this.name = '旺财'; //属性是实例对象私有的
}
Dog.sex="男" //Dog的静态属性,实例对象访问不到
var d1 = new Dog();
var d2 = new Dog();
Dog.prototype.say = function(){ //往原型对象中加了一个属性(方法)say,方法是公有的。
console.log('wang wang!');
}
d1.say();// d1中没有say这个属性,就往Dog.prototype(原型对象)中去找,找到就用了。
d2.say();
在原型中查找值的过程是一次搜索,对原型对象所做的任何修改都能够立即从实例上反映出来。(先创建的实例然后修改原型也是一样的。)如上。
原型分为:显式原型和隐式原型
- 显式原型:就是构造函数的prototype属性。
- 隐式原型:就是调用构造函数实例化出来的对象的
__proto__
属性。
prototype
和__proto__
都指向了同一个对象。因为在ES5的时候只允许操作prototype属性
,不允许操作实例对象__proto__
属性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5o2lOApZ-1610018155606)(C:\Users\宋文现\AppData\Roaming\Typora\typora-user-images\image-20200711203525638.png)]
在实例化对象上找属性时,过程是先从自身找有没有这个属性,如果有就直接使用了,如果没有就沿着这个对象的隐式原型对象(__proto__
所指向的对象),继续找。如果能够找到就使用如果找不到就去找隐式原型对象中的__proto__
指向的对象,一直按照这种查找方式,直到最后找的是Object显式原型对象( Object.prototype),如果到这里都没有找到那就只能返回undefiend。
原型对象中的属性是给构造函数实例化出来的对象服务的,构造函数本身是不能使用的。
但是如果是Object的原型对象Object.prototype和Function的原型对象Function.prototype上的属性是可以使用的
function Person(name, sex) {
this.name = name;
this.sex = sex;
}
Person.prototype.say = function(){
console.log('Person.prototype.say');
}
var p = new Person('张三', 'nan');
Person.say();//报错,找不到say方法。
原型链Obejct和Function的双重身份
Obejct即是构造函数,也是Function实例化出来的对象,如下:
function Function(){
}
var Obejct = new Function()
所以Obejct作为对象的__proto__
指向的是构造函数的原型对象,即为Function.prototype
所有函数都是Function的实例, 包含它自己(Function是通过new自己产生的实例),所以Function作为实例化对象时,其__proto__
指向的是自己的原型对象Function.prototype
instanceof 操作符
格式:对象 instanceof 函数
//这个对象是 对应的这个函数的实例化对象(或类型的对象)吗? 返回值是true或false。
-
A instanceof B
instanceof运算符
的第一个变量是一个对象,暂时称为A,第二个变量一般是一个函数,暂时称为B。- instaneof判断的规则:沿着A的
__proto__
这条线来找,同时沿着B的prototype这条线来找,如果两条线能够找到同一个引用,既同一个对象,返回true,否则返回false。(即如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false)
-
所有的引用类型都是Object函数的实例(其实Object也是Function实例对象),所以在检测引用类型时和Object时(使用instanceof操作符检测)都会返回true。
console.log(stu1 instanceof StuInfo ) //true console.log(stu1 instanceof Object ) //true
function Foo() { } var f1 = new Foo(); console.log(f1 instanceof Foo);//true console.log(f1 instanceof Object);//true //案例2 console.log(Object instanceof Function)//true console.log(Object instanceof Object)//true console.log(Function instanceof Object)//true console.log(Function instanceof Function)//true console.log(Object instanceof Foo)//false console.log(Foo instanceof Function) //true console.log(f1 instanceof Function)//false console.log(Function.prototype.__proto__ === Object.prototype)//true
-
如果是基本类型,基本类型不是对象,所以在使用instanceof操作符时会始终返回false。
关于原型链的面试题
var A = function () {
}
A.prototype.n = 1
var b = new A();
//原型链在实例化的时候就已经确定好了
console.log(b);
A.prototype = {
n: 2,
m: 3
}
var c = new A()
console.log(c);
console.log(b.n, b.m, c.n, c.m)//1 undefined 2 3
var F = function () { };
Object.prototype.a = function () {
console.log('a')
};
Function.prototype.b = function () {
console.log('b')
};
var f = new F();
f.a()//a 可以找到Object.prototype
// f.b()//报错
F.a()//a
F.b()//b
for…in遍历 对象
for...in
用来遍历自身和原型链中可枚举的属性。
-
数组、函数都是对象,可以添加属性。同样,添加上属性之后还可以继续维持本来的功能。
-
数组是对象,数组对象中的索引(下标)本质上来说就是属性。length也是属性。
//遍历数组(不要用知道就好) var a = [1,2,3,4] for (var pro in a){ // var pro 可以自定义 console.log(pro); //遍历下标即属性 console.log(a[pro]);// 遍历元素的值 } //遍历对象 function Person(){ this.name = 'pengjinli'; this.sex = 'nan'; this.age = 18; } Person.prototype.say = function(){ } var p = new Person(); //不光对象本身的属性能够被遍历出来,在对象的原型链上的属性也会被遍历出来。 for (var pro in p) { console.log(pro); //打印 name,sex,age,say }
-
一些对象中有些属性是不可以遍历出来的,你可以理解为
一般的时候用不到的属性
-
千万不要用
for...in
遍历数组,虽然数组也是对象,但是数组的索引只是具有整数名称的枚举属性,for...in
不能保证遍历的顺序,而数组最重要的特征就是顺序(有序)。
关键字in
可以判断对象中是否包含某个属性
语法:
var res = usrKey in 对象;
in右边必须是对象;
放回一个bool值,表示usrKey是否存在于对象中;
那怕属性值是undefinef,in检查都会返回true;
如果用delete删除了属性,in检查是会返回false
var obj = new Object();
obj.usrName = 'james';
obj.usrAge = 18;
obj.usrGender = true;
console.log('usrName' in obj); // true
console.log('usrHi' in obj); // false
this指向
前面的时候说this指代的是__当前正在实例的这个对象__,这种说法有些片面。this最终代表的是函数的执行者。
-
全局执行环境中的this。就是window。
console.log(this); //打印window
-
如果是__光秃秃的__函数调用this指向的是window(全局)。
function test(){ function test1(){ console.log(this);//window console.log('@@@@'); } test1(); } test();
-
如果是通过对象的方法调用的,this指向这个对象。
var name="全局的属性"
// function fun(){
// console.log(this.name);
// }
var dog={
name:"旺财啊",
sex:"man",
age:20,
//say:fun
say:function(){
console.log(this.name)
}
}
var fun=dog.say
fun(); //函数调用this指向全局的name,相当于全局中的name
dog.say();//方法调用this指向调用它的对象,相当于dog.name
坑记住
var a = 1;
var foo = {
a: 2,
bar: function () {
console.log(this.a);
}
};
(false || foo.bar)();// 1 这里是个坑还讲不了现在
(foo.bar = foo.bar)(); // 1
apply、call方法
每个函数对象都有call
和apply
两个方法,它的本质:设置函数体内的this对象
的值。
call(thisArg,arg1,arg2.....)
apply(thisArg,[argsArray])
-
括号前面1个是设置函数体内this的指向对象,后面是函数调用传入的实参
-
call调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
-
apply调用的时候,参数必须是一个数组,然后再执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
-
如果第一个参数指定了null或者undefined则内部this指向window
call方法
var obj = {
name:'sex',
say:function(){
console.log(this);
console.log(this.name);
}
}
obj.say(); //函数调用内部打印出整个Obj对象 打印出sex
var obj1 = {
name:'hello',
sex:'nv'
}
obj.say.call(obj1);//强行的讲obj.say中的this设置为了obj1 所以函数调用内部打印出整个Obj1对象 打印出hello
apply方法
function Demo(name){
this.name = name;// 这里等于obj.name = yanhaijing;
}
var obj = {sex:'nan',age:18};
Demo.apply(obj,['yanhaijing']);//设置this指向0bj,
console.log(obj); //打印{sex:'nan',age:18,name:'yanhaijing'};
作用:
- 借用其他函数的方法,让本对象可以使用。
- 让其他构造函数中的属性变成自己的。细思极恐啊
new运算符的流程
流程:
- 创建对象,开辟内存空间(堆);
- 设置原型链
- this指向该内存。
- 执行函数代码
- 将创建对象实例返回。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E0Cr1ZeN-1610018155608)(D:/笔记案例/JS上课内容/7.11/day8-all/笔记、代码/4.对象_img/image-20200711163831171.png)]
基本类型、引用类型判断类型
-
基本类型判断使用typeof
typeof的返回值:
- undefiend,未声明或声明未初始化的变量。
- bollean,布尔值
- string,字符串
- number,数值
- object,对象或null
- function,函数。
-
其他对象的类型判断
console.log(b instanceof Object); console.log(b.constructor == Test);//true ,生成b对象的构造函数是Test
-
是否是同一个对象,用
==
,判断的是是否是同一个地址。
JSON
JSON对象是ES中内置的对象,不用定义,直接就能用。
大写的JSON表示的是js中内置的对象,小写的json表示的是一种数据格式。
-
json是一种数据格式;我们以后大多数时候都是通过json数据格式进行前后端数据交互。
-
json本质上就是字符串,简称json串。
后台往前台传递数据时,要传json格式的数据。
json的值
可以表示三种类型的值。
-
简单的值:使用与JavaScript相同的语法,可以在JSON中表示字符串、数值、布尔值、null。
5 "Hello world" null false
-
对象:表示的是一组无序的键值对儿。
json中对象要求给属性加上双引号。
{ "name":"yanhaijing", "age":29 }
-
数组:数组也是对象,表示一组有序的键值对儿。
[25,"hi",true] [ { "name":"张三", "age":28 }, { "name":"李四", "age":30 } ] {"success":false,"error_msg":"不是有效的话题id"}
json数据处理
JSON对象中有两个方法:
-
JSON.stringify(值)
,将JavaScript对象序列化为JSON字符串(用来向后多发送数据时)。JSON.stringify输出的JSON字符串不包含任何的空格字符串和缩进。
序列化JavaScript对象时,所有的函数、原型中的成员、undefined属性都会被忽略。
var person = { name: 'pengjinli', age: 19, sex: 'nan', friends: [ 'zs', 'ls', 'ww', 'ztc', 'lgd' ], say: function () { console.log('pengjinli 说: 闫海静真丑!'); }, money: undefined } console.log(JSON.stringify(person)); 打印{"name":"pengjinli","age":19,"sex":"nan","friends":["zs","ls","ww","ztc","lgd"]}
-
JSON.parse(JSON字符串)
,将JSON字符串解析为原生的JavaScript值(用来得到后端的数据转换为我们使用的JavaScript数据)。一旦传递给
JSON.parse
的字符串不是有效的JSON就会报错。
console.log(JSON.parse('abc')); //报错
console.log(JSON.parse("abc")); //报错
console.log(JSON.parse('"abc"'));//这个是正确的。
math
ECMAScript还为数学计算提供了全局的内置对象,即Math对象。
-
将小数转换为整数,如果是整数则不改变。
-
Math.ceil()
,进一取整。 -
Math.floor()
,舍一取整。 -
Math.round()
,执行标准的四舍五入。var num1 = 1.1; var num2 = 1.9; //math.ceil console.log(Math.ceil(num1)); //2 console.log(Math.ceil(num2)); //2 //math.floor console.log(Math.floor(num1)); //1 console.log(Math.floor(num2)); //1 //math.round console.log(Math.round(num1)); //1 console.log(Math.round(num2)); //2
-
-
进行数学计算
-
Math.min()
,获得最小值。 -
Math.max()
,获得最大值。 -
Math.abs()
,求绝对值。 -
Math.pow(num,次方)
,进行幂运算。 -
Math.PI
,圆周率,这是一个属性。var arr = [1,2,3,4,5,6,10]; console.log(Math.max.apply(null,arr)); //10 最大值 console.log(Math.min.apply(null,arr)); //1最小值 console.log(Math.max(...arr)); //绝对值 console.log(Math.abs(-1)); console.log(Math.abs(1)); //幂运算 console.log(Math.pow(4,3));//表示4的三次方 //圆周率 console.log(Math.PI);//这是个属性
-
Math.random()
,返回大于等于0小于1的一个随机数。Math.floor(Math.random() * (可能的最大值 - 可能的最小值 + 1) + 可能的最小值)
案例:验证码
//验证码中的内容要有一个范围。 a-zA-Z0-9 4
var str = "abcdefghjklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; var code = ''; //字符串拼接 for (var i = 1; i <= 4; i ++) { //4位数验证码 //随机出来的下标ind var ind = Math.floor(Math.random() * (str.length - 1 - 0 + 1) + 0); code += str[ind]; } console.log(code);
-
Date
Date是javascript中内置的函数(构造调用的),它用来处理日期和时间。
Date既然是函数就有两个角色(函数:可以运行的代码。对象:其中包含属性和方法)
时间戳
是一个整数,用来进行计算。
从1970年1月1日0:0:0(计算机元年)~现在的毫秒数。
1秒等于1000毫秒,1毫秒等于1000微秒
Date.now()
,获得当前的时间戳。
当做构造函数来使用
格式:new Date();
得到当前的时间
new Date(年,月,日[,时[,分[,秒]]]);
时间处理方法
getFullYear()
,获得4位的年份。getMonth()
,获得月份。 其中0表示一月,11表示12月。 //0~11getDate()
,获得月份中的天数。//1~31getDay()
,获得星期。0表示周日 16表示的是周一周六。getHours()
,获得小时。 //0 ~23getMinutes()
,获得分钟//0~59getSeconds()
,获得秒数。//0~59
案例
- 获得当前的日期和时间,格式为:
yyyy-mm-dd H:i:s
function formatDate(){
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var nDate = date.getDate();
var day = date.getDay();
//获得对应的周几
switch(day){
case 0:
day = '周日';
break;
case 1:
day = '周一';
break;
case 2:
day = '周二';
break;
case 3:
day = '周三';
break;
case 4:
day = '周四';
break;
case 5:
day = '周五';
break;
case 6:
day = '周六';
break;
}
var hour = date.getHours();
var minutes = date.getMinutes();
var second = date.getSeconds();
var str = year + '-' + month + '-' + nDate + ' ('+ day +') ';
str += hour + ':' + minutes + ':' + second;//字符串太长拼接两次
console.log(str);
}
formatDate();
包装对象
str是字符串但可以调用length
属性,返回字符串的长度。
var str="abc";
console.log(str.length); //3
在JavaScript中,提供了三个特殊的构造函数Number
、String
、Boolean
,函数可以使用new操作符来进行操作。 当你new 这些函数之后会生成对应类型的对象。
var str = new String('abc');
console.log(str); //变成string类型的对象
上面确实是一个对象,而且有__proto__
属性指向原型对象,原型对象中有大量的方法。
包装对象
实际开发中不用,说这三个函数的原因是因为当你把number、boolean、string当做对象调用时时会调用对应的构造函数来生成一个对象。
var str= 'abc';
console.log(str.length);//是当做对象来调用的。
console.log(str);//还是普通值 abc
执行过程:
- 声明了一个字符串放入了str中。
- 当发现你把str当做对象调用时,js内部会自动调用
new String('abc')
创建了一个对象(字符串对象,包装对象); - 然后使用包装对象的属性、方法。当使用完成之后,又会将包装对象转换成字符串。
number、boolean、string都会经过上面的过程。
普通对象和包装对象的区别:主要是针对对象的生命周期。
- 使用new 操作符创建的引用类型的实例,在执行流离开之前的作用域中将会一直存在。
- 自动创建的基本包装类型对象,只存在于那一行代码的执行的一瞬间,然后立即销毁了。
字符串方法
ES5方法
-
charAt(),根据传入的位置取得其所在的字符。
var string = 'hello world!'; console.log(string.charAt(6));//()里的值也跟下标一样,从0开始,空格也算 console.log(string[1]);
-
charCodeAt(),根据传入的位置取得其所在的字符编码(unicode码)。(不常用)
var string = 'hello world!'; console.log(string.charCodeAt(1));// e 的 编码是多少。 打印101
-
String.fromCharCode(),根据传入的unicode码,转换为相应的字符。返回转化后的字符。
console.log(String.fromCharCode(101));// 打印 e
-
concat(),将一或多个字符串拼接起来返回拼接的到的新字符串。
var str1 = 'abc'; var str2 = 'efg'; var res = str1.concat(str2); console.log(res); //abcefg console.log(str1 + str2);//这也效果也一样
-
indexOf(searchValue[,offset]),从一个字符串中向后搜索给定的子字符串,然后返回子字符串的位置(如果没有找到该子字符串,则返回-1)。第二个参数可以指定从哪开始(默认从0位置开始查找);(常用)
var str = 'helloe'; console.log(str.indexOf('e'));// 返回1 ,从0开始找,两个e找前面的位置,没有则返回-1 //第二个参数用来设置查找的开始位置。默认0开始,返回位置 console.log(str.indexOf('o',4)); //返回4
-
lastIndexOf(),从一个字符串中向前搜索给定的子字符串,然后返回子字符串的位置(如果没有找到该子字符串,则返回-1)。
console.log(str.lastIndexOf('l')); //从后面开始找,返回位置还是顺序的
-
replace()方法,替换子字符串。(其他功能讲正则的时候说)将某个字符串置换成某个字符串。
var str = 'cat,bat,sat,fat'; var result = str.replace('at','ond');//将at替换成ond,只能替换一次(只有cat替换了),多次的话不能自动替换。 console.log(result);
-
slice(beginSlice[,endSlice]),提取一个字符串的一部分,返回一个新的字符串。beginSlice从0开始,endSlice可以省略,如果省略endSlice会一直提取到字符串末尾。(beginSlice是开启的位置,endSlice是结束的下标的----但结果不会包含结束的下标)—用的较多。(按下标取字符串)
var str = 'hello'; console.log(str.slice(1,2)); //e 从1开始到2截取但不包含2 (根据下标截取)
-
substr(start[,length]),返回一个字符串中从指定位置开始到指定字符数的字符。start,提取字符的位置,索引从0开始。length提取的字符数,length的最大值为字符串的长度减去1。省略length会从起始位置一直到字符串结束位置(按长度取字符串)
console.log(str.substr(3,2));//lo 从3开始截取2位 (根据长度截取)
-
split(),基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。(常用)
第一个参数如果不写的话会将整个字符串放入到结果数组中。
如果第一个参数是空串,则会将每个字符都作为数组的一个元素。
第二个参数用于指定分隔后的数组的大小,用来确保返回的数组不会超过该大小。
var colors = "red,blue,green,yellow"; console.log(colors.split()); //不写打印一个长度为1的数组 只有一个元素["red,blue,green,yellow"] console.log(colors.split('')); //空串 打印每个字符都是一个数组元素 console.log(colors.split(',', 2)); //字符串按字符串里的逗号分隔,数组长度为2
-
toLowerCase(),将字符串转换为小写。
-
toUpperCase(),将字符串转换为大写。
var colors = "red,bLue,greEn,yellow"; console.log(colors.toLowerCase()); console.log(colors.toUpperCase());
-
valueOf(),返回对象的字符串、数值或布尔值表示。如果是包装类型返回包装类型的值,如果是对象则返回对象本身。
如果是包装对象取得是
__proto__
原型对象中的valueOf(),返回的是基本值。如果是其他的对象类型,取得是Object构造函数原型对象中的valueOf(),返回的是对象本身。该方法没有参数
返回值:如果是包装对象,返回的是包装对象中的基本值;如果不是包装对象类型,返回的是对象本身。
-
toString(),返回对象的字符串表示。
包装对象和非包装对象都可以使用这个方法,包装对象和非包装对象调用的也不是同一个
toString()
方法。是包装对象取得是
__proto__
原型对象中的toString()
,如果是其他的对象类型,取得是该对象的__proto__
原型对象的toString()
该方法没有参数
返回值:如果是包装对象,返回的是包装对象中的基本值转换为字符串;如果不是包装对象类型,根据对象类型的不同返回值也不同。
对象分为包装对象和非包装对象。都可以使用valueOf和toString方法。s
- valueOf:用来取得对象中的基本值。
- 包装对象:调用的都是各自的valueOf,用来获得基本值。[[PrimitiveValue]]
- 非包装对象:调用的是同一个valueOf,Object原型中的。获取不到基本值,返回的还是对象本身。
- toString:用来把对象转换为字符串
- 如果是包装对象,转换的时候将基本值拿出来转换为字符串。
- 非包装对象,转换的时候根据对象的不同,结果也不同。
- 数组,返回的是去掉中括号之后的列表。
- 函数,返回的是函数本身转换为字符串。
- 如果是普通对象,返回的是
[object Object]
字符串。
- valueOf:用来取得对象中的基本值。
var num = new Number(123);
//[[PrimitiveValue]]: 123 只有包装对象才有,表示的是基本值,如果不是基本对象 就没有基本值。
console.log(num.valueOf());//基本类型的valueOf拿的是基本值([[PrimitiveValue]]: 123)
var obj = {};
console.log(obj.valueOf());//不是包装对象,返回的就是对象本身。
//toString(),返回对象的字符串表示。
var num = new Number(123);
//如果是包装对象,返回的是包装对象中的基本值转换为字符串。 [[PrimitiveValue]]: 123
console.log(num.toString());
var obj = {};
//普通的对象返回的是[object Object]
console.log(obj.toString());
var arr = [1,2,3];
//如果是数组返回的是去掉中括号之后的列表。
console.log(arr.toString()); //1,2,3
function test(){
}
//函数返回的是函数的本身转换为字符串。
console.log(test.toString());
- match(),根据正则来匹配指定的字符串。
- search()方法,返回字符串中第一个匹配项的索引,如果没有找到则返回-1。该方法是中从字符串开头向后查找模式。
ES6方法
-
includes(string,n),返回布尔值,表示是否找到了参数字符串。
var s = 'hello world!'; console.log(s.includes('o')); // true console.log(s.includes('w',4)); //true ,第二个参数是从下标位置4开始找
-
startsWith(string,n),返回布尔值,表示参数字符串是否在源字符串的头部。
-
endsWith(string,n),返回布尔值,表示参数字符串是否在源字符串的尾部。
-
repeat(N),返回一个新字符串,表示将原字符串重复N次。返回新字符串。
var str = '闫海静真英俊!'; console.log(str.repeat(100));
-
padStart(length,str),如果没有到达length的长度使用str在头部进行填充。(可以用于前面补0的操作)
如果str与原字符串长度之和超过了指定的最小长度,会截去超出的位数。
如果原来字符串的长度等于或大于指定的length,原样返回。
如果第二个参数省略,会用空格来进行补全。
-
padEnd(length,str),如果没有到达length的长度使用str在尾部进行填充。
var x = 'x';
var newX1 = x.padStart(5,'ab');//ababx
console.log(newX1);
var x = 'x';
var nx1 = x.padEnd(5,'ab');
console.log(nx1);//xabab;
console.log(x.padEnd(4,'ab'));//xaba
- 去空格
- trim():删除字符串两端的空白符
- trimStart() 去除首部的空格
- trimEnd() 去除尾部的空格
//字符串是基本类型值,一般的方法只能返回新值,而不能改变字符串
let message = " hello world ";
log(message);
log(message.trim());
log(message.trimStart());
log(message.trimEnd());
math补充
//指数运算符 求一个数组的n次方
{
//es5
console.log(Math.pow(2, 10));
console.log(Math.pow(32, 5));
//es6
console.log(3 ** 3); //3的3次方
console.log(2 ** 10); //2的10次方
// 计算顺序 先计算右边 在依次计算
console.log(2 ** 3 ** 3);
}
{
//去掉小数部分
console.log(Math.trunc(3.12)) //3
//判断数字 正--》1 负--》-1 0--》0 NaN--》NaN
console.log(Math.sign(3.12)) //1
console.log(Math.sign(-3.12)) //-1
console.log(Math.sign(0)) //0
console.log(Math.sign(NaN)) //NaN
//平方根
console.log(Math.sqrt(4)) //2
//立方根
console.log(Math.cbrt(27)) //3
//求所有参数的平方和的平方根 3平方+4平方=5平方 5是3和4的平方和的平方根
console.log(Math.hypot(3, 4)) //5
}
数组方法
既然以Array.prototype开头的那就说明 是给Array的实例对象使用的。
ES5方法
数组末尾增加元素push
-
Array.prototype.push(),接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
var arr = ['red','blue','green']; arr.push('black',"yellow"); //末尾加任意个数 console.log(arr);
数组末尾删减pop
-
Array.prototype.pop(),从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。
var arr = ['red','blue','green']; console.log(arr.pop()); //返回green console.log(arr); // red,blue
数组头部加任意个数元素unshift
-
Array.prototype.unshift(),在数组前端添加任意个数选项并返回新数组的长度。
var arr = ['red','blue','green']; console.log(arr.unshift('pink','hotpink')); //返回新数组长度5 console.log(arr); //p,h,r,b,g
数组头部删减元素shift
-
Array.prototype.shift(),移除数组中的第一个项并返回该项,同时将数组长度减1。
var arr = ['red','blue','green']; console.log(arr.shift()); //返回red console.log(arr); // blue,green
splice(删除,替换,插入)
-
Array.prototype.splice(),向数组的中部插入项。
整体格式:
arr.splice(起始项,删除的个数,要插入的项目1,要插入的项目2......)
-
删除,可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数,返回删除的项组成的数组,原数组受到更改。
-
替换,可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需要指定3个参数:起始位置、要删除的项数、要插入的任意数量的项(插入的项数不必与删除的项数相等)。
-
插入,可以向指定位置插入任意数量的项,只需要提供3个参数:起始位置、0(要删除的项数)、要插入的项目(要插入的项目可以传入任意多个项)。
//删除 var colors = ['red','green','blue','yellow']; var delColors = colors.splice(0,2); //从0下标开始删除2项 console.log(delColors); //返回删除的项组成的数组 [red,green] console.log(colors); // 原数组变成[blue,yellow] //替换 var colors = ['red','green','blue','yellow']; var replaceColors = colors.splice(1,1,'pink','black'); //可以删除1个插入2个 console.log(replaceColors); //返回替换的项green console.log(colors); // r,p,b,b,y //插入 var colors = ['red','green','blue','yellow']; colors.splice(1,0,'pink','white'); console.log(colors); //red pink white green blue yellow
-
-
Array.prototype.concat(value1,…,valuen),基于当前数组中的所有项创建一个新数组。这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾。最后返回新构建的数组。(合并数组)
如果传递给concat()方法的是一个或多个数组,则该方法会将这些数组中的每一项都添加到数组中。
没有给concat()方法传递参数的情况下,它只是复制当前数组并返回副本。返回的数组是新数组
var arr1 = [1,2,3,4]; var arr2 = [5,6,7,8]; var res = arr1.concat(arr2,[9,10]);//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] var res1 = arr1.concat(arr2,11);// [1, 2, 3, 4, 5, 6, 7, 8, 11] console.log(res1);
-
Array.prototype.join(),使用指定的字符串拼接数组中的每个元素,组成一个字符串。
-
如果参数是空字符串(
""
),则所有元素之间都没有任何字符。var arr = ['red','green','blue']; console.log(arr.join());//如果不写参数使用,号作为分隔符 console.log(arr.join('.'));
-
Array.prototype.reverse(),反转数组项的顺序,返回反转后的原数组。
var arr = ['red','green','blue']; console.log(arr.reverse()); //blue,green,red var arr1 = arr.reverse();//改变的原数组。 console.log(arr== arr1); //true console.log(arr); //还是原来的数组
-
Array.prototype.slice(),基于当前数组中的一个或多个项创建一个新数组(该方法不会影响原始数组)。
只有一个参数的时候该方法返回从该参数指定位置到当前数组末尾的所有项组成的数组。
如果有两个参数,该方法返回的是起始位置和结束位置之间的项(但不包括结束位置的项)
var colors = ['red','green','blue','yellow','pink','white','black'];
var res1 = colors.slice(1); // ["green", "blue", "yellow", "pink", "white", "black"]
var res1 = colors.slice(1,4); //green blue yellow
var res1 = colors.slice(); //
console.log(res1);
-
Array.prototype.sort([sortFunc]),按升序排列数组项(最小的值位于最前面,最大的值排在最后面)。排序后改变原数组。
-
不传递参数的情况下,sort()方法会调用每个数组项的toString(),然后比较的是字符串。
-
sortFunc(这个参数),接收一个比较函数作为参数,这个函数接收两个参数(比较的两个数),如果第一个参数应该位于第二个参数之前则需要返回一个负数,如果相等则需要返回一个0,如果第一个参数应该在第二个参数后面则需要返回一个正数。
//这个函数返回负数,第一个数在前面。返回正数第一个数在后面。
//第一个-第二个数 = 负数 第一个数就在前面。
//第一个数-第二个数= 正数 第一个数就在后面。
-
升序:方法一
var values = [0,5,1,15,10];//0 1 5 10 15
var res = values.sort(function(value1,value2){
if (value1 < value2) {//0 5 //第一个数小于第二个数返回负数,返回负数 第一个数就在第二个数前面了。
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
});
console.log(res);
升序:方法二
var values = [0,5,1,15,10];//0 1 5 10 15
var res = values.sort(function(value1,value2){
return value1 - value2;
});
console.log(res);
降序:
var values = [0,5,1,15,10];//0 1 5 10 15
var res = values.sort(function(value1,value2){
return value2 - value1; //参数后面可以接属性,然后按属性排
});
console.log(res);
-
Array.prototype.toString(),返回数组的字符串表示。
var a = [1,2,3]; console.log(a.toString());
-
Array.prototype.indexOf(),查找指定的值并返回要查找的项在数组中的位置。没有找到返回-1。比较使用全等操作符。
var colors = ['red','green','blue']; //返回的是下标。 没有找到返回-1 console.log(colors.indexOf('red',1)); //第二个参数从哪开始找
-
Array.prototype.forEach(),对数组中的每一项运行给定函数,这个方法没有返回值(返回值是
undefiend
)。forEach和for都能遍历数组不同点:
- forEach只能遍历数组。for可以遍历类数组。因为它是取下标。
- forEach只能遍历完,for中可以中途使用break跳出。
var colors = ['red','green','blue']; colors.forEach(function(value,index){ value是值 index是下标 //函数里面写的是具体的功能。 console.log(value,'-------',index); });
-
Array.prototype.map(),对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
var colors = ['red','green','blue']; var res = colors.map(function(val1,index){ return val1 + index; }); console.log(res); //["red0", "green1", "blue2"]
-
Array.prototype.filter(),对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。
var colors = ['red','green','blue']; var res = colors.filter(function(val,inde){ if (val == 'green') { return false; } return true; }); console.log(res); //red blue
-
Array.prototype.every(),对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true
var a = [5,6,7,8]; var res = a.every(function(val,inde){ if (val >= 6){ return true; } return false; }); console.log(res); //false 全部true则返回true
-
Array.prototype.some(),对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
var a = [5,6,7,8]; var res = a.some(function(val,inde){ if (val >= 6){ return true; } return false; }); console.log(res); //true 有一个true就返回true
-
Array.isArray(value),确定value是否是数组,如果是数组返回true否则返回false。
var b = ["red","yellow"]; console.log(Array.isArray(b)); //true
-
Array.prototype.reduce(function(previousValue, currentValue){}) : 遍历累加返回一个最终结果
var arr = [1, 2, 3, 4, 5];
//如果reduce方法只有第一个函数参数的话,那么第一次执行的下标是从1开始的,因为上一次返回值没有,默认是arr[0],
//当前的值是arr[1],这样是少了一轮遍历
//如果reduce方法拥有第二个参数,则这个参数就是初始进入的prevItemf,这样就是给累加一个初始值
var value = arr.reduce(function (prevItem, nowItem, index) {
// prevItem就是上次函数的返回值, nowItem就是当前遍历的数组的值
return prevItem + nowItem;
}, 0)
console.log(value)
数组的slice和splice方法
slice()方法并没有改变原来的数组,返回一个新的数组
splice()方法他改变了原来的数组
/**
*slice()方法接收两个参数,一个是起始位置,一个是结束位置
* 可以只传一个起始位置,就会返回从起始位置到结尾的所有项
* 他会返回起始位置和结束位置之间的项,包含起始位置,但是不包含结束位置
*/
var arr = [1,2,3,4,5,6,7,8,9,0];
console.log(arr.slice(1))//[2, 3, 4, 5, 6, 7, 8, 9, 0]
console.log(arr.slice(1,4))//[2, 3, 4]
console.log(arr)// [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
/**
*slice()方法并没有改变原来的数组,返回一个新的数组
* slice(),两个参数还可以用负数哟
* 用负数的话就是 数组的长度 + 负数 来确定位置从哪里开始,从哪里结束
*/
console.log(arr.slice(-4,-2))//[7, 8] .....这个就是从 10-4 开始 10-2结束
console.log(arr.slice(-2,-4))//[] ..... 这个就是从 10-2 开始 10-4 结束
/**
* splice()方法应该是最强大的数组方法了
* 主要用途是向数组的中部插入项,参数有三个,一个是起始位置,第二个是删除,插入的项数,第三个是要插入的项
*/
var arr1 = [1,2,3,4,5,6];//删除任意数量的项
console.log(arr1.splice(0,1))//[1] 返回了要删除的项
console.log(arr1)//[2, 3, 4, 5, 6] 他改变了原来的数组
var arr2 = [1,2,3,4,5,6];//插入任意的数量项
console.log(arr2.splice(6,0,7,8,9))//[]
console.log(arr2)//[1, 2, 3, 4, 5, 6, 7, 8, 9]
var arr3 = [1,2,3,4,5,6];//向指定位置插入任意数量的项,并删掉任意数量的项
console.log(arr3.splice(1,2,7,8))//[2, 3]
console.log(arr3)//[1, 7, 8, 4, 5, 6]
ES6
-
Array.from方法用于将类数组对象转为真正的数组:类数组对象(array-like object,有length属性和索引元素)和可遍历对象。
function test(){ var res = Array.from(arguments);//将类数转换为数组 那就可以使用数组的相关方法。 console.log(res); } test();
-
Array.of用于将一组值转换为数组。返回新数组。
var arr = Array.of(1,2,3,4,5); console.log(arr); var a = new Array(4); console.log(a); //长度为4,全是undefined 弥补了Array的不足。 如果只传递一个数值的时候 Array是有差异的。 但是使用Array.of()就不会有差异了。 var b = Array.of(4); //只有一个元素4的数组 console.log(b);
-
Array.prototype.includes()方法返回一个布尔值,表示某个数组是否包含给定的值。
该方法主要用来替代
indexOf()
,因为indexOf()
是全等运算
,而NaN
和NaN
也不相等。var a = [1,2,3,4,NaN]; var res = a.includes(5); console.log(res); //false
Object.keys(obj)
对象转成数组
**Object.keys()**
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
简单说就是保留对象的key组成数组
Object.values(obj)
对象转成数组
**Object.values()**
方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in
循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
简单说就是保留对象的values组成的数组
hasOwnProperty
检查对象上有没有某个属性,只检查对象本身,不检查原型
object.hasOwnProperty(proName)
其中参数object是必选项。一个对象的实例。
proName是必选项。一个属性名称的字符串值。
如果 object 具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回 true,反之则返回 false。