9、数组方法、类数组
数组
数组的概念
- 数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式。
- 数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。
- 数组是一种将一组数据存储在单个变量名下的优雅方式。
创建数组
/**
* @创建数组
* 数组的字面量是方括号[]
* 声明数组并赋值称为数组大的初始化
*/
// 利用 new 创建数组
var arr=new Array();
// 利用数组字面量创建数组
var arr1=[]; // 空数组
var arr2=['1','2',3]; // 初始化数组
// 数组中可以存放任意类型的数据
var arrStus=['小白',13,true,18.8,null,undefined];
遍历数组
/**
* @数组遍历
* 把数组中的每个元素从头到尾都访问一次,通过for循环索引遍历数组中的每一项
*/
var arr=[...arr];
for(var i=0;i<arr.length;i++){
console.log(arr[i]);
}
数组方法
检测是否为数组
/**
* @检测数组
* instaceof 可以判断一个对象是否是某个构造函数的实例
* Array.isArray() 用于判断一个对象是否为数组
*/
var arr=[1,23];
var obj={}
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
添加/删除数组元素
方法名 | 说明 | 返回值 |
---|---|---|
push(args) | 末尾添加一个或多个元素 | 返回新的长度 |
pop() | 删除数组最后一个元素(无参) | 放回删除的元素的值 |
unshift(args) | 头部添加一个或多个元素 | 返回新的长度 |
shift() | 删除数组第一个元素(无参) | 返回删除元素的值 |
/**
* @添加删除数组的元素
*/
var arr=[1,23,4]
// push() 末尾添加元素(有参),返回数组的新长度
var res1=arr.push(5);
console.log(res1,arr); // 4,[1,23,4,5]
// nushift() 头部添加元素(有参),返回数组的新长度
var res2=arr.unshift(0);
console.log(res2,arr); // 5,[0,1,23,4,5]
// pop() 末尾删除一个元素(无参),返回删除元素的值
var res3=arr.pop();
console.log(res3,arr); // 5,[0,1,23,4]
// shift() 头部删除一个元素(无参),返回删除元素的值
var res4=arr.shift();
console.log(res4,arr); // 0,[1,23,4]
数组排序
方法名 | 说明 | 是否修改原数组 |
---|---|---|
reverse() | 反转数组中元素的顺序,无参数 | 改变原数组,返回新数组 |
sort() | 对数组元素进行排序 | 改变原数组,返回新数组 |
/**
* @数组排序
*
*/
// 数组反转reverse()
let arr=[1,2,3,4,5];
console.log(arr.reverse()); // [5,4,3,2,1]
console.log(arr); // [5,4,3,2,1]
// 数组排序sort
/**
* sort():默认是按照最左边的数字进行排序,不是按照数字的大小惊醒排序
* sort((a,b) => a-b):按照升序进行排序
* sort((a,b) => b-a):按照降序进行排序
*/
let arrSort=[1,22,3,41,0,32];
console.log(arrsort.sort()); // [0,1,22,3,32,41]
let arrSort1=arrSort.sort((a,b)=>a-b);
console.log(arrSort1); // [0,1,3,22,32,41]
let arrSort2=arrSort.sort((a,b)=>b-a);
console.log(arrSort2); // [41,32,22,3,1,0]
数组索引
方法名 | 说明 | 返回值 |
---|---|---|
indexOf() | 数组中查找给定元素的第一个索引,有参数 | 存在,返回索引号;不存在,返回-1 |
LastIndexOf() | 数组中最后一个元素的索引 | 存在,返回索引号;不存在,返回-1 |
/**
* @数组索引
* indexOf() 查找某个元素的索引值,如有重复的,返回第一个查到的索引值;不存在返回-1
* lastIndexOf() 和indexOf()功能一样,不同的是从后往前找
*/
// indexOf()
let arr = [1,2,3,4,5,2]
let arr1 = arr.indexOf(2)
console.log(arr1) // 1
let arr2 = arr.indexOf(9)
console.log(arr2) // -1
// lastIndexOf()
let arr3 = arr.lastIndexOf(2)
console.log(arr3) // 5
let arr4 = arr.lastIndexOf(9)
console.log(arr4) // -1
数组转换字符串
方法名 | 说明 | 返回值 |
---|---|---|
toString() | 把数组转换成字符串,逗号分隔每一项 | 返回一个字符串 |
join(‘分隔符’) | 把数组中的所有元素转成为一个字符串,按条件分隔每一项 | 返回一个字符串 |
/**
* @数组转换字符串
* toString()
* join()
*/
let arr=['a','b','c',3];
console.log(arr.toString());// "a,b,c,3"
console.log(arr.join('-'));// "a-b-c-3"
其他方法
方法名 | 说明 | 返回值 |
---|---|---|
concat() | 连接两个或多个数组,不影响原数组 | 返回一个新数组 |
slice(begin,end) | 数组截取(截取begin到end之间的元素) | 返回被截取元素组成的新数组 |
splice(begin,num) | 数组删除(从begin开始,要删除num个) | 返回删除元素组成的新数组,改变原数组 |
/**
* @其他方法
* cancat() 连接两个数组,返回值为连接后的新数组
* slice(begin,end) 截取索引值begin到索引值end的数组,不包含end索引的值,返回值是截取的数组
* splice(i,n) 删除从i开始之后的那个元素,返回值是删除的元素
*
*/
// cancat() 原数组不变
let arr=[1,2,3,4,5];
console.log(arr.concat([1,2]));// [1,2,3,4,5,1,2]
console.log(arr); // [1,2,3,4,5]
// slice() 元素组不变
console.log(arr.slice(1,3));// [2,3]
console.log(arr); // [1,2,3,4,5]
// splice() 原数组改变
console.log(arr.splice(2,2)); // [3,4]
console.log(arr); // [1,2,5]
ES6新增方法
/**
* @ES6新增
* callbak的参数:item -- 当前索引的值
* index -- 索引
* arr -- 原数组
*/
// arr.forEach(callback) 遍历数组,无return,即使有return,也不会返回任何值,并且会影响原来的数组
let arr = [1, 2, 3, 4, 5];
arr.forEach((item, index, arr) => {
console.log(`value:${item}--index:${index}--array:${arr}`);
});
var res = arr.forEach((item, index, arr) => {
arr[index] = item * 2;
return arr;
});
console.log(arr); // [2,4,6,8,10]
console.log(res); // undfined
// arr.map(callback) 映射数组,有return返回一个新数组
arr.map((item, index, arr) => {
item = item * 2;
console.log(`value:${item}--index:${index}--array:${arr}`);
});
let arr1 = arr.map((item, index, arr) => {
item = item * 3;
return item;
});
console.log(arr1); //[3,6,9,12,15]
// arr.filter(callback) 过滤数组,返回一个满足条件的数组
let arr2 = arr.filter((item, index) => {
return item < 3;
});
console.log(arr2); // [1,2]
// arr.every(callback) 依据判断田间,数组的元素是否全满足,若满足返回true
let arr3 = arr.every((item, index) => item < 3);
console.log(arr3); // false
let arr4 = arr.every((item, index) => item < 6);
console.log(arr4); // true
// arr.some(callback) 依据判断条件,数组的元素是否有一个满足,若有一个满足返回true
let arr5 = arr.some((item, index) => item < 3);
console.log(arr5); // true
let arr6 = arr.some((item, index) => item > 6);
console.log(arr6); // false
// arr.reduce(callback,initialValue) 迭代数组的所有项,累加器,数组中的每个值(从左到右)合并,最终计算为一个值
let arr7 = arr.reduce((preValue, curValue) => preValue + curValue);
console.log(arr7); // 15
类数组
/**
* @类数组
* 具有:指向对象元素的数字索引下标以及length属性告诉我们对象的元素个数;
* 不具有:诸如push、forEach以及indexOf等数组对象具有的方法
* 例如:1、DOM方法document.getElementByClassName()的返回结果(实际上许多DOM方法的返回值都是类数组);
* 2、特殊变量arguments对象;
* 3、input的文件对象FileList;
*/
// 通用方法
Array.prototype.m.call(args); // m是方法名
// 将类数组对象转化为数组
Array.prototype.slice.call(arguments);
// 类数组判断
function isArrayLike(o) {
if (
o &&
typeof o === "object" &&
isFinite(o.length) &&
o.length > 0 &&
o.length === Math.floor(o.length) &&
o.length < 4294967296
)
return true;
else return false;
}
10、自定义原型方法
自定义原型发方法
1、自定义Array.prototype.unshift
(1) 利用spliece,改变原数组
/**
* @自定义Array.prototype.unshift
*/
Array.prototype.unshift=function(){
for(var position=0;position<arguments.length;position++){
this.splice(position,0,arguments[position])
}
return this.length
}
(2)利用Array.prototype.splice.call(arguments)将类数组转换成数组
/**
* @自定义Array.prototype.myUnshift
*/
Array.prototype.myUnshift=function(){
let arr=Array.prototype.slice.call(arguments);
// 参数为数组时,合并数组,为其他值时,将参数push到数组末尾
let newArr=this.concat(arr);
return newArr;
}
2、根据每个元素的总字节数,对数组惊醒排序
/**
* @自定义sumStrBytes-数组排序
*/
function sumStrBytes(str){
let sumBytes=str.length;
for (let i = 0; i < str.length; i++) {
if(str[i].charCodeAt()>255) sumBytes++
}
return sumBytes;
}
function sortByStrBytes(arr){
arr.sort((a,b)=>{
return sumStrBytes(a) - sumStrBytes(b);
});
}
3、封装更精确的typeof方法
/**
* @封装typeof方法
* 先判断是否为null
* 在根据typeof返回结果来区别引用值和原始值
*/
function myTypeof(val) {
var toString = Objcet.prototype.toString,
resOfToString = toString.call(val),
resOfTypeof = typeof val,
exactTypeMatch = {
"[object Objec]": "object",
"[object Function]": "function",
"[object Array]": "array",
"[object Number]": "object number",
"[object String]": "object string",
"[object Boolean]": "object boolean",
};
if (val === null) {
return "null";
} else if (resOfTypeof === "object") {
return exactTypeMatch[resOfToString];
} else {
resOfTypeof;
}
}
4、数组去重(利用对象唯一的特点)
/**
* @数组去重
*/
Array.prototype.unnique = function(){
let temArr = [],
obj = {};
for(var i = 0; i < this.length; i++){
if(!this[i]){
//赋一个正值,给之后判断留一个标记
obj[this[i]] = true
//非得直接将元素作为属性名的话,可以用hasOwnProperty来判断,是否已经赋值了
temArr.push(obj[this[i]])
}
}
return temArr
}
//用forEach和Object.values方法
Array.prototype.unique = function(){
let obj = {};
this.forEach((item)=>{
if(!obj[item]){
obj[item] = item
}
})
console.log(Object.values(obj))
return Object.values(obj)
}
5、找出字符串中第一个只出现一次的字符
/**
* @找出只出现一次的字符(第一个)
*/
let str = 'eqwerqwerqewrrasdfej';
function recordStrAmount(arr){
let obj = {};
for(var i = 0; i<arr.length; i++){
if(obj.hasOwnProperty(arr[i])){
obj[arr[i]]++
}else{
obj[arr[i]] = 1
}
}
//得到字符出现次数
for(var key in obj){
if(obj[key] === 1){
return key
}
}
}
11、错误信息、错误捕获
错误信息
/**
* @SyntaxError 语法错误
*/
// Unxepected number
var 1=1;
// Invalid or unexpected token
var 1a=1;
function 1test(){}
// Unexpected token =
new = 5;
// Unexpected token :
var a=5:
/**
* @RefereneError 引用错误
*/
// test is not defined
test()
// Invalid left-hand side in assignment
var a=1=2
var b=2;
console.log(b)=3;
/**
* @TypeError 类型错误
*/
// Bad:
// 123 is not a function
123()
// obj.say is not a function
var obj={};
obj.say();
// "string" is not a constructor
var a = new "string";
// 123 is not a constructor
var b = new 123;
/**
* @URIError URI错误
* URI 统一资源标识符 UNIFORM RESOURCE IDENTIFIER
* URL 统一资源定位符 UNIFORM RESOURCE LOCATOR
* URN 统一资源名称 UNIFORM RESOURCE NAME
* URL和URN是URI的子集,属于URI
* URL有协议有域名有资源空间 通过一个地址能访问到特定的页面
* URN 没有了URL中的协议 相当于一个id 名字 指代了资源的唯一性 所以叫NAME
*/
// 使用encodeURI方法把中文转为中文编码字符 是js内置的方法,
// 再使用decodeURI方法把中文编码字符转为中文,也是js内置的方法
let str = "测试字符";
let encodeStr = encodeURI(str);
console.log(encodeStr); // %E6%B5%8B%E8%AF%95%E5%AD%97%E7%AC%A6
let decodeStr = decodeURI(encodeStr);
console.log(decodeStr); //测试字符
// URI malformed at decodeURI(<anonymous>)
let str="%ef%erffv";
console.log(decodeURI(str));
错误捕获
/**
* @错误捕获
*/
// 原生JS实现
window.onerror=function(errorMessage,scriptURI,lineNumber,columnNumber,errorObj){
console.log('错误信息',errorMessage);
console.log('出错文件',scriptURI);
console.log('出错行号',lineNumber);
console.log('出错列号',columnNumber);
console.log('错误详情',errorObj);
}
// 收集请求错误
var ajax = function(type, url, callback){
var xhr = new XMLHttpRequest();
xhr.open(type, url);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
callback();
} else {
console.log('收集到一条错误');// 在这里收集错误信息
}
};
xhr.send(null);
}
// 因为.ccccccom这个路径是不存在的, 所以会执行收集区域的代码。
ajax('GET', 'http://www.lovejavascript.ccccccom', function(a){console.log(a)});
12、严格模式
严格模式
/**
* @严格模式
* es5的严格模式是采用具有限制性JavaScript变体的一种方式,从而是代码显示的脱离马虎模式、稀松模式、懒散模式(sloppy)模式。
* 严格模式不仅仅是一个子集:它的产生是为了形成与正常代码不同的语义。
* 不支持严格模式与支持严格模式的浏览器在执行严格模式代码时会采用不同行为。
* 严格模式代码和非严格模式代码可以共存,因此项目脚本可以渐进式地采用严格模式。
* 1、严格模式通过抛出错误来消除了一些原有静默错误。
* 2、严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:
* 有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
* 3、严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。
*/
// 整个脚本都开启严格模式
("use strict");
var v = "Hi! I'm a strict mode script";
// 为函数开启严格模式
function func() {
// 函数级别严格模式语法
"use strict";
}
// 1、不允许用with
!(function () {
with ({ x: 1 }) {
console.log(x); // 1
}
})();
/**--------------------------------- */
!(function () {
"use strict";
with ({ x: 1 }) {
console.log(x); // SyntaxError
}
})();
// 2、不允许未声明的变量被赋值
!(function () {
x = 1;
console.log(window.x); // 1
})();
/**--------------------------------- */
!(function () {
"use strict";
x = 1;
console.log(window.x); // ReferenceError
})();
// 3、arguments变为参数的静态副本
!(function (a) {
arguments[0] = 100;
console.log(a); // undefined
})(1);
/**--------------------------------- */
!(function (a) {
"use strict";
arguments[0] = 100;
console.log(a); // 1
})(1);
/**--------------------------------- */
!(function (a) {
"use strict";
arguments[0].x = 100;
console.log(a.x); // 100
})(1);
// 4、delete参数、函数名报错
!(function (a) {
console.log(delete a); // false
})(1);
/**--------------------------------- */
!(function (a) {
"use strict";
console.log(delete a); // SyntaxError
})(1);
// 5、delete不可配置的属性报错
!(function (a) {
var obj = {};
Object.defineProperty(obj, "a", { configurable: false });
console.log(delete obj.a); // false
})(1);
/**--------------------------------- */
!(function (a) {
"use strict";
var obj = {};
Object.defineProperty(obj, "a", { configurable: false });
console.log(delete obj.a); // TypeError
})(1);
// 6、对象字面量重复属性名报错
!(function () {
var obj = { x: 1, x: 2 };
console.log(obj.x); // 2
})();
/**--------------------------------- */
!(function () {
'use strict';
var obj = { x: 1, x: 2 };
console.log(obj.x); // SyntaxError
})();
// 7、禁止八进制字面量
!function(){
console.log(0123); // 83
}();
/**--------------------------------- */
!function(){
'use strict';
console.log(0123); // SyntaxError
}();
// 8、eval,arguments变为关键字,不能作为变量、函数名
!function(){
function eval(){}
console.log(eval); // function eval(){}
}();
/**--------------------------------- */
!function(){
'use strict';
function eval(){}
console.log(eval); // SyntaxError
}();
// 9、eval独立作用域
!function(){
eval('var evalVal=2;');
console.log(typeof evalVal); // number
}();
/**--------------------------------- */
!function(){
'use strict';
eval('var evalVal=2;');
console.log(typeof evalVal); // undefined
}();
13、变量声明周期、垃圾回收
变量声明周期
局部JavaScript变量
在 JavaScript 函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。
您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。
只要函数运行完毕,本地变量就会被删除。
** 全局JavaScript变量**
在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。
JavaScript变量的生存期
JavaScript 变量的生命期从它们被声明的时间开始。
局部变量会在函数运行以后被删除。
全局变量会在页面关闭后被删除。
向未声明的JavaScript变量分配值
如果您把值赋给尚未声明的变量,该变量将被自动作为 window 的一个属性。
垃圾回收
标记清楚(常用)
工作原理:
1、当变量进入环境时,将这个变量标记为“进入环境”。
2、当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
1.垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
2.去掉环境中的变量以及被环境中的变量引用的变量的标记。
3.再被加上标记的会被视为准备删除的变量。
4.垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
引用计数
工作原理:
跟踪记录每个值被引用的次数。
工作流程:
1.声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。
2.同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1.
3.当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1.
4.当引用次数变成0时,说明没办法访问这个值了。
5.当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。