5-9
约 23249 字大约 78 分钟
2025-01-20
5,JavaScript 引用类型
// 本地对象:独立于宿主环境的由ECMAscript实现提供的对象;如object,Function,Array等;
// 内置对象:由ECMAscript实现提供的,独立于宿主环境的所有对象,如Global和Math(本质上是静态的,就不需要创建对象);
// 宿主对象:所有非本地对象都是宿主对象,既由scmascript实现的宿主黄江提供的对象,如多有BOM和DOM对象丢失宿主对象;
5-1 object 类型
5-1-1 创建
// 创建object实例:两种方法;
1,var person = new Object();
2,Var person = {}; (属性名可以是字符串,用引号引起来,属性名中的空格无法识别);
3,数值属性名会自动转换成字符串,但是用方法不同;
ar myo = {}; // 是一个空对象,只包含默认的属性和方法;
Var myo = new Object(); // 两者可以等同;
为对象添加方法
// 注:字面量对象花括号里面用逗号
// 类创建对象里面的类里面使用分号
5-1-2 对象属性
// Object对象属性是动态的
// 两种访问方式:
1,使用点访问如person.name;
2,使用方括号表示法,属性应以字符串的方式放在括号中;
// 注:如果属性名中包含会导致语法错误的字符,或者属性名使用的是关键字或者保留字,就可以使用方括号访问法;
5-1-3 动态添加属性和方法
5-1-4 Object类及对象的属性和方法
4-1 constructor 属性
对创建对象构造函数的引用;如:
4-2 prototype 属性
对对象的原型的引用
4-3 obj.hasOwnProperty(property)
判断该对象是否具有某个属性
4-4 Object.prototype.isPrototypeOf(object)
判断该对象是否为另一个对象的原型;
// 就是判断obj是不是由object创建出来的
// 结果是TRUE,因为p就是由person创造出来的
4-5 Obj.propertyIsEnumerable(property)
判断给定的属性是否可以用for in 枚举;
4-6 obj.toString()
返回对象的原始字符串表示
4-7 obj.valueOf() :
返回最适合该对象的原始值
5-2 其他引用类型
1,数组 var arr = new Array(1,2,3,4,5);
2,字符串 var str = new String(“aini”);
3,真则表达式 var pattern = new RegExp(“w” , “i”);
5-3 对象的废除
// 一个对象不使用,应该废除,释放内存;JS自带垃圾回收程序;
// 强制销毁:object = null;这个对象就不存在了;
// 必须把所有对象的所有引用都设为null,对象才会被清除;
// Delete操作符删除不掉对象,但是可以删除对象的属性,对象的引用
5-4 基本引用类型和引用类型的值
// 对象的比较并非值得比较,即使两个对象包含相同的属性和相同的值也并不相等
// 对象的比较是引用的比较,当且劲当他们引用同一个对象时,他们才相等;
5-5 引用类型的复制
判断两个对象是否有相同的属性值
6,JS基本包装类型
// ES提供了三个基本的引用类型:Booleanj ,Number , String;
// 但是同时还具有与各自的基本类型相应的特殊行为;
// 实际上,每当读取一个基本类型的值得时候,后台就会创建一个对应的基本包装类型的对象,让我们用一些方法来操作这些数据
// 后台默认给s1创建了一个String对象,是临时的一个对象,我们是看不见的再调用了他的substring()方法,调用完了以后再把这个临时对象销毁
// 后台默认给s1创建了一个引用类型,并调用了他的substring()方法赋给s2;
基本包装类型的实例调用typeof 会返回object,而且所有包装类型的对象转换为布尔值类型都是TRUE
Object构造函数,仅接受一个参数;其会根据传入的值得类型返回相应包装类型的实例;
ES在必要时会将包装类型转换成原数值;如:
实际上比较的是他们的值,基本包装类型用valueOf方法返回他的值再进行比较,所以两者相等;
注:null和undefined没有包装对象,访问他们的属性会造成一个类型错误;
6-1 Boolean 类型
// 1,Boolean类型是与布尔值对应的引用类型,Boolean 对象表示两个值:"true" 或 "false"。
// 2,创建Boolean对象:var oBool=new Boolean(true); // 传入true或false值
// 注:如果逻辑对象无初始值或者其值为 0、-0、null、""、false、undefined 或者 NaN,那么对象的值为 false,否则,其值为 true;
// 不判断对象的值,而是判断这个对象存不存在,所以返回TRUE;
// 总结:理解基本类型的布尔值与Boolean对象之间的区别非常重要,建议不要使用,最好使用Boolean原始值;
// 可以使用Boolean(参数)进行数据类型转换;
6-2 Number 类型
// 1,Number 对象,是原始数值的包装对象。在必要时,JavaScript 会自动地在原始数据和对象之间转换;
// 2,可以用构造函数 Number() 明确地创建一个 Number 对象:
6-2-1 对象属性
// 对象属性:
1,MAX_VALUE: // 表示最大的数,静态属性;
2,MIN_VALUE: // 表示最小的数,静态属性;
3,NaN: // 非数字值;静态属性;
4,NEGITAVE_INFINITY:// 负无穷大,溢出时返回该值,静态属性;
5,POSITIVE_INFINITY:// 正无穷大,溢出时返回该值,静态属性;
一般用于判断表达式
// 注:Number.NaN 是一个特殊值,说明某些算术运算(如求负数的平方根)的结果不是数字。
// 方法 parseInt() 和 parseFloat() 在不能解析指定的字符串时就返回这个值。
6-2-2 对象方法
Number类或对象方法:
1,parseInt() 和 parseFloat()方法,静态方法;
2,isNaN()// 来判断是否为数字,静态方法;
3,isFinite:// 是否为有限,静态方法;
4,isInteger:// 是否为整形,静态方法;
5,toString()方法: // 如:NumberObject.toString(radix),radix可选,规定表示数字的基数,使 2 ~ 36 之间的整数,若省略该参数,则使用基数 10。
6,tolocaleString() :// 把一个Number对象转换为本地格式的字符串
7,toFixed() 方法:// 把 Number 四舍五入为指定小数位数的数字,类型是字符串;参数规定了小数的位数,是 0 ~ 20 之间的值;有些实现可以支持更大的数值范围。如果省略了该参数,将用 0 代替
8,toExponential()方法 // 可把对象的值转换成指数计数法。如:NumberObject.toExponential(num) ,num规定指数计数法中的小数位数,是 0 ~ 20 之间的值;
// 用指数计数法表示,而且还要保留指定的小数位数
9,toPrecision() // 可在对象的值超出指定位数时将其转换为指数计数法;其可能会返回固定大小格式,也可能返回指数格式,具体规则是看哪种格式最合适;如:NumberObject.toPrecision(num) ,num规定必须被转换为指数计数法的最小位数,该参数是 1 ~ 21;
// 就是小数点后或者整数位加起来就等于指定的位数,若不满足可以用科学计数法来凑
6-3 String 对象
String类型是字符串对象包装类型,用于处理文本(字符串)。语法:var str = new String(str); 如
// 上面两个是对象
// 下面是强制类型转换的字符串;
6-3-1 字符串属性
1-1 length属性
返回字符串中的字符个数 如: str.length (汉字也是一个字符)
6-3-2 字符串操作方法
2-1 charAt() charCodeAt()
都接受一个参数,是基于0的字符位置;其中charAt() 方法以单字符串的形式返回给定位置的那个字符;
如果想得到字符编码而不是字符时,使用charCodeAt(),如:
也可以用方括号来访问字符串,向数组一样
2-2 concat()
用于将一或多个字符串拼接起来,返回拼接得到的新字符串,如:
concat()方法可以接受多个任意参数,即可以通过它可以拼接任意多个字符串,如:
2-3 slice(x,y)
// x位置开始截取,到y结束(不包含y位置)
2-4 substr(x,y)
// x是截取开始的位置,y是截取长度
substr()将负的第一个参数加上字符串的长度,而将负数的 第二个参数转换为0
2-5 substring(x,y)
// x位置开始截取,到y结束(不包含y位置)
// 如果只设置一个参数,则从开始位置截取到末尾
substring()方法会把所有负值参数都转换为0
// 同时这些方法的值可以是负值;其中,slice()会将传入的负值与字符串的长度相加,substr()将负的第一个参数加上字符串的长度,而将负数的第二个参数转换为0,substring()方法会把所有负值参数都转换为0,如:
2-5-1 文字滚动效果
6-3-3 字符串位置方法
3-1 indexOf() 和 lastIndexOf()
// 这两个方法都是从一个字符串中搜索给定的子字符串,然后返回子字符串的位置,如果没有找到子字符串,则返回-1
// lastIndexOf从后面往前找,两个方法只返回第一个出现的位置
// 可以设置第二个参数,表示从什么位置开始查找;
在使用第二个参数的情况下,可以循环调用两个办法找到所有匹配的字符串
3-2 trim()
trim()方法:.// 删除前置及后缀的所有空格,然后返回结果;
还有两个非标准的trimLeft()和trimRight()方法, // 分别用于删除字符串开头和末尾的空格
3-3 大小写转换
toLocaleLowerCase() // 把字符串转换为小写;
toLocaleUpperCase() // 把字符串转换为大写;
toLowerCase() // 把字符串转换为小写;
toUpperCase() // 把字符串转换为大写;
// 其中toLowerCase()和toUpperCase()是最常用的方法
6-3-4 字符串匹配方法
4-1 match()
本质上与RegExp的exec()方法相同;其只接受一个参数,要么是一个正则表达式,要么是一个RegExp对象,如
// 说明:match()返回了一个数组,其第一项是与整个模式匹配的字符串,之后的每一项(如果有)保存着与正则表达式中的捕获组匹配的字符串
4-2 search()
接受的参数与match()一样;该方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回-1;如
4-3 replace()
replace()方法:// 替换字符串,接受两个参数,第一个参数是一个查找的字符串或者是正则,第二个参数是要替换的字符串或者函数;
// 注意:如果第一个参数是字符串,则只会替换第一个找到的字符串,想替换所有的话第一个参数可以写个正则;
// 想要替换全部就加上g(全局标志),不然只会替换第一个如果没匹配上替换不了,就会输出原有的值
4-4 split
split()方法:// 基于指定的分隔符将一个字符串分割成多个字符串,并把结果放在一个数组中;分隔符可以是字符串,也可以是正则;其可以接受可选的第二个参数,用于指定数组的大小;
第二个参数可以指定长度
也可以是正则表达式
6-2-5 其他方法
5-1 ocaleCompare()
localeCompare() // 用本地特定的顺序来比较两个字符串,默认返回下列值中的一个:-1、0、1,如
5-2 fromCharCode()
fromCharCode()方法: // String构造函数本身还有一个静态方法:fromCharCode(),这个方法的任务是接受一或多个字符编码,然后将它们转换成一个字符串;从本质上看,这个方法与实例方法charCodeAt()执行的是相反的操作
把asccII码转换成对应的字母或数字
5-3 HTML方法
// 早期的Web浏览器可以使用Javascript动态格式化HTML,其扩展了字符串的标准,实现了一些专门用于简化常见HTML格式化任务的方法;但是,尽量不要使用这些方法,因为它们创建的标记通常无法表达语义
anchor(name) // 创建 HTML 锚,输出如:<a name=”name”>string</a>
big() // 用大号字体显示字符串,如:<big>string</big>
small() // 使用小字号来显示字符串,如:<small>string</small>
blink() // 显示闪动字符串;
bold() // 使用粗体显示字符串,如:<b>string></b>
fontcolor(color) // 使用指定的颜色来显示字符串,如:<font color=”color>string</font>
fontsize(size) // 使用指定的尺寸来显示字符串,如:<font size=”size”>string</font>
italics() // 使用斜体显示字符串,如:<i>string</i>
link(url) // 将字符串显示为链接,如:<a href=”url”>string</a>
fixed() // 以打字机文本显示字符串,如:<tt>string></tt>
sup() // 把字符串显示为上标,如:<sup>string</sup>
sub() // 把字符串显示为下标,如:<sub>string</sub>
strike() // 使用删除线来显示字符串,如:<strike>string</strike>
7,Global 对象
// 全局对象及其属性在任何地方都可以直接使用;
// Global对象是对象,不是类,所以没有构造函数,不能new实例化
// 注:所有全局变量也都是全局对象的属性;
这个对象是不存在的,换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法; 因此它没有名字(Global不是全局对象的名字,只是我们这样称呼它)
// isNaN就是全局对象的函数
7-1 全局属性
// 如undefined、NaN以及Infinity都是Global属性;
// ECMAScript5禁止给undefined、NaN和Infinity赋值,这样做即使在非严格模式下也会导致错误。
7-2 全局对象
Math,json
7-3 全局函数
// isNaN(), isFinite(), parseInt(), parseFloat()等
// isNaN()用来确定一个值是否为NaN,而Number.isNaN()确定传递的值是否为NaN和其类型是Number;它是原始的全局isNaN的强大的版本
// Number.isFinite()和全局的isFinite()相比,不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的,才返回true
// 只有为Number的值且是有限的时候Numebr.isFinite()会返回true。而isFinite()在参数经过Number转换后再判断是否是有限的。正无穷、负无穷和NaN都不是有限数字。
// Number.isInteger()用来判断给定的参数是否为整数;只有是Number类型的值并且是整数才会返回true,Infinity和NaN都不是整数。
7-4 全局构造函数
// 全局对象还定义了一些属性,用来引用Javascript预定义的所有其他对象,这些对象大部分是原生引用类型的构造函数:
Object、Number、String、Boolean、Array、Date、Function、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
// 根据JavaScript的运行环境,在JS中存在两种全局对象:JS全局对象和window全局对象
// 当Javascript放在特定宿主环境时,全局对象通常具有与该特定环境相关的额外属性;这些额外属性并不是ES标准规定的,而是由宿主环境实现的;如在客户端Javascript中,全局对象是Window对象,表示运行JS的Web浏览器窗口;或在nodejs中,Global指的就是global对象;
// 全局中的this:由于全局对象位于作用域链的最顶部,所以在全局环境中,关键字this指的就是全局对象;如果在是浏览器中,this指的就是window对象;
This 就相当于全局对象的代理人,在浏览器环境中就是window;
在ES中,全局对象的预定义属性都是不可枚举的,因此使用for/in来只能列出所有隐式或显式声明的全局变量,如
7-5 全局变量
所有的全局变量都全局对象的属性
声明的4种方法:
// 1.直接在全局作用域中用var 声明的变量就是全局变量,此种方式声明的变量具有不可配置的属 性,不能使用delete操作符把变量删除。
// 2.window.变量,这种声明的变量也是全局变量,但这种变量跟上面用var 声明的变量有点不一样,这种方式声明的全局变量是可配置的,因此能用delete操作符把变量删除。
// 3.隐式声明全局变量,就是不使用var声明,直接进行赋值的变量,在不严格模式中,相当于window.变量这种方式,但在严格模式下,会报错。
// 4.在html中给标签指定一个id属性,也相当于给Window对象添加了一个id的属性,在javascript中可直接通过标签的id访问该标签(或者window['id'])。
7-6 Global对象的方法
7-6-1 字符编码方法
1-1 Escape() 函数
// escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串(其中某些字符被替换成了十六进制的转义序列 )
// 注:该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码:- + _ . *
var str ="aini is a good boy"
var a="_,.艾尼!-+";
console.log(escape(str));
console.log(escape(a));
// 转换成以%开头的16进制码
// 如 空格→%20 艾尼 → %u827e %u5c3c
1-2 unescape() 函数
unescape() 函数
// unescape() 函数可对通过 escape() 编码的字符串进行解码。
// 注:通过找到形式为 %xx 和 %uxxxx 的字符序列(x 表示十六进制的数字),用 Unicode 字符 \u00xx 和 \uxxxx 替换这样的字符序列进行解码。
console.log(unescape("aini%20is%20a%20good%20boy"));
解码方式 :
// 把% 抓换成 \ ,这样以后浏览器就能识别\uxxxx的Unicode编码了
console.log("\u827e"+"\u5c3c"); // 艾尼
7-6-2 URL编码方法
// Global对象的encodeURI()和encodeURIComponent()可把字符串作为 URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器;
// 有效的URI中不能包含某些字符,如空格;这两个编码方法,用特殊的UTF-8编码替换所有无效的字符,从而让浏览器能够接受和理解。
// 注:两个方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ‘ ( )
encodeURI()也不会对在URI中具有特殊含义的符号;/?:@&=+$,#进行转义,但encodeURIComponent()会
// 其中,encodeURI()主要用于整个URI,如:
http://www.zeronetwork.cn/my case.html,而encodeURIComponent()主要用于对URI中的某一段,如my case.html进行编码;
// 它们的主要区别在于,encodeURI()不会对本身属于URI的特殊字符进行编码,如:冒号、正斜杠、问号和井号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码
2-1 encodeURI() 和 encodeURIComponent()
encodeURI()函数 和 encodeURIComponent() 函数
var str="zero!ne work零-_!~'(*&#?";
console.log(encodeURI(str));
var str="zero!ne work零-_!~'(*&#?";
console.log(encodeURIComponent(str));
// encodeURIComponent() 函数 编码范围广一点(用的时候仅仅对URL里面某一部分进行编码)
var url1="https://www.zeronetwork.cn/case.html?user%20name&age=18#one";
console.log(encodeURI(url1));
console.log(encodeURIComponent(url1));
// 所以encodeURICompenent不适合对整个URL进行编码
// 注:一般来说,使用encodeURIComponent()的场景比使用encodeURI()要多,因为在实践中更常见的是对查询字符串参数而不是对基础URI进行编码
2-2 decodeURI() 和 decodeURIComponent()
decodeURI() 和 decodeURIComponent()
// 其中decodeURI() 只能对 encodeURI() 编码过的 URI 进行解码;
// 如:可将%20替换成一个空格,但不会对%23作任何处理,因为%23代表井号,而井号不是使用encodeURI()替换的;
// 同样的,decodeURIComponent()能够解码encodeuricomponent()编码的所有字符,即它可以解
var url1="https://www.zeronetwork.cn/case.html?user name&age=18#one";
console.log(encodeURI(url1));
console.log(encodeURIComponent(url1));
console.log(decodeURI("https://www.zeronetwork.cn/case.html?user%20name&age=18#one"));
console.log(decodeURIComponent("https%3A%2F%2Fwww.zeronetwork.cn%2Fcase.html%3Fuser%20name%26age%3D18%23one"));
// 注:URI方法代替了已经被ECMA-262第3版废弃的escape和unescape()方法,因为URI方法能对所有Unicode编码,而原来的方法只能对ASCII符号正确编码;因此在实际场景中,一定要使用URI方便,不要再使用escape()和unescape()方法;
onsole.log(escape("http://www.zeronetwork.cn/index.html?id=101&username=艾尼"));
console.log(escape("?!=()#%&"));
console.log(encodeURI("http://www.zeronetwork.cn/index.html?id=101&username=艾尼"));
console.log(encodeURI("?!=()#%&"));
console.log(encodeURIComponent("http://www.zeronetwork.cn/index.html?id=101&username=艾尼"));
console.log(encodeURIComponent("?!=()#%&"));
7-7 eval 函数
//只接受一个字符串参数,是正常的JS或者ES代码
// 可以动态执行JS代码
function swapImg(){
var img = eval("document.getElementById(myimg)")
myimg.src="images/02.jpg";
}
var myImg=document.getElementById("myimg");
myImg.onmouseover=swapImg;
同样的,也可以在eval()中定义一个函数,在该调用的外部代码中引用这个函数,如:
eval("var msg='hello world';");alert(msg);
// 注:在eval()中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中,它们只在eval()执行的时候被创建;
console.log(str); // undefined
var str = "aini like dilnur";
// 虽然变量声明在后,访问在前;但是str会被提升,但是他的值不会被提升,所以str存在,返回undefined;
show();
function show(){
console.log("show");
}
// 这里函数也提升了
eval("console.log(str)");
str="aini like dilnur";
// 会报错,str未定义,说明str没有被提升
var s="global";
function func(){
eval("var s = 'local——';") // 局部的 在外边不能够访问
window.eval("var s = 'window.local';") //全局
}
func();
console.log(s); // window.local
var x = y ="global";
function f(){
var x = "local";
eval("x+='changed';") //改变的是局部x的值
console.log(x);
}
function g(){
var y = "local";
window.eval("y+='changed';"); //改变的是全局x的值
console.log(y)
}
f(); //返回函数局部变量 x 的值
g(); //返回局部变量 y 的值,由于没改变 所以返回 local
console.log(x); //返回全局变量 x 的值
console.log(y); //返回全局变量 y 的 值
所以说window.eval在函数内能改变全局变量的值;
// 函数内可以访问到全局变量,函数外不能访问函数内的局部变量;当函数内,函数外声明同一个变量时,函数内优先访问函数内的局部变量;
Window.eval()在函数内优先访问全局变量
eval() 当在函数内部作用域中,eval执行的是局部,如果调用window.eval则在全局空间执行
// 注:在严格模式下,ES5对eval()的行为施加了更多的限制,在外部访问不到eval()中创建的任何变量或函数,同时,为eval()赋值也会导致错误
不是严格模式下:
var x = 18;
eval("var y=20;console.log(y)");
console.log(y); // 外部也能访问
在严格模式下:
"use strict"
var x = 18;
eval("var y=20;console.log(y)");
console.log(y); // 外部不能访问
7-8 window对象
// ECMAScript虽然没有指出如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的;因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性
var color="red";
function sayColor(){
console.log(window.color); //就可以当window的属性用
}
sayColor();
// 注:JavaScript中的window对象除了扮演ECMAScript规定的Global对象的角色外,还承担了很多别的任务(即window对象下的一些方法和属性都是js原生提供的,但在浏览器环境中,大量的功能都是由宿主对象完成的);另外一种取得Global对象的方法
var global=function(){
return this;
}(); // 相当于global();
// 定义的同时函数也运行了
// 说明分析:以上代码创建了一个立即调用的函数表达式,返回this;在没有给函数明确指定this值的情况下,this值等于Global对象
var global=function(){
return this;
}(); // 相当于global();
var str="aini like dilnur";
console.log(str);
console.log(window.str);
console.log(this.str);
console.log(global.str);
在函数内访问外部变量的方法
var global=function(){
return this;
}(); // 相当于global();
var str="aini like dilnur";
function show(){
var str="aini";
console.log(window.str);
window.eval("str");
console.log(global.str);
console.log(this.str); //容易产生歧义
}
show();
7-9 Math 对象
// Math对象是一个静态对象,或者称为工具类,不能使用new关键字创建对象,应直接使用”对象名.成员”的格式来访问其属性和方法,如:
var num=Math.random();
7-9-1 math对象的属性
E:// 自然对数的底数,即常数e.约等于2.718;
LN10:// 代表10的自然对数,约等于2.302;
LN2:// 代表2的自然对象,约等于0.693;
LOG2E:// 代表以2为底E的对数;
LOG10E:// 代表以10为底E的对数;
PI:// 代表II的值,约等于3.1415926;
SQRT1_2:// 代表2的平方根分之一,约等于0.707;
SQRT2:// 代表2的平方根,约等于1.414;
console.log(Math.PI);
console.log(Math.E);
console.log(Math.SQRT1_2);
console.log(Math.abs(-5));
7-9-2 math对象的方法
2-1 min()和max()
// 用于确定一组数值中的最小值和最大值;这两个方法都可以接收任意多个数值参数
var max =Math.max(3,25,45,78,96,54,100);
var min =Math.min(87,45,12,59,87);
console.log(max);
console.log(min);
注:数组中不能用,所以需要借助apply()方法,如:
var arr=[1,25,84,75,95,42,35,68];
var max=Math.max.apply(Math,arr);
console.log(max);
2-2 ceil()
// 向上取整
console.log(Math.ceil(1.1)); // 2
console.log(Math.ceil(-1.5)); // -1
2-3 floor()
// 向下取整
console.log(Math.floor(1.1)); // 1
2-4 round()
// .5的时候round返回比它大的整数,所以负数有点不一样
// 注:在round里面使用负数时注意:
console.log(Math.round(-1.1));
console.log(Math.round(-1.5)); //注意中间的
console.log(Math.round(-1.6));
2-5 Random()
Random()方法:// 随机数;
// 产生[0,1)之间的随机数
console.log(Math.random());
般与其他方法配合使用,以求得一个在一个范围内的随机数
console.log(Math.floor(Math.random()*10+1)); // [1,10]
总结公式:
Math.floor(Math.random()*(max-min+1)+min)
得到一个区间的整数随机数
function intRandom(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
console.log(intRandom(10,100));
数组当中随机拿出一项
var colors=["red","blue","green","black","purple"];
var color=colors[Math.floor(Math.random()*(colors.length))];
console.log(color);
随机返回字符
var str="ABCDEFGHIJKLNOPQURSTUVWXYZabcdefghijklnopqurstuvwxyz0123456789";
var a="";
function getRandomStr(x){
for(var i=0;i<x;i++){
var random=Math.floor(Math.random()*(str.length-1));
a+= str.substring(random,random+1);
}
return a;
}
console.log(getRandomStr(4));
从中文里面随机取x个字符
// 首先确定汉字编码的范围
4E00 ----------------9FA5
// 16进制转换成10进制
19968 -------------40869
function getRandomHan(len){
var str="";
for(var i=0;i<len;i++){
c=(Math.floor(Math.random()*(40869-19968+1)+19968)).toString(16);
c = "\\u" +c;
c=c.replace(/\\/g,"%");
c= unescape(c);
c.replace(/%/g,"\\") //还存在%的用\代替回去;
str+=c;
}
return str;
}
console.log(getRandomHan(4
继续封装,继续简化
function getRandomHan(len){
var str="";
for(var i=0;i<len;i++){
c=getRandom(19968,40869).toString(16);
c = decodeUnicode(c);
str+=c;
}
return str;
}
console.log(getRandomHan(4))
function getRandom(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
};
function decodeUnicode(str){
str="\\u"+str;
str=str.replace(/\\/g,"%");
str=unescape(str);
str=str.replace(/%/g,"\\");
return str
}
老师课件上的封装
function getRandomHan(len){
var str = '';
for(var i=0;i<len; i++){
var c = (Math.floor(Math.random() * (40869 - 19968) + 19968)).toString(16);
c = "\\u" + c;
c = c.replace(/\\/g,"%");
c = unescape(c);
c = c.replace(/%/g,"\\");
str += c;
}
return str;
}
console.log(getRandomHan(6));
// 拆分整合
function getRandom(min,max){ // 获取指定范围内的随机数
return Math.floor(Math.random() * (min - max) + max);
}
function decodeUnicode(str) { // 解码
str = "\\u"+str; //Unicode显示方式是\u4e00
str = str.replace(/\\/g, "%");
str = unescape(str); //转换中文
str = str.replace(/%/g, "\\"); //将其他受影响的转换回原来
return str;
}
function getRandomChinese(len){
var str = "";
for(var i = 0;i<len;i++){
str += decodeUnicode(getRandom(0x4e00,0x9fa5).toString(16));
}
return str;
}
console.log(getRandomChinese(2));
7-9-3 其他函数
abs(num):// 绝对值、
asin(x):// 反正弦值、
exp(num):// Math.E的num次幂、
atan(x):// 反正切值、
log(num):// num的自然对数、
atan2(y,x):// y/x的反正切值、
pow(num,power):// num的power次幂、
cos(x):// 余弦值、
sqrt(num)://平方根、
sin(x):// 正弦值、
acos(x):// 反余弦值、
tan(x):// 正切值
8,函数的形参与实参
( 形参 parameter 实参 argument )
8-1 形参
8-1-1 可选参数
可选参数:
// 当实参比形参的个数要少时,剩下的形参都将设置为undefined值;为了让实参与形参保持较好的适应性,有必要为可选形参设置一个合理的默认值;如:
function show(name,msg){
if(name==undefined) name="aini";
if(msg==undefined) msg="18 years old";
console.log("hello,"+name+",message:"+msg);
}
show();
show("aili");
show("dilnur","22 years old");
不用 if 可以使用||,也是惯用手法
function show(name,msg){
name=name || "aini";
msg= msg || "18 years old";
console.log("hello,"+name+",message:"+msg);
}
show();
show("aili");
show("dilnur","22 years old");
// 将对象o中可枚举的属性追加到数组a中,并返回数组a ,如果省略a,则创建一个新数组并返回这个新数组;
function getProprtyNames(o,/* optional */a){ // 一般可以加个注释来指定一下不定参数
if(a == undefined) a=[];
// a= a || [];
for(var property in o){
a.push(property);
}
return a;
}
var o={name:"aini",age:18,sex:"nan",friend:"dilnur"};
var result=getProprtyNames(o);
console.log(result);
var arr=[1,2,3];
console.log(getProprtyNames(o,arr));
// 说明:当设置可选参数时,一定要把可选参数放到形参列表的最后,因为不能省略第一个参数(定参)并传入第二个实参;如果第一个参数(定参)不存在或不使用,也需要显式的传入undefined,真实的场景是传入一个无意义的占位符null
function show(a,b,/* optional*/c,d){
}
show(1,2,3,4); //正常使用
show(1,null,2,3); // 使用null占位
8-1-2 形参个数
//函数本身的length属性,只读,它代表函数的形参个数,即函数期望传入的实参个数,如:
// 函数名.length 可以返回传入的形参的个数
function show(a,b,/* optional*/c,d){
console.log(show.length);
}
show(1,2,3,4);
show(1,null,2,3);
8-2 实参
8-2-1 实参对象(arguments)
// 传入的实参个数超过函数定义的形参个数时,没有办法直接获得超出的实参的值,但可以使用实参对象arguments来获取
// 在函数体内,arguments是指向实参对象有的引用,其是一个类数组对象;
// 函数接收的始终都是这个数组,而不关心数组中包含哪些参数;
// 即使没有传递参数,这个数组也存在,只不过是包含0个元素的数组;
function show(a,b){
console.log(arguments);
}
show();
show(1,2,3,4);
Arguments.length 可以返回实参个数
function sayHi(){
console.log("传入了,"+arguments.length+"个实参");
console.log("hello,"+arguments[0]+","+arguments[1]);
}
sayHi();
sayHi("aini","huan ying ni lai xue xi");
可以使用arguments对象来验证所传递的参数是否符合函数要求;如
//实际场景中,如果对参数个数有要求需要验证;
function func(x,y,z){
//首先验证传入的个数是否正确
if(arguments.length!== func.length) throw new Error("需要"+func.length+"三个函数"); //抛出异常错误;
//在判断类型;
if(typeof arguments[0] != "number")
console.log("实参类型是数字");
z=+arguments[2]?y:0;
return x+y+z;
}
console.log(func(1,2,"w"));
// 在某些时候,没有必要检查实参的个数,因为JS的默认行为是可以满足需要的,如省略的实参都是undefined,多出的参数自动省略
function check(args){
var actual=args.length; //实参的真实个数;
var expected=args.callee.length; //形参个数;
if(actual != expected) throw new Error("参数个数不对");
}
function f(x,y,z){
try{
check(arguments);
return x+y+z;
}
catch(e){
console.log(e);
}
};
console.log(f(1,2,3));
console.log(f(2,3));
可以利用arguments对象,让函数能够接收任意个数并分别实现适当的功能,如
function doAdd(){
if(arguments.length==1) console.log(arguments[0]);
else if(arguments.length==2) console.log(arguments[0]+arguments[1]);
else if(arguments.length==3) console.log(arguments[0]+ arguments[1]+arguments[2]);
}
doAdd(10,20);
doAdd(5,10,20);
doAdd(10,35,12,45);
// 根据实参的个数进行处理
优化代码(利用循环
function doAdd(){
var sum = 0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
console.log(sum);
}
doAdd(10,20);
doAdd(1,2,3,4,5,6,7,8,9);
继续优化
function doAdd(){
var sum = 0;
for(var i=0;i<arguments.length;i++){
sum+= (+arguments[i]?arguments[i]:0);
}
console.log(sum);
}
doAdd(10,20);
doAdd(1,2,3,4,5,"aini",NaN,8,9);
根据实参返回最大值
function max(){
var max=Number.NEGATIVE_INFINITY;
for(var i=0;i<arguments.length;i++){
(arguments[i]>max)?(max=arguments[i]):max;
}
return max;
}
var largest=max(1,45,32,189,245,12,36,985,421,65);
console.log(largest);
// 类似于这种可以接收任意个数的实参,这种函数也被称为“不定实参函数”,这个术语来自C语言
// 但真正的不定实参的个数不能为零,其应用的场景是:该函数包含固定个数的命名和必须的参数,以及随后个数不定的可选实参,即arguments对象可以与命名参数一起使用
function doAdd(num1,num2){
var result=parseInt(num1)+parseInt(num2);
if(arguments.length > 2){
for(var i=2;i<arguments.length;i++){
result+= parseInt(arguments[i]);
}
}
return result;
}
console.log(doAdd(20,30,40,50,60,30));
arguments并不是真正的数组,它是一个对象,其数组元素是函数实参的别名;arguments的值永远与对应命名参数的值保持同步
function func(num1){
console.log(num1); // 10
console.log(arguments[0]); //10
arguments[0]=1;
console.log(num1); // 1
console.log(arguments[0]) //1 ;
}
func(10);
// 注:在严格模式下,重写arguments的值会失效,但不会导致语法错误
// Javascript是一门弱类型语言,所以函数中的形参并没有类型声明,并且在传入参数前也未做任何类型检查,虽然JS可以进行数据类型的自动转换,但在某些时候(类型不同,不会导致语法错误,但在程序运行时有可能导致错误),函数还是希望得到一个类型明确的参数值,此时,应当对所传入的参数进行类型的检查,如:
function sum(arr){
if(Array.isArray(arr)){
var total=0;
for(var i=0;i<arr.length;i++){
var element=arr[i]
if(element == null) continue;
if(isFinite(element)) total+=element;
else throw new Error("数组元素必须是有限数");
}
return total;
}else throw new Error("函数的参数必须是数组");
}
console.log(sum([1,2,3,4,5,6]));
扩大累加的数据类型
function flexsum(a){
var total=0;
for(var i=0;i<arguments.length;i++){
var element=arguments[i],n;
if(element == null) continue;
if(Array.isArray(element)){
n=flexsum.apply(this.element);
}else if(typeof element ==="function"){
n=Number(element());
}else n=Number(element);
if(isNaN(n)) throw new Error("无法把:"+element+"转换为数字");
total+=n;
}
return total;
}
console.log(flexsum(1,2,3));
console.log(flexsum(1,"",3));
console.log(flexsum(1,[7,8],3));
console.log(flexsum(function(){return 18;}));
没有重载
unction func(){
console.log("func函数");
}
function func(num){
console.log("年龄为:"+num);
}
function func(name,age){
console.log("名字为:"+name+",年龄为:"+age);
}
func();
func(18);
func("aini",18);
// 虽然定义了三个一样的函数,但是最后定义的把前面定义的两个覆盖掉
// ECMAScript函数不能像传统意义上那样实现重载;而在其他语言中,可以为一个函数编写两个定义,只要这两个定义的签名(参数的类型和数量)不同即可;ECMAScript函数没有签名,因为其参数是由包含零或多个值的数组来表示的;而没有函数签名,真正的重载是不可能做到的;
var addNum = function(num){return num+100};
var addNum = function(num){return num+300};
函数表达式定义同名的函数时,下面的变量就把前面的变量覆盖了,所以也不存在重载;
8-2-2 模拟重载
可以通过检查传入函数中参数的类型和数量并作出不同的反应,可以模拟方法的重载;
function overrideFunc(){
var arglen=arguments.length;
if(arglen==0) console.log("wu canshu de fangfa");
else if(arglen==1) console.log("传入的参数为:"+arguments[0]);
else if(arglen==2) {console.log("传入了2个参数");
if(!isNaN(arguments[0]&& !isNaN(arguments[1]))){
console.log("和为:"+(arguments[0]+arguments[1]));
}else console.log("不是数字:"+arguments[0]+arguments[1]);
}else console.log("未知,其他处理");
}
overrideFunc();
overrideFunc("零点程序员");
overrideFunc(3,4);
overrideFunc(3,"a",33);
8-2-3 callee和caller属性
// callee属性指向当前正在执行的函数,在某些时候非常有用,如在一个匿名函数中使用递归;但在严格模式下会抛出错误,所以,尽量避免使用callee属性,可以为函数明确定义一个名称;
//数的阶乘
function factorial(x){
if(x<=1) return 1;
return x* factorial(x-1);
}
console.log(factorial(10));
//数的阶乘
function factorial(x){
if(x<=1) return 1;
// return x* factorial(x-1);
return x*arguments.callee(x-1) //这样就不会出错了;
}
var myFactorial = factorial;
factorial = null;
console.log(myFactorial(10));
8-2-4 匿名函数中使用
//数的阶乘
function create(){
return function(n){
if(n<=1) return 1;
return n*arguments.callee(n-1);
};
}
console.log(create()(5));
// 由于内函数没有名字,所以只能用arguments.callee来指向他;
caller是非标准的,但大多数浏览器都实现了这个属性,它指向调用当前函数的函数,在ES5中被移弃了,其始终会返回undefined,不会报错
8-3 参数的传递方式
// 基本数据类型:值传递
// 引用数据类型:引用传递,地址传递(本质上还是按值传递)
function setname(obj){
obj.name="aini";
}
var person = new Object();
setname(person);
console.log(person.name); //aini
// obj和person内存对应同一个地址
function setname(obj){
obj.name="aini";
obj = new Object();
obj.name = "dilnur";
console.log(obj.name);
}
var person = new Object();
setname(person); //dilnur
console.log(person.name); //aini
8-4 函数回调和匿名函数
8-4-1 函数回调
// 回调 :将一个函数对象a 传递给另一个函数对象 b,让后者在适当的时候执行 a。这就是所谓的“回调”
// 适当的时候:当这个外部函数在一定条件下就会调用参数指定的函数,此函数就是回调函数。如:
function funcA(){
console.log("this is func A");
}
function funcB(func){
func();
}
funcB(funcA); // this is a func A
<input type="text" id="score">
<input type="button" value="检测" onclick="test()"><br>
<p id="myp"></p>
<script>
function test(){
var myp=document.getElementById("myp");
var str;
var score = document.getElementById("score").value;
if(score<0) str="分数不能为负"
else if(score == 0) str="此考生没有参加考试"
else if(score>0 && score<60) str="不及格"
else if(score>=60 && score <70) str="及格"
else if(score>=70 && score<90) str="良好"
else if(score>=90 && score<=100) str="优秀"
else str="无效分数";
myp.innerHTML=str;
}
也可以这么样写
var str;
function fun(score){
if(score<0) str="分数不能为负"
else if(score == 0) str="此考生没有参加考试"
else if(score>0 && score<60) str="不及格"
else if(score>=60 && score <70) str="及格"
else if(score>=70 && score<90) str="良好"
else if(score>=90 && score<=100) str="优秀"
else str="无效分数";
}
function test(){
var myp=document.getElementById("myp");
var score = document.getElementById("score").value;
fun(score);
myp.innerHTML=str;}
继续改进,用回调函数
<body>
<input type="text" id="score">
<input type="button" value="检测" onclick="test()"><br>
<p id="myp"></p>
<script>
var str;
function fun(score,callback){
if(score<0 || score>100) str="分数无效"
else if(score == 0) str="此考生没有参加考试"
else callback();
}
function test(){
var myp=document.getElementById("myp");
var score = document.getElementById("score").value;
fun(score,function(){ //匿名函数
if(score>0 && score<60) str="不及格"
else if(score>=60 && score <70) str="及格"
else if(score>=70 && score<90) str="良好"
else if(score>=90 && score<=100) str="优秀"
});
myp.innerHTML=str;
}
数组排序
function sortArr(arr,fun){
if(!Array.isArray(arr) || !(fun instanceof Function)){
throw new Error("参数类型不准确");
}else{
for(n in arr){
for(m in arr){
if(fun(arr[n],arr[m])){
var tmp=arr[n];
arr[n]=arr[m];
arr[m]=tmp;
}
}
}
}
}
function compare(num1,num2){
return num1>num2;
}
try{
var arr=[45,12,68,95,115,65,32,25,12,78,35];
sortArr(arr,compare);
console.log(arr)
}catch(e){
console.log(e);
}
8-4-2 匿名函数
// 匿名函数即为没有名字的函数,也称为拉姆达函数;匿名函数功能强大,用途很广
// 一般将匿名函数作为参数传入另一个函数,或者从一个函数中返回另一个函数,这是一种极为有用的技术
// 函数也可以作为值来使用;也就是说,可以将函数赋值给变量、存储在对象的属性或数组元素中,也可以当作一个参数进行传递
function func(x){return x*x}
var s = func;
console.log(func(4));
console.log(s(4));
同样可以将函数赋值给对象的属性,此时应该称为方法
var o = {
name:"aini",
say:function(x){
return x*x;
}
}
console.log(o.say(5));
或者
var o = {}
function say(x){
return x*x;
};
o.say=say;
console.log(o.say(5));
或者
这里用的就是匿名函数
var o = {}
o.say=function(x){return x*x;}
console.log(o.say(5));
赋值给数组元素,此时的函数可以不需要名字,即是匿名函数
var arr = [function(x){return x*x},18];
console.log(arr[0](arr[1]));
函数作为参数传递
function myFun(someFun,someArg){
return someFun(someArg)
}
function add(num){
return num+=10;
}
var result = myFun(add,20);
console.log(result);
还可以这样用
//定义一些简单函数
function add(x,y){return x+y};
function subtract(x,y){return x-y};
function multiply(x,y){return x*y};
function devide(x,y){return x/y};
function operate(operator,num1,num2){
return operator(num1,num2);
}
var result = operate(add,operate(multiply,4,5),operate(add,2,3));
console.log(result);
第二种用法
// 使用函数直接量,定义在一个对象直接量中
var operators = {
add: function(x,y){return x + y;},
subtract: function(x,y){return x - y;},
multiply: function(x,y){return x * y;},
divide: function(x,y){return x / y;},
pow: Math.pow // 使用内置的Math对象的pow方法
};
// operate函数接受一个名字作为运算符,在operators对象中查找这个运算符
// 然后将它作用于所提供的操作数
function operate(operation, num1, num2){
if(typeof operators[operation] === "function")
return operators[operation](num1, num2);
else throw "unknown operator";
}
// 调用并计算
var result = operate("add", "hello", operate("add", " ", "wangwei"));
console.log(result); // hello wangwei
result = operate("pow", 10,2);
console.log(result); // 100
// 当函数作为值的一个典型的应用,就是在Array.sort()方法中使用,该方法用来对数组元素进行排序;因为排序的规则有很多,如:基于数值大小、字母表顺序、日期大小、从小到大等,所以sort()方法接受一个自定义函数作为参数,该自定义函数用来处理具体的排序操作,其原理是:对于任意两个值都返回一个值,以指定它们在在排序后的数组中的先后顺序;如:
function compare(x,y){
return x - y;
}
var arr = [1,88,3,5,12,18,67];
console.log(arr.sort(compare));
9,数组
// 数组是值的有序集合;每个值叫做一个元素,而每个元素在数组中都有一个位置,以数字表示,称为索引或下标;
// JavaScript数组是无类型的,数组元素可以是任意类型;即使同一个数组内,不同的元素也可以有不同的类型
// JS数组的索引是基于零的32位整数,第一个元素的索引为0;
// ES数组是动态的,即它的大小是可以动态调整的,可以随着数据的添加与删除会自动增长或缩减
9-1 创建数组
9-1-1 第一种
使用构造函数Array()创建
var arr = new Array(); // 空数组
var arr = new Array(3); // 指定长度
var arr = new Array("wangwei","wujing","jingguo"); //显示指定元素
// 1.如果预先知道数组要保存的项目数量,可以为构造函数传递该数量,这个数量实际上就是该数组length属性(实际上是预分配了一个数组空间,但数组中没有存储值,甚至数组的索引都未定义)
// 2.直接传递项,即参数就是数组的元素
// 3.如果需要创建只有一个值的数组,并且这个值是数字,只能把该数字使用字符串的形式;当这一个值如果是非整数时,会抛出异常;
var arr = new Array(18.5); //抛出异常
也可以省略new操作符,实际上是进行了数据类型转换,如
var arr = Array(); // 空数组
console.log(arr);
var colors = Array(3); // 指定长度
console.log(colors);
var names = Array("wangwei","wujing","jingguo"); //显示指定元素
console.log(names);
9-1-2 第二种
使用数组字面量(直接量)创建
var empty = [];
var num = [2,3,4,5];
// 这是创建数组最简单的方法,即使用方括号[ ]将数组元素用逗号分隔开
// 当为[ ]空数组时,与new Array()等价
// 数组直接量中的值不一定是常量,也可以是任意的表达式
var year = 2022;
var years = [year,year+1,year+2];
console.log(years);
还可以包含对象或其他数组
var objArr = [{name:"wangwei",age:18},[1,2],[3,{x:4,y:5}]];
如果省略数组中某个值,省略的元素的值都是undefined值
var count = [1,,3];
console.log(count[1]); // undefined;
允许有可选的结尾逗号,但会省略最后的元素
count = [1,2,]; // 2
console.log(count);
count = [,,,]; // 3
console.log(count);
// 不建议使用,某些浏览器会解析为3项数组,因为它们对数组的实现不一致
// 总结:在实际场景中,使用数组字面量要比构造函数简单、灵活
9-2 数组的内存分布
// 数组在内存中用一串连续的区域来存放一些值;在 JavaScript 中,数组是哈希映射,在内存中不是连续的,它可以通过多种数据结构实现,其中一种是链表,因此,如果使用大数组,需要的时间很长;
// 在ES6中引入了类型化数组,JavaScript 引擎已经在为同种数据类型的数组分配连续的存储空间了,如ArrayBuffer,其使用的就是一块连续的内存空间
9-3 数组的读写
要读取或设置数组元素的值时,使用“[ ]”运算符,并提供相应的基于0的数字索引下标
var citys = new Array("蚌埠","宿州","南京");
var c1 = citys[0]; // 读取第一个元素
citys[0] = "怀远"; // 写第一个元素
在越界访问不存在的数据元素时,会返回undefined
如果设置某个值的索引超过了数组现有的项数,数组会自动增加到该索引值加1的长度
var colors=["red","blue"];
colors[2]="green"; // 明确添加了一个新元素
var i = 3;
colors[i] = "purple";
colors[i+1] = "yellow";
注意:跳过某个索引添加值时,跳过的自动变成empty
var colors=["red","blue"];
colors[3] = "purple";
console.log(colors);
console.log(colors.length); // 4
// 数组的最大索引为4294967294(2的32次方-2)
// 数组是对象的特殊形式;使用方括号访问数组元素就像用方括号访问对象的属性一样;
// 其会将指定的数字索引值转换成字符串,并将其作为属性名来使用
var n = [];
n[0] = "one"; n[“0”]=”one”
n[1] = "two"; n[“1”]=”two” 等同
console.log(n[1]);
var p = {};
p[0] = "one"; // 将0转换为”0”
p[1] = "two";
console.log(p[1]);
var o = {0:"wangwei",1:"wujing",2:"huiyuan"};
o[1] = "lili";
console.log(o[1]); // lili
// 所有的数组都是对象,所以可为其创建任意名字的属性,此时这个任意名字如果不是非负整数,它就只能当作常规的对象属性,而非数组的索引;
var arr = [1,2];
arr[-2] = "-2的值";
arr["name"] = "aini";
console.log(arr);
// 如果凑巧使用了是非负整数的字符串,它就当作数组索引,而非对象属性;当使用一个浮点数和一个整数相等时情况也是一样的
var arr = [1,2];
arr["2"] = "333"; 把二当做数组的索引,因为字符串2能转换成数字
var arr = [1,2];
arr[-1.23] = true; // 创建了一个名为“-1.23”的属性
arr["1000"] = 18; // 数组的第1001个元素
arr[1.000] = 10; // 等同 arr[1]
console.log(arr)
// 事实上,数组索引仅仅是对象属性名的一个特殊类型,这意味着ES数组没有“越界”错误的概念,即试图查询任何对象中不存在的属性时,不会报错,只是得到undefined值
// 所有的索引都是属性名,但只有在2的32次方之内的整数属性名才是索引;超出的索引只能是属性名(所以超出4294967295的都属属性,数组索引最大长度为4294967295)
arr[4294967294] = "a";
arr[4294967294] = "a";
console.log(arr);
console.log(arr.length);
arr[4294967295] = "b";
console.log(arr);
console.log(arr.length);
arr[4294967296] = "c";
console.log(arr);
console.log(arr.length);
通常,数组的实现是经过优化的,用数字索引来访问数组元素一般来说比访问常规的对象属性要快很多,所以尽量使用数组传递数据
9-4 length 属性
数组对象的length(长度)属性返回或指定数组元素的个数
var colors=["red","blue","green","skey","gray"];
console.log(colors.length);
colors.length = 3;
console.log(colors.length);
console.log(colors);
// 说明:即多于length的元素被删除了
var names=["wangwei"];
names.length = 4;
console.log(names); // 添加了三个空的向;
// length属性设置为大于数组项数的值,则新增的每一项都会取得undefined值;(从本质上讲,并不会向数组中添加新的元素,它只是在数组尾部创建一个空的区域,其实就是后面要讲的稀疏数组)
利用length属性可以方便的在数组末尾添加新项
var colors=["red","blue","green"];
colors[colors.length]="black";
colors[colors.length]="brown";
console.log(colors);
// 数组的最大索引为4294967294(2的32次方-2),即可以包含4292967295个元素,当索引小于此值并且不为非负时,数组会自动更新其length属性;超出这个范围,length不会更新
在ES中,可以用Object.defineProperty()让数组的length属性变成只读的
9-5 稀疏数组
Sparse arrays 稀疏数组:ES中的数组是稀疏的,即数组的元素的索引不一定要连续,它们之间可以有空缺
var arr = new Array(5); // 数组没有元素,但length是5
arr = []; // 空数组,length为0
arr = [1,,4]; //
arr[1000] = 0; // 赋值添加了一个元素,length为1001
arr = [1,2,3,4,5];
delete arr[2]; // 使用delete也能生成稀疏数组
console.log(arr);
如果数组是稀疏数组,length值大于元素的个数,否则,该属性就是数组元素的个数;(即稀疏数组的length长度与个数是不一致的)
遍历时会跳过这些空隙
var a1 = [1,,,4];
for(i in a1)
console.log(i);
循环遍历时都能打印出来,空数组就打印undefined;
9-6 稠密(密集)数组
与稀疏相对应,则为密集数组,即元素中不存在空隙,其实密集数组基本就是平时常见的正常数组
var spare = new Array(3);
var dense= Array.apply(null,spare); 可以产生值为undefined
console.log(spare); 的密集数组,跟稀疏数组
console.log(dense); 有区别
console.log(spare[0]);
console.log(dense[0]);
区别
var arr = new Array(3);
arr[2] = "aini";
for (index in arr){
console.log(index,arr[index]); // 2 aini
}
var dense = Array.apply(null,Array(3));
dense[2] = "dilnur";
for (index in dense){
console.log(index,dense[index]); //都能打印
}
// 说明稀疏数字里面的undefined和密集数组里面的undefined是两回事儿
// 密集数组
console.time('one');
Array.apply(null,Array(1e5)).forEach(function(){});
console.timeEnd('one');
// 稀疏数组
console.time('two');
Array(1e5).forEach(function(){});
console.timeEnd('two');
// one: 8.589111328125ms
// two: 2.122314453125ms
// 稀疏数组速度比密集数组处理速度快
// 在实际应用中,绝大部分的es数组都不是稀疏数组;如果使用了稀疏数组,可以像处理非稀疏数组一样处理,只不过它们包含一些undefined值
// 稀疏的数组通常在实现上比稠密的数组更慢、内存利用率更高,在这样的数组中查找元素的时间与常规元素属性的查找时间一样长;另外,在通常的情况下,我们想要直接声明一个数组并赋予其一些特定的初始值,并且为了避免问题,通常是希望申明为密集数组的
压缩稀疏数组:可以通过filter()方法压缩其中的空隙,因为filter会跳过空隙,返回密集的数组
var a1 = [1,,,4];
var a2 = a1.filter(function(x){return true;});
for(i in a2)
console.log(i);
9-7 数组方法
ES在Array.prototype中定义了很多操作数组的方法
console.log(Array.prototype);
9-7-1 数组转字符串
1-1 toString(),valueOf()
所有对象都具有toLocaleString()、toString()及valueOf()方法
1,valueOf() // 返回的是数组本身
2,toString() // 方法将数组表示为字符串,各个元素按顺序排列组合以逗号分隔的字符串返回。(通过对每项调用toString()方法,然后用逗号把它们连接在一起构成的)
var arr = [1,2,3,5];
console.log(arr);
console.log(arr.valueOf());
console.log(arr.toString());
var colors=["red","blue",["green","black"]];
console.log(colors.valueOf()); // ["red", "blue", Array(2)]
console.log(colors.toString()); // red,blue,green,blank
console.log(colors); // ["red", "blue", Array(2)]
// 以上console.log()换成alert,由于alert要接收字符串参数,所以它会在后台调用toString(),由此会得到与直接调用 toString()方法相同的结果
// 这里的toString()方法与不使用参数的join()方法返回的字符串是一样的
// toLocaleString()是toString()方法的本地化版本,一般情况下会返回与toString()和valueOf()方法相同的值,但也不总是如此(其会使用本地化的分隔符);当调用数组的toLocaleString()方法时,后台会为每一项调用 toLocaleString()方法,而不是tostring()方法
var p1 = {
toLocaleString:function(){return "aini"},
toString:function(){return "ai"}
}
p2 = {
toLocaleString:function(){return "dilnur"},
toString:function(){return "norah"}
}
var person = [p1,p2];
console.log(person);
console.log(person.toString());
console.log(person.toLocaleString());
1-2 join()
2,join()方法
// 将数组中所有元素使用不同的分隔符转化为字符串,分隔符号由用户指定
var colors = ["red","green","blue","balck"];
console.log(colors.join());
console.log(colors.join(","));
console.log(colors.join("-"));
console.log(colors.join("|"));
console.log(colors.join(" "));
var arrStr = new Array(10);
console.log(arrStr.join("-"));
如果不指定分隔符,或者传入undefined,则使用默认的逗号分隔
var colors = ["red","green","blue","balck"];
console.log(colors.join(undefined));
console.log(colors.join("undefined"));
console.log(colors.join(null));
console.log(colors.join("null"));
// 注:”undefined”,”null”是字符串;如果直接是undefined则会用逗号分割;null的话转换成”null”,再用”null”分割
// join()方法是split()方法的逆向操作,后者是将字符串分割成若干块来创建一个数组。
// 注:如果数组中的某一项的值是null或者undefined,那么该值在以上所有方法中返回空字符串。
字符串split()方法:把字符串转换为数组
var str = "red,blue,green"; // 字符串
var aStr=str.split(","); // 数组
var aStr=str.split(""); // 单个字符数组
关于字符串split方法的几个注意点:
// (1)如果指定错误的分隔符,则把整个字符串当做数组的一个 元素返回
var str = "red,blue,green";
var aStr=str.split("-");
console.log(aStr);
// (2)如果什么也不传,就会把字符串里面的每一个字符拿出来,当做数组的一个元素
var str = "red,blue,green";
var aStr=str.split("");
console.log(aStr);
// (3)如果把空字符串声明为分隔符,split会返回字符串的每个字条符;一般用于需要逐个处理字符.
9-7-2 队列方法
//(1)栈和队列是一种限制了插入和删除数据项操作的数据结构;
//(2)栈数据结构的访问规则是LIFO(先进后出)
//(3)而队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出)
//(4)栈最近添加的项是最先删除的项;栈中的插入和删除操作都只发生在一个位置,即栈顶部;把一个项添加到栈中,叫做推入栈;从栈中删除一项,叫做从栈中弹出;
//(5)队列在列表的末端添加项,从列表的前端移除项;可以使用push()方法向数组末尾添加项,使用shift()方法从数组前端移除项,这两个方法模拟队列;
2-1 push()
在数组尾部添加一个或多个元素;并返回更新后数组长度会修改原数组
var colors = new Array();
var count = colors.push("red","green");
console.log(colors);
console.log(count); // 2
count = colors.push("black","blue");
console.log(colors);
console.log(count); //4
// push()方法实际上同手工添加数据一样.
// 注:pop()不带值也不会报错,只返回数组长度
2-2 pop()
与push()方法相反,其会删除数组末尾的一个元素,并将其返回;会修改原数组
var colors=new Array();
colors.push("red","green","blue");
var item = colors.pop();
console.log(item); // blue
console.log(colors);
2-3 unshift()
// (1) 在数组顶端插入元素,一次可以插入单个或多个元素,所有元素按顺序插入,操作完成后返回新数组的长度;会修改原数组;其与push()类似
// (2)unshift()方法将引发所有下标的改动
// (3)在一次性插入多个参数时,插入的元素的顺序和它们在参数列表中的顺序一致
// (4)如果不计较元素插入的位置,则推荐使用push方法,因为,unshift()方法可能会影响依靠下标才能准确进行的计算。
var colors = new Array();
colors.push("red","green","blue");
var newLen = colors.unshift("black","gray");
newLen = colors.unshift("r",["g","b"]);
console.log(newLen);
console.log(colors);
2-4 shift()
// shift()移除数组的第一个元素并将其返回;该方法执行后数组剩下的元素向前移动,下标索引号重新调整从0开始按顺序赋予所有元素;会修改原数据;其与pop方法类似;
var colors = new Array();
colors.push("red","green","blue");
var shiftItem = colors.shift();
console.log(shiftItem);
var item = colors.shift();
console.log(item);
console.log(colors);
调用push()方法和shift(),可以使Array对象具有队列一样的行为(先进先出)
var colors=new Array();
var count = colors.push("red","green","blue");
count = colors.push("black");
var item = colors.shift();
console.log(colors);
使用unshift()和pop()方法,可以从相反的方向来模拟队列,即在数组的前端添加项,在末端移除项;
var colors=new Array();
var count = colors.push("red","green","blue");
count = colors.unshift("black");
var item = colors.pop();
console.log(colors);
9-7-3 数组排序
排序算法:选择排序和冒泡排序;
3-1 选择排序
var arr = [78,15,69,3,45,78,95,62,35,98,789,125,98,5,58];
for (var i=0; i<arr.length;i++){
var element
for(var j=i+1; j<arr.length;j++){
if(arr[j]>arr[i]){
element = arr[i];
arr[i] = arr[j];
arr[j]=element;
}
}
};
console.log(arr);
3-2 冒泡排序
var arr = [78,15,69,3,45,74,785,125,654,12,35,64,05,45,78,126];
function compare(num1,num2){
return num1<num2?true:false;
}
function sort(arr){
var len = arr.length;
for (var i=0; i<len;i++){
var element
for(var j=0; j<len-i;j++){
if(compare(arr[j],arr[j+1])){
element = arr[j];
arr[j] = arr[j+1];
arr[j+1]=element;
}
}
}
return arr;
};
console.log(sort(arr));
3-3 reverse()
reverse()方法将一个Array对象中所有元素的次序反转,会修改原数组
var arr = ["aini","dilnur","xinjiang","shanghai"];
console.log(arr.reverse());
3-4 sort()
sort()方法:
(1)sort()方法:// 对数组元素进行排序后并返回;默认将一个数组中的所有元素进行升序排序:会改变原数组
(2)// 如果数组元素中undefined或null,它们会被排列在数组的尾部;
var values = [0,1,5,10,15];
values.sort();
console.log(values);
var arr = new Array("banana",undefined,"cherry",null,"apple"); 按照Unicode编码大小
arr.sort(); 进行排序;
console.log(arr);
实际上,sort()方法是把元素转换成字符串进行排序的
// (1)sort()方法可以接收一个比较函数,以确定一个排序的规则
// (2)比较函数接收两个参数,如果第一个参数位于第二个之前则返回一个负数
// (3)如果两个参数相等则返回0
// (4)如果第一个参数位于第二个之后则返回一个正数
function compare(value1,value2){
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
var values = [0,1,5,10,15];
values.sort(compare);
// 对于数值类型或者valueOf()方法会返回数值类型的对象类型,可以使用一个更简单的比较函数,这个函数只要用两个值互减即可
function compare(value1,value2){
return value1 - value2;
}
注:参数必须是数字才能这样用;
// 说明:由于该函数通过返回一个小于零、等于零或大于零的值来影响排序结果,因此减法操作就可以适当地处理所有这些情况。
3-5 随机排序
var arr = [1,2,3,4,5,6,8,9];
var randomArr = arr.sort(
function(){
return Math.random()-0.5;
}
);
console.log(arr);
对于字符串排序,主要是大小写的问题
var str = ["dilnur","AINI","aini","DILNUR"];
console.log(str.sort());
str.sort(function(s,t){
var s = s.toLowerCase();
var t = t.toLowerCase();
if(s < t) return -1;
if(s > t) return 1;
return 0;
});
console.log(str);
另外,有时需要按字符串长度进行排序,此时还应该同时判断字符是否为双节字
var arr = ["阿卜杜艾尼a","aini-艾尼","dilnur_地","aini艾尼","dilnur地理"];
function getDword(str){
var len = str.length;
for(var i=0;i<len;i++){
if(str.charCodeAt(i)>255){
len++;
}
}
return len;
}
arr.sort(function(s1,s2){
return getDword(s1)-getDword(s2);
})
console.log(arr);
// 汉字的长度就按双字节算;
3-6 对象排序,
比较的主要是对象的valueof()值:
var arr = [o,
{valueOf:function(){return 15}},
{valueOf:function(){return 18}},
{valueOf:function(){return 4}}
];
arr.sort(function(o1,o2){return o1 - o2});
console.log(arr);
for(var i in arr)
console.log(arr[i].valueOf());
或者按指定的对象属性进行排序
var wang = {name:"wangwei",sex:"男",age:18};
var wu = {name:"wujing",sex:"女",age:28};
var guo = {name:"jianguo",sex:"男",age: 25};
var arr = [wang,wu,guo];
var pArr = arr.sort(function(p1,p2){
return p1.age - p2.age;
});
console.log(pArr);
9-7-4 数组操作
4-1 concat()
concat()方法:
// (1)基于当前数组,将多个元素连接一起成为新的数组并返回
// (2)新数组中的元素按连接时的顺序排列
// (具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。当需要合并多个数组时,此方法比较方便)
var arr = [1,2];
arr1 = arr.concat();
arr2 = arr.concat(3,4);
arr3 = arr.concat([3,4,],["aini"]); // 3,4,aini拿出来放到源数组里面; 扁平化
arr4 = arr.concat([3,4,[5,6,7]]);
console.log(arr);
console.log(arr1);
console.log(arr2);
console.log(arr3);
console.log(arr4);
// (3)concat()不会递归扁平化数组的数组,也不会修改原来的数组
// (4)当没有给concat()方法传递参数的情况下,它只是复制当前数组并返回副本
4-2 slice()
2,slice()方法:
// (1)能够基于当前数组中的一个或多个项创建一个新数组
// (2)其接受一或两个参数,即要返回项的起始和结束位置;
// (3)在只有start一个参数的情况下,slice()方法返回从该位置开始到当前数组末尾的所有项;
// (4)如果两个参数,该方法返回起始和结束位置之间的项,但不包括结束位置的项;
// (5)其不会影响原有数组;
var colors = ["red","green","blue","yellow","purple"];
var a = colors.slice(2);
var b = colors.slice(1,4);
console.log(a);
console.log(b);
var c = colors.slice(1,-2); //从索引1 取到 倒数第二个;
var d = colors.slice(-4,-2); // 倒四 取到 倒二;
console.log(c);
console.log(d);
只能从左往右截,像colors.slice(3,1) 就会返回空数组;
4-3 splice()
3,splice()方法:(改变原数组)
// (1)可以删除、替换或插入数组元素,是最强大的数组方法,有很多种用法;
// (2)具体作用是,从一个数组中移除一个或多个元素;剩下的元素组成一个数组,移除的元素组成另一个新数组并返回;
// (3)同时,原数组可以在移除的开始位置处顺带插入一个或多个新元素,达到修改替换数组元素的目的;
start:// 必选项,表示从数组中剪切的起始位置下标索引号。
deleteCount:// 必选项,表示将从数组中切取的元素的个数。
item:可选项,// 表示切取时插入原数组切入点开始处的一个或多个元素;
3-1 删除
var colors = ["red","green","blue","yellow","purple"];
var a = colors.splice(1,3);
console.log(colors); // 返回原数组删除某些元素以后的数组
console.log(a); // 返回删除的项
1)删除:// 可以删除任意数量的项,只需要指定2个参数,如:splice(0,2);// 删除数组中前两项;如果只指定1个参数,则删除从该位置到结尾的元素;
2)插入:// 可以向指定位置插入任意数量的项,需要指定3个参数,其中第二个参数为0;如果要插入多个项,可再传入第4第5等任意多个项,如:splice(2,0,”red”,”green”);
3)替换:// 可以向指定位置删除任意数量的项,同时插入任意数量的项,需要指定至少3个参数;插入的项数不必与删除的项数相等,如:splice(2,1,”red”,”green”);
splice()方法始终会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)
3-2 插入
没必要把返回值赋给新变量
var colors = ["red","green","blue","yellow","purple"];
var a = colors.splice(1,0,"grey","skey");
console.log(colors); // 返回插入以后的新的数组;
console.log(a); // 返回空数组
3-3 替换
替换的数量跟删除的数量不一定一致
var colors = ["red","green","blue","yellow","purple"];
var a = colors.splice(1,2,"grey","skey");
console.log(colors); // 返回替换以后的新的数组;
console.log(a); // 返回数组删除的项
注:splice会改变源数组,但是splice本身只返回删除的项
var colors = ["red","green","blue","yellow","purple"];
var a = colors.splice(1,2,["wangwei"],["aini"]);
console.log(colors);
console.log(a);
不会扁平化,区别于concat()方法
第一个参数可以接受负数,如果为负数,则指定的位置是数组长度加上该负值,或者从后向前定位
var colors = ["red","green","blue","yellow","purple"];
var a = colors.splice(-3,2,"aini","dilnur");
console.log(colors);
console.log(a);
// (倒数第三个位置往后数两个)
// 在实际的场景中,主要还是向数组的中部插入项;
// delete运算符删除数组元素:
// 通常使用delete运算符删除一个指定的元素(与删除对象属性一样)
var colors=["red","green","blue","yellow","purple"];
console.log(colors.length);
delete colors[1];
console.log(colors);
console.log(colors.length);
// delete删除不会修改数组的length属性;如果从数组中删除一个元素,该数组就变成稀疏数组;
// 此删除并没有真正删除数组元素,仅删除元素内容;(此删除与为其赋undefined值是类似的,但有一些微妙的区别)
// 删除数组元素有多种方式,如设置length属性小于数据长度,会删除数组尾部的元素;
9-7-5 数组位置操作
5-1 ndexOf(),lastIndexOf()
// 1.两者会搜索整个数组中具有给定值的元素,并返回要查找的元素在数组中的第一个索引位置,或者在没找到返回-1;
// 2.这两个方法都是接收两个参数:要查找的项和(可选的)表示查找起点位置的索引;其中,indexOf()方法从数组的开头(位置为0)开始向后查找,lastIndexOf方向则从数组的末尾开始向前查找。
// 注:在查找时,使用是全等操作符
var numbers = [1,2,3,4,5,4,3,2,1];
console.log(numbers.indexOf(4)); // 3
console.log(numbers.lastIndexOf(4)); // 5
console.log(numbers.indexOf(4,4)); // 5 从索引4位置往后找“4”这个元素;
console.log(numbers.lastIndexOf(4,4)) // 3 从索引4位置往前找|“4”这个元素;
var p ={name:"wangwei"};
var p1 = p;
var people = [{name:"wangwei"},{name:"aini"}];
var people = [p1,{name:"aini"}];
var morePeople = [p];
console.log(people.indexOf(p)); // 0
console.log(morePeople.indexOf(p)); // 0
// 4.第二个参数也可以是负数,即为数组长度加上该负数的值为查找的索引开始位置,或者从后往前定位
var numbers = [1,2,3,4,5,4,3,2,1];
console.log(numbers.indexOf(4,-5)); // 5
封装一个函数,查找所有给定值的索引,并返回一个包含匹配索引的数组
var numbers = [1,2,3,4,5,4,3,2,1,4,2,7,9,4,2,3,5];
function findIndex(arr,index){
var len = arr.length;
var result = [];
var pos = 0;
while(pos<len){
pos = arr.indexOf(index,pos);
if(pos == -1) break;
result.push(pos);
pos++;
}
return result;
}
console.log(findIndex(numbers,4));
字符串也有indexOf()和lastIndexOf()方法,它们和数组的方法功能类似
对于数组这些方法,也可以重写,本质上就是修改其prototype对象的属性方法,如
var arr = [1,2,3];
arr.push(4,5);
console.log(arr);
Array.prototype.push = function(){
for(var i=0; i<arguments.length; i++)
this[this.length] = arguments[i] * arguments[i];
}
arr.push(6,7);
console.log(arr);
9-7-6 遍历数组
// 所谓的数组的遍历就是通过索引挨个取出数组元素;遍历目的是为了读取所有元素或者处理所有元素;使用for循环是遍历数组最常见的方法
var cities = ["beijing","nanjing","bengbu"];
for(var i=0;i<cities.length;i++) console.log(cities[i]);
for(var a in cities) console.log(a,cities[a]);
var o ={name:"aini",age:18,sex:true,hobby:"reading"};
var keys = Object.keys(o); // 把对象的属性放到数组里返回
console.log(keys);
var values = [];
for(var i=0,len=keys.length;i<len;i++){
// values[i]=o[keys[i]]
values.push(o[keys[i]]);
}
console.log(values);
// 在遍历稀疏数组,或者需要排除null、undefined和不存在的元素时,需要先检测:
var arr = [1,,3,null,5,undefined,7];
for(var i=0,len=arr.length;i<len;i++){
console.log(arr[i]); // 可以把所有元素都打印出来;
}
console.log("----------------");
for(var i in arr){
console.log(arr[i]); //会过滤掉empty元素
};
console.log("----------------");
for(var i in arr){
if(!arr[i]) continue; //过滤掉null和undefined
console.log(arr[i]);
};
console.log("----------------");
for(var i in arr){
if(arr[i] === undefined) continue; //过滤掉undefined
console.log(arr[i]);
};
console.log("----------------");
for(var i in arr){
if(arr[i] === null) continue; //过滤掉null
console.log(arr[i]);
};
console.log("----------------");
for(var i=0,len=arr.length;i<len;i++){
if(!(i in arr)){
console.log(arr[i]); // 可以用for循环过滤空元素;
}
} ;
// (注:空元素的索引用in操作符判断时返回false;用for循环时可以用来过滤掉稀疏数组里的空元素;)
// 但是,for/in能够枚举继承的属性名,如添加到Array.prototype中的方法;由于这个原因,在数组上不应该使用for/in循环,除非使用额外的检测方法来过滤不用的属性,如:
Array.prototype.company ="zeronetwork";
var arr = [1,,3,null,undefined,6];
arr.custom = "wangwei";
arr[-18.1]=18;
for(var i =0;i<arr.length;i++){
console.log(i,arr[i]);
} //for循环只遍历元素;
//用 for in把所有可枚举的属性枚举出来;
for(var i in arr){
console.log(i,arr[i]);
} //custom ,company,-18.1都打印出来;
//hasOwnProperty()方法过滤;
for(var i in arr){
if(!arr.hasOwnProperty(i)){
console.log(i,arr[i]); //只有 company zeronetwork;
}
};
//跳过不是非负整数的i;
for(var i in arr){
if(String(Math.floor(Math.abs(Number(i))))!== i) continue;
console.log(i,arr[i]);
}
// (1)ES允许for/in循环以不同的顺序遍历对象的属性;
// (2)通常数组元素的遍历实现是升序的,但不能保证一定是这样的;特别地,如果数组同时拥有对象属性和数组元素,返回的属性名很可能是按照创建的顺序而非数值的大小顺序;
// (3)如何处理这个问题的实现各不相同,如果算法依赖于遍历的顺序,那么最好不要使用for/in而使用常规的for循环。
9-7-7 遍历数组的迭代方法
(1)// ECMAScript5为数组定义了5个迭代方法;
(2)// 这些方法的第一个参数接收一个函数,并且对数组的每个元素调用一次该函数;
(3)// 第二个参数是可选的:其是运行该函数的作用域对象(影响this的值);
(4)// 或者说:调用的函数被当作是第二个参数的方法;
(5)// 如果是稀疏数组,对不存在的元素不调用传递的函数
(6)// 这些方法不会修改原始数组;
(7)// 参数函数使用三个参数:数组元素、元素索引和数组对象本身;通常,只需要第一个参数,可以忽略后两个参数;
(7)// 参数函数可以修改原始数组;
(8)// 根据使用的方法不同,这个函数执行后的返回值可能也会不同;
7-1 forEach()
遍历数组,为每个元素调用指定的函数;该函数使用三个参数:数组元素、元素索引和数组本身;此方法没有返回值,本质上与使用for循环迭代数组一样
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item,index,array){
console.log(item,index,array[index]);
});
第一个参数:// 数组元素;
第二个参数:// 数组索引;
第三个元素:// 数组对象本身
// 为每个元素加1
var data = [1,2,3,4];
data.forEach(function(v,i,a){
a[i] = v + 1; //会改变原数组元素
});
console.log(data);
如果只关心数组元素的值,可以只使用一个参数,额外的参数将被忽略
// 求和
var data = [1,2,3,4];
var sum = 0;
data.forEach(function(value){ //第一个参数数组元素,相当于遍历多有元素;
sum += value; //不会改变原数组元素;
});
console.log(sum);
(1)// forEach()方法无法在所有元素都被传递给调用的函数之前终止遍历,即没有像for循环中使用的break语句;
(2)// 如果要提前终止,必须把forEach()方法放在try块中,并能抛出一个异常:
function foreach(a,f,t){
try{a.forEach(f, t);}
catch(e){
if(e === foreach.break) return;
else throw e;
}
}
foreach.break = new Error("StopIteration");
7-2 every(),some()
(1)// 最相似的是every()和some(),它们都是数组的逻辑判定:用于判定数组元素是否满足某个条件;
(2)// 对every(),传入的函数必须对每一项都返回true,这个方法才返回true;
(3)// 而some()是只要传入的函数对数组中的某一项返回true,就会返回true,否则返回false
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){
return (item > 2);
});
console.log(everyResult); // false
var someResult = numbers.some(function(item,index,array){
return (item > 2);
});
console.log(someResult); // true
(4)// 一旦every()和some()确定该返回什么值时,它们就会停止遍历数组元素;
(5)// (some()在判定函数第一次返回true后就返回true,但如果判定函数一直返回false,它将会遍历整个数组;every()恰好相反,它在判定函数第一次返回false后就返回false,但如果判定函数一直返回true,它将会遍历整个数组)。
(6)// 根据数学上的惯例,在空数组上调用时,every()返回true,some()返回false;
7-3 filter()方法
(1)// 返回的是数组元素是调用的数组的一个子集;
(2)// 回调函数是用来逻辑判定的,该函数返回true或false;如果返回的是true或真值,则该函数处理的数组元素就被添加到返回的子集数组中;
(3)// filter()方法会跳过稀疏数组中缺少的元素,它的返回数组总是密集的
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item,index,array){
return item>2;
});
console.log(filterResult);
var evenResult = numbers.filter(function(value,index){
return index % 2 == 0; // [1, 3, 5, 3, 1] index是索引
});
console.log(evenResult);
// 压缩稀疏数组
var sparse = [1,,,4];
var dense = sparse.filter(function(){
return true; //过滤稀疏元素
});
console.log(dense);
// 压缩并删除undefined和null元素
var sparse = [1,null,3,undefined,,6];
var dense = sparse.filter(function(v){
return v !== undefined && v != null;
});
console.log(dense);
(注:该方法返回的是符合条件的数组元素;)
7-4 map()
(1)// 将调用的数组的每个元素传递给回调函数,并将调用的结果组成一个新数组返回;
(2)// 其不会修改原始数组,如果是稀疏数组,返回的也是相同方式的稀疏数组,即具有相同的长度、
相同的缺失元素;
var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,index,array){
return item*2;
});
console.log(mapResult);
var a = [1,null,3,undefined,5];
var b = a.map(function(v){
return v * v;
});
console.log(b);
不做任何处理
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
});
console.log(dense);
返回所有数组元素
var dense = spare.map(function(v,i,a){
return v;
});
console.log(dense);
能处理的元素进项处理,空元素返回空元素
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
return v*v; //进行数据类型转换,由于null和undefined不能进行数据类型转换});
console.log(dense) //,所以都返回NaN
7-5 reduce()和reduceRight()
(1)// reduce()和reduceRight();这两个方法都会迭代数组的所有项,然后构建一个最终返回的值;
(2)// 其中,reduce()方法从数组的第一项开始,逐个遍历到最后;而reduceRight则从数组的最后一项开始,向前遍历到第一项;
(3)// 这两个方法都接收两个参数:调用的函数callbackfn和作为归并基础的初始值initialValue(可选的);
(4)// 这个函数接收4个参数:前一个值、当前值、项的索引和数组对象;其返回的任何值都会作为第一个参数自动传给下一项;第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项
使用reduce()方法可以执行求数组中所有值之和的操作,如
// 求和
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur,index,array){
return prev + cur;
});
console.log(sum); // 10
reduce()参数函数的参数也可以只使用两个参数,如:
// 求和
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur){
return prev + cur;
});
console.log(sum); // 10
// 求积
var product = values.reduce(function(prev,cur){
return prev * cur;
});
console.log(product); // 24
// 求最大值
var max = values.reduce(function(prev,cur){
return prev > cur ? prev : cur;
});
console.log(max); // 4
reduce()方法的第二个参数initialValue是回调函数的第一个参数previousValue的初始值;如:
// 把以上所有示例添加第二个参数,如:
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur){
return prev + cur;
},2); // 12
意思就是pre不是第一项,而是给pre赋一个值,cur从第一项开始迭代
var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev,cur,index,array){
console.log(prev,cur);
return prev + cur; //21
},6); //pre的初始值为6;
console.log(sum); // 15
// 求2^(3^4),乘方操作的优先顺序是从右到左
var a = [2,3,4];
var big = a.reduceRight(function(accu, value){
console.log(value);
return Math.pow(value, accu);
});
console.log(big);
// 在空数组上,不带初始值参数的reduce()将导致类型异常;如果数组只有一个元素并且没有指定初始值,或者一个空数组并且指定了一个初始值,该方法只是简单的返回那个值而不会调用回调函数;
var a = [];
// var b1 = a.reduce(function(x,y){return x + y}); //no initial value 会报错
// var b2 = a.reduce(function(x,y){return x + x}); //也会报错
var b3 = a.reduce(function(x,y){return x + y},3); //只返回初始值
console.log(b3); // 3
(1)// 使用reduce()还是reduceRight(),主要取决于要从哪头开始遍历数组;除此之外,它们完全相同。
(2)// reduce()和reduceRight()是典型的函数式编程;有些地方,会把其回调函数称为化简函数,这个化简函数的作用就是用某种方法把两个值组合或化简为一个值,并返回化简后的值;如以上的示例,化简函数通过各种方法,组合了两个值
9-8 检测数组及类数组
9-8-1 检测数组
1.// 数组是具有自己特殊行为的对象,因此,确定某个对象是不是数组就很重要;
2.// typeof只能检测其是object的类型,而不能确定是否为Array类型;
3,// 对于一个页面或者一个全局作用域而言,可以使用instanceof操作就可以得到检测结果,如:
var arr = [];
console.log(arr instanceof Array);
console.log(arr instanceof Object);
console.log(arr.constructor);
console.log(arr.constructor === Array);
注:实际上就是判断它的原型;
4. // 但instanceof与constructor有问题:如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,分别具有自己的全局对象,每个全局对象都有自己的Array构造函数,因此一个框架窗口中的对象不可能是另一个框架窗口的实例;
5.// 比如:如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中创建的数组分别具有各自不同的构造函数,它们就不是同一类对象
var frame = document.createElement('iframe');
document.body.appendChild(frame);
var FrameArray = window.frames[0].Array; // 获取框架全局环境中的Array构造函数
var fa = new FrameArray(1,2,3); // 在框架全局环境中创建数组
console.log(fa instanceof Array); // false,在当前环境中判断fa是不是数组
console.log(Array.isArray(fa)); // true
console.log(fa.constructor === Array); // false
6. // 解决方案:使用Object.prototype上的原生toString()方法判断;如:
Object.prototype.toString.call(arr);
7.// 该方法返回一个[object NativeConstructorName]格式的字符串;但该方法不能检测非原生构造函数的函数名,因此自定义的构造函数都将返回[object Object],如:
var frame = document.createElement('iframe');
document.body.appendChild(frame);
var FrameArray = window.frames[0].Array; // 获取框架全局环境中的Array构造函数
var fa = new FrameArray(1,2,3); // 在框架全局环境中创建数组
console.log(fa instanceof Array); // false,在当前环境中判断fa是不是数组
console.log(Array.isArray(fa)); // true
console.log(fa.constructor === Array); // false
console.log(Object.prototype.toString.call(fa));
console.log(Object.prototype.toString.call(fa) === "[object Array]");
var d = new Date();
console.log(Object.prototype.toString.call(d));
console.log(Object.prototype.toString.call(p));
// 所以说如果不是系统自己定义的,而是我们自己定义的独享无法用
Object.prototype.toString.call();这个方法来判断,因为都会返回[object object];
<body>
<iframe src="a.html" name="myFrame"></iframe>
<script>
// 修改上例
console.log(Object.prototype.toString.call(fa) === "[object Array]"); // true
function Person(){}
var p = new Person();
console.log(Object.prototype.toString.call(p)); // [object Object]
// 或者:
<script>
// a.html 中定义了 var aArr = [1,2];
window.onload = function(){
var myFrame = window.frames["myFrame"];
console.log(myFrame.aArr);
console.log(myFrame.aArr instanceof Array); // false
console.log(Object.prototype.toString.call(myFrame.aArr) === "[object Array]"); // true
}
</script>
根据封装一个判断类型的函数:
function getType(target){
var types = {
"[object Object]" : "Object",
"[object Array]" : "数组",
"[object Number]" : "Number",
"[object Boolean]" : "Boolean",
"[object String]" : "String",
"[object Date]" : "日期",
};
if(target == null) return null;
if(typeof target == "object"){
var toStr = Object.prototype.toString.call(target);
return types[toStr];
}else{
return typeof target;
}
}
console.log(getType(18));
console.log(getType({}));
console.log(getType([]));
console.log(getType(new Boolean()));
console.log(getType(new Date));
console.log(getType([1,2,"aini"]));
// 在ES5中,新增了Array.isArray()静态方法,该方法的作用是确定某个值是不是数组,而不管它是在哪个全局环境中创建的;如:
var colors = ["red","blue","yellow"];
console.log(Array.isArray(colors));
(万能的静态方法,都能判断)
9-8-2 数组去重
1,// 使用一个空对象作为临时数组不重复元素值的载体,即把数组不重复元素值保存为空对象属性,再把该属性名添加到一个返回的数组中;
2,// 最主要的原理:对象的属性名不可能重复,会覆盖,如
var arr = [1,3,2,3,4,2,3,2,1,1,2,3,4];
function getUnique(arr){
var obj=[];
var a = [];
for(var i=0;i<arr.length;i++){
if(!obj[arr[i]]){
console.log(arr[i]);
obj[arr[i]]="aini";
a.push(arr[i]);
}
}
return a;
}
console.log(getUnique(arr));
9-8-3 类数组对象
ES数组的有一些特性是其他对象所没有的:
(1)// 当有新的元素添加到列表中时,自动更新length属性;
(2)// 设置length为一个较小值时将截断数组;
(3)// 从Array.prototype中继承一些方法;
(4)// 其类属性为Array;
(5)// 这些特点让数组和常规的对象有明显的区别;
(6)// 通常来说,把一个拥有数值length属性和对应非负整数属性的对象看作一种“类数组”对象;
(7)// 这些“类数组”对象不能直接调用数组方法或者其length属性也没有特殊的行为(如字符串的length属性就是只读的),但可以使用数据遍历的方法来遍历它们,就像操作真正的数组一样;
// 字符串是一个类数组对象:
// Arguments对象是一个类数组对象:
str = "zeronetwork";
str.length = 5;
console.log(str.length); // 11
字符串Length属性只读,不可写
str = "zeronetwork";
console.log(str[0],str[1],str[2]); // z e t
在客户端JS中,一些DOM方法,如document.getElementsByTagName()也返回类数组对象,如:
var lis = document.getElementsByTagName("li");
console.log(lis);
for(var i=0;i<lis.length;i++){
var li = lis[i];
li.onclick = function(e){
alert(this.innerText); //在对应的li标签鼠标单击时弹出里面内容
}
}
自定义一个类数组对象
如果让一个对象成为一个类数组对象,主要的做法是:利用属性名模拟数组元素索引;动态的length属性;甚至可以调用push等数组方法;
添加length属性
var obj = {0:"aini",1:"dinur",2:"dil"};
console.log(obj[0],obj[1],obj[2]);
for(p in obj){
console.log(p,obj[p]);
};
//添加length属性
var obj = {0:"aini",1:"dinur",2:"dil",length:3};
console.log(obj[0],obj[1],obj[2]);
console.log(obj.length);
//动态添加length属性
var o = {};
var i = 0;
while(i<10){
o[i]= i*i;
i++;
};
o.length=i;
//把o当做数组一样遍历
var sum = 0;
for(var j=0;j<o.length;j++){
sum+=o[j];
}
console.log(sum);
自定义一个类数组对象
//自定义类数组类及对象
function MyArray(){
this.length = arguments.length;
for(var i=0;i<this.length;i++){
this[i]=arguments[i]
}
};
var arr = new MyArray(4,3,5,"wangwei");
for (var i=0; i<arr.length;i++){
console.log(arr[i]);
};
function YourSize(size){
this.length=size;
for(var i=0;i<size;i++){
this[i]=[i];
}
};
var a = new YourSize(3);
for(var x in a){
this[i]=i;
console.log(x,a[x]);
};
console.log(a);
添加push方法
//添加push方法,同时更新length属性;
var obj = {'0':'a','1':'b','2':'c',length:3,push:function(){
this[this.length]=arguments[0];
this.length+=arguments.length;
return this.length;
}}
console.log(obj.length);
console.log(obj.push("aini"));
添加push方法;添加多个元素
//添加push方法,同时更新length属性;
var obj = {'0':'a','1':'b','2':'c',length:3,push:function(){
for(var i=0;i<arguments.length;i++){
this[this.length]=arguments[i];
this.length++
}
return this;
}}
console.log(obj.length);
console.log(obj.push("aini","dilnur","diana","norah"));
可间接应用Array的方法:
//间接使用array的push方法;
var obj = {
length:0,
push:Array.prototype.push,
splice:Array.prototype.splice
};
console.log(obj.push("c","d")); //2
console.log(obj.length); // 2
9-8-4 检测类数组对象
封装一个函数,用来检测类数组对象,如
var o = new String("aini");
// 判定 O 是否是一个类数组对象
// 字符串和函数都具有length属性,但可以用typeof进行排除
// 客户端,DOM文件节点也具有length属性,需要用额外判断o.nodeType!=3将其排除
function isArrayLike(o){
if(o && // o 是非null或undefined
typeof o =="object" && // 是对象
isFinite(o.length) && // 有限数
o.length>0 && // 大于0
o.length == Math.floor(o.length) && //length 属性是整数
o.length < 2^32 //
)
return true
else
return false;
};
console.log(isArrayLike(o));
ES的数组方法为了能应用在类数组对象上而特意定义为通用的,但是,由于类数组没有继承自Array.prototype,就不能直接调用某些数组方法,只有间接的使用Function.call方法调用,如
var s1 = "aini like ";
console.log(s1.concat(" dilnur"));
var o = {0:"a",1:"b",2:"c",length:3};
o.length=3;
console.log(Array.prototype.join.call(o,"+"));
console.log(Array.prototype.slice.call(o,0));
console.log(Array.prototype.map.call(o,function(x){
return x.toUpperCase();
}));
注:concat方法是特例,可以直接用在字符串上面
作为数组的字符串
// 字符串的行为类似于只读的数组;除了用charAt()方法来访问单个字符外,还可以使用方括号;ES为可索引的字符串定义这些特性,主要为了更加简洁、可读和更高效的操作;
var s = "aini";
console.log(s.charAt(0)); // a
console.log(s[0]); // a
字符串的这些类数组的行为,可以使用数组方法
var str = "ainilikedilnur";
console.log(Array.prototype.join.call(str,","));
console.log(Array.prototype.filter.call(str,function(s){
return s
}).join(""));
字符串是不可变值,所以当把它当作数组看待时,它们是只读的;故如push、sort、reverse、splice等数组方法会修改数组,但在字符串上是无效的;
9-8-5 二位数组和多维数组
// 二维或多维数组就是以数组作为数组元素的数组;多维数组的应用场景还是比较多的,比如一个班的学生成绩表;
但从本质上说,ES并不支持真正的二维或多维数组;
var a1 = [1,2,3];
var a2 = [4,5,6];
var a3 = [7,8,9];
var arr = [a1,a2,a3];
// 或者
var arr = [
[1,2,3],
[4,5,6],
[7,8,9,10,12]
];
var arr = [
[1,2,3],
['wangwei','wujing'],
['零点','网络']
];
var arr = new Array(
new Array(1,2,3),
['wangwei','wujing'],
new Array('零点','网络')
);
如果把二维数组当作另外一个数组的元素,那就是三维数组了,以此类推,就形成多维数组;
访问二维或多维数组,只要多次使用[ ]操作符即可,如:
var arr = [
[1,2,3],
['wangwei','wujing'],
['零点','网络']
];
console.log(arr[1][1]); // 访问
arr[3] = new Array('one','two','three'); // 添加
arr[2][2] = "程序员";
console.log(arr);
二维或多维数组的遍历同一维数组一致,只不过使用了嵌套遍历
求最大值
var arr = [
[88,67,44,98],
[56,78,99,56],
[79,95,78,92]
];
var max = arr[0][0];
for(var i=0;i<arr.length;i++){
for (var j=0;j<arr[i].length;j++){
arr[i][j]>max?(max=arr[i][j]):max;
}
};
console.log(max);
九九乘法表
var table = new Array(9);
for(var i=0;i<table.length;i++) table[i]=new Array(9);
for(var row=0;row<table.length;row++){
for(var col=0;col<table[row].length;col++){
table[row][col]=row*col;
}
}
console.log(table[5][9]);
function getTable(arr){
var table = document.createElement("table");
for(var i=1; i<arr.length;i++){
var tr = document.createElement("tr");
for(var j=1;j<arr[i].length;j++){
var td = document.createElement("td");
td.innerText=i+"*"+j+"="+arr[i][j];
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}
getTable(table);