深浅拷贝
要解释深浅拷贝,就不得不说一下引用类型的赋值操作,这里我们在demo中看一下什么叫深拷贝与浅拷贝。
引用类型的赋值操作
1. 简单数据类型/基本数据类型
实例
// 此时 str1 中存储的是 数据 北京
var str1 = '北京';
// 将 str1 中存储的数据北京 赋值给 str2
var str2 = str1;
// 之后对str1 的操作,与str2,无关
str1 = '上海';
console.log(str1 , str2);
// 此时 str1 = '上海', str2 = '北京'
总结
根据本demo,数据类型为基本数据类型时,变量经过赋值后得到的存储的数据,赋值之后,变量 str1 和 str2 没有关系。
2. 引用数据类型/复杂数据类型
实例1(浅拷贝)
// arr1 变量中 存储的是 数组的 内存地址
var arr1 = [1,2,3,4,5];
// 将arr1 存储的内存地址,赋值给arr2,两个变量存储的是相同的内存地址
var arr2 = arr1;
// arr1操作,数组,arr2也会改变
// 因为arr1和arr2,同时存储的是相同的数据,操作的也是同一个数组
// 同理,arr2,操作数组,arr1也会改变
arr1[0] = '北京';
console.log(arr2);
// 此时arr2 = ['北京',2,3,4,5]
总结
根据本demo,数据类型为引用数据类型时,直接将变量 arr1 赋值给 arr2 存储的是引用数据类型的内存地址,这种操作称为浅拷贝。
此时操作 arr1 ,arr2 也将发生改变。
实例2(深拷贝)
// 只复制arr3中的数据
var arr3 = ['北京','上海','广州','重庆','天津'];
// 循环遍历,获取arr3中的所有数据数值,赋值到新的数组中
var arr4 = [];
// 通过循环,生成arr3的所有索引下标
for(var i = 0 ; i <= arr3.length-1 ; i++){
// arr3[i] 就是获取 arr3中的 存储的 数据信息
// arr4.push() 将arr3中的数据 arr3[i] , 写入到 arr4中
arr4.push(arr3[i]);
}
console.log(arr4);
// 此时 arr4 = ['北京','上海','广州','重庆','天津']
// 与 arr3 的数据内容相同
arr3[0] = '武汉';
console.log(arr3 , arr4);
// 此时 arr3 = ['武汉','上海','广州','重庆','天津']
// 但是 arr4 = ['北京','上海','广州','重庆','天津'] 不变
总结
根据被demo,数据类型为引用数据类型时,我们可以通过循环遍历只将 arr3 的数组内容赋值给 arr4 ,而不赋值数组的内存地址,此时操作 arr3 就不会影响到 arr4 了。
像这样只复制引用数据类型中的数据内容而不是复制地址的操作,叫做深拷贝。
循环遍历数组的方法
1. 通过 for 循环,生成所有的索引下标
语法
for(var i = 0 ; i <= arr.length-1 ; i++){
程序内容;
}
实例
var arr = ['北京','上海','广州','重庆','天津'];
for(var i = 0 ; i <= arr.length-1 ; i++){
console.log(i,arr[i]);
}
2. 通过 forEach() 方法实现循环遍历
语法规则
数组变量.forEach(function(形参1,形参2,形参3){
程序内容;
})
其中:
形参1:存储的是当前循环次数时,获取的单元数据
参数2:存储的是当前循环次数时,获取的单元索引下标
参数3:存储的是原始数组
三个形参,不一定都要定义,可以根据实际需求
形参可以任意定义,只要符合命名规范就可以
forEach() 只能循环遍历数组,不能循环遍历对象
实例
var arr = ['北京','上海','广州','重庆','天津'];
/*
第一次循环,循环对象是 第一个单元
val : 存储数据 '北京' key : 存储索引 0 arr : 存储原始数组
第二次循环,循环对象是 第二个单元
val : 存储数据 '上海' key : 存储索引 1 arr : 存储原始数组
第三次循环,循环对象是 第三个单元
val : 存储数据 '广州' key : 存储索引 2 arr : 存储原始数组
第四次循环,循环对象是 第四个单元
val : 存储数据 '重庆' key : 存储索引 3 arr : 存储原始数组
第五次循环,循环对象是 第五个单元
val : 存储数据 '天津' key : 存储索引 4 arr : 存储原始数组
*/
arr.forEach(function(val,key,arr){
console.log(val,key,arr);
})
3. 通过 for…in方法实现循环遍历
语法规则
for(var 自定义变量 in 数组){
程序内容;
}
其中:
自定义变量:存储数组单元索引
存储的索引的数据类型是字符串类型。如果要执行加法运算,需要转化为数值类型
for...in 是数组,对象都可以使用的循环。
for...in 循环只能获取索引,别的不能获取
实例
var arr = ['北京','上海','广州','重庆','天津'];
for(var key in arr){ // 自定义变量,存储索引 0 1 2 3 4 ....
console.log(key,arr[key]); // arr[key] 就是索引对应的数据
}
总结
for循环 forEach()循环 for...in循环
针对数组而言,都可以完成我们的需求
只是针对不同的情况
推荐使用 forEach() ,最简单最方便
for循环的优点是,可以控制循环的起始和结束
for...in一般是针对对象的循环
在介绍如何进行数组去重,截取扩展名,屏蔽关键字之前,首先我们需要先了解一些字符串的操作方法,因为需要用到这些方法:
字符串的循环操作方法
字符串是一个非常神奇的数据类型。
字符串虽然是一种基本数据类型,也没有length属性,但是当我们企图使用 length 和 索引下标 的方式来操作字符串时,也是可以操作的。
字符串类型,也称作包装数据类型,在操作时,会将自己包装成一个 伪数组 的形式 。
支持 length 和 索引下标的操作,但是一些数组的操作方法不支持,例如:不支持 forEach() 方法循环遍历字符串。
实例
var str= 'abcdefg';
console.log(str);
console.log(str[0]); // 字符串中索引下标是0,实际是第一个字符
console.log(str.length); // 获取字符串的长度,也就是字符个数
str.length = 2; // 不能通过设定length属性来设定字符串串度
console.log(str); // 输出,字符串没有变化
// 使用for循环,生成字符串所有的所有索引下标
// 通过索引下标,获取对应的字符
for(var i = 0 ; i <= str.length-1 ; i++){
console.log(str[i]);
}
// 使用for...in方法循环遍历字符串
// 自定义变量key中 存储的是 str 字符串的索引下标
// str[key] 就是索引下标对应的字符
for(var key in str){
console.log(str[key]);
}
// 字符串是伪数组,不支持forEach() 方法循环遍历,会报错
str.forEach(function(val){
console.log(val);
})
字符串的基本操作方法
1. 变量.toString()
将数据类型强制转换为字符串,参数是转换的进制。
实例
var int = 100;
console.log( int.toString(8) );
利用此转换方法可实现数组的扁平化。
// 数组的扁平化
// 将多维数组,转换为一维数组
var arr = [1,2,3,4,5,[6,7,8,9,10,[11,12,13,[14,15,16]]]];
console.log(arr);
// 获取数组中数值的内容,以逗号间隔
var str = arr.toString();
console.log(str);
// 再将字符串转成数组
var newArr = str.split(',');
console.log(newArr);
但是此方法有些缺陷:
转换之后的所有数据类型,都是字符串
而且还不能转换存储对象、函数的数组
要完美转换,我们之后有其他方法学习,先记住这种粗糙的方式
2. 变量.indexOf() — 字符首次出现的位置
通过此方法可以查找数组或者字符串中是否有某个数值或者某个字母。
如果有这个内容,返回值是这个内容的 索引下标;
如果没有这个数据,返回值是 -1 。
这里执行的是 === 全等比较。
实例
var arr = ['北京','上海','广州',1,2,3,4,5];
console.log( arr.indexOf('广州') ); // 返回值是 2
console.log( arr.indexOf(5) ); // 返回值是 7
console.log( arr.indexOf(500) ); // 返回值是 -1
console.log( arr.indexOf('5') ); // 返回值是 -1
3. lastIndexOf() — 字符最后一次出现的位置
通过此方法可以查找数组或者字符串中是否有某个数值或者某个字母。
如果有符合条件的数据,执行结果是数据最后一次出现位置的 索引下标;
如果没有符合条件的数据,执行结果是 -1 。
执行的也是 === 全等比较。
实例
var arr1 = [1,2,3,4,5,1,2,3,4,5,1,2,3,4,5];
console.log(arr1);
console.log( arr1.indexOf(5) ); // 返回值是 4
console.log( arr1.lastIndexOf(5) ); // 返回值是 14
console.log( arr1.lastIndexOf('5') ); // 返回值是 -1
4. 字符串变量.toUpperCase() 字符串变量.toLowerCase()
通过这两种方法可以改变字符的大小写。
字符串.toUpperCase() 字符串中所有字符大写
字符串.toLowerCase() 字符串中所有字符小写
不会改变字符串存储的原始内容,如果需要改变,要做赋值操作
实例
var str1 = 'hello word';
// 将函数值操作的返回值,赋值给变量
str1 = str1.toUpperCase();
console.log(str1);
var str2 = 'HELLO WORD';
str2 = str2.toLowerCase();
console.log(str2);
5. 字符串变量.charAt() 字符串变量.charCodeAt()
通过这两种方法可以获取指定位置的字符或者获取指定位置的字符的编码数值。
实例
// 获取指定位置的字符
// 字符串.charAt()
var str3 = 'abcdefg';
console.log( str3.charAt(0) );
// 获取指定位置的字符的编码数值
// 字符串.charCodeAt()
// 返回字符在ASCII码表中的数值
// 范围是 0-255 一共 256 个字符
console.log( str3.charCodeAt(0) );
6. 字符串变量.substring() 字符串变量.substr()
通过这两种方法可以对字符串进行截取操作。
字符串变量.substring()语法规则
字符串截取:从原始字符串(母串)中,截取一部分字符串(子串)
参数1: 截取起始字符的索引下标
参数2: 截取结束字符的索引下标
<!--
定义1个参数:从指定的索引位置开始,截取至字符串的结尾
定义2个参数:从指定的索引位置开始,截取至指定的结束位置索引
包含起始索引,不包含结束索引
两个参数相同,结果是空字符串
如果是第1个数值小,第2个数值大,是向字符串结尾截取
如果是第1个数值大,第2个数值小,是向字符串起始截取
第一个参数是负数,相当于从字符串的起始位置开始截取
第二个参数是负数,相当于从指定的位置开始,向前截取,截取到字符串的起始字符串
不包括起始字符
-->
注:高亮的为常用规则,要能熟练运用,注释中的其他规则主要是了解。
实例
var str4 = 'abcdefghijklmn';
// 从索引下标是4,实际是第5个字符开始截取,到字符串的结尾
console.log( str4.substring(4) );
// 从索引是4,实际是第五个字符开始截取
// 截取至索引是5,实际是第六个字符结束
// 包裹索引是4的字符,不包括索引是5的字符
console.log( str4.substring(4 , 5) );
// 从索引是4的位置开始,向索引是0的位置截取
// 此时,不会包括4索引的字符,会包括0索引的字符
console.log( str4.substring(4 , 0) );
// 第一个参数是负数,效果是从字符串起始位置开始截取
console.log( str4.substring(-4) );
// 第二个参数是负数,效果是从指定位置开始,向字符串的起始位置截取
// 截取至字符串的第一个字符
// 不包含指定位置的字符串
// 指定索引是3,向字符串起始位置截取
// 结果是 索引是2的字符,至起始第一个字符
console.log( str4.substring(3 , -6) );
字符串变量.substr()语法规则
从母串中截取子串
参数1:指定截取的起始位置
参数2:指定截取的字符个数
语法1:只定义一个参数,从指定位置开始,截取至字符串结尾
语法2:定义两个参数,从指定位置开始,截取指定个数的字符
语法3:第一个参数是负数,指定起始位置是倒数第几个字符
实例
var str5 = 'abcdefghijklmn';
// 从索引是3,也就是第四个字符开始,截取至字符串结尾
console.log( str5.substr( 3 ) );
// 从索引是3,也就是第四个字符开始,截取3个字符
console.log( str5.substr( 3 , 3 ) );
// 从倒数第三个字符开始截取,截取至字符串结尾
console.log( str5.substr( -3 ) );
// 从倒数第三个字符开始截取,截取两个字符
console.log( str5.substr( -3 , 2 ) );
// 第二个参数是负数,结果是空字符串
console.log( str5.substr( 3 , -2 ) );
7. 字符串变量.split()
将字符串按照指定的字符,切割成数组。可以用来删除字符串中的关键词。
实例
var str = '北京卧槽今天卧槽的天气卧槽有点热';
var arr = str.split('卧槽');
// 数组单元中,数据就没有 卧槽 一词了,但同时,字符串也被切割成了数组
console.log(arr);
8. 字符串.replace()
字符串内容替换函数,将制定的字符串内容,替换为新的内容。
语法规则
第一个参数:字符串原有的,要替换没的。
第二个参数:字符串没有的,要新替换进来的。
默认只能执行一次替换,要替换所有符合的内容,必须要配合正则表达式。
实例
var str6 = '北京卧槽今天卧槽的天气卧槽有点热';
// 将关键词替换为 ** 只能替换第一个符合的内容
str6 = str6.replace('卧槽' , '**');
console.log(str6);
屏蔽关键词
屏蔽字符串中的关键词,这里我们可以使用 split() 方法 配合 join() 方法实现。
// 输入的字符串中,没有关键词和敏感词
// 例如 卧槽 这样的关键词会被屏蔽为 **
var str = '北京卧槽今天卧槽的天气卧槽有点热';
// 思路:
// 1,以关键词为间隔符号,将字符串切割为数组
// 新数组的单元,是没有关键词的
// 2,将数组再拼接成字符串,以 ** 为间隔符号
var arr = str.split('卧槽');
// 数组单元中,数据就没有 卧槽 关键词了
console.log(arr);
// 用 ** 作为拼接字符串的间隔符号
// 效果就是将 关键词 替换为 **
var newStr = arr.join('**');
console.log(newStr);
上面我们也说过,replace() 方法也可以实现,但是需要配合正则表达式,这里暂时不多做介绍。
数组去重 / 字符串去除重复字母
这里,我们用 indexOf() 方法进行去重。
// 数组中存储重复的数值
// 去除重复数值之后,应该是 [1,2,3,4,5]
var arr = [1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,5,5,5,5,5]
// 基本思路1 indexOf()
// 建立一个新的数组,将原始数值中的数值,向新的数组中写入
// 如果这个数值不存在于新的数组中,就执行写入,如果已经存在,就不写入
// 建立一个空数组,来存储原始数组中,不重复数据
var newArr = [];
// 循环遍历,获取原始数组arr中的所有数值
arr.forEach(function(v){
// 在新数组中,查找当前获取的原始数组的数值
// newArr.indexOf(v) 执行结果如果是 -1
// 证明在新数组中,没有这个原始数组的数据
if(newArr.indexOf(v) === -1){
// 将这个数据,写入到新数组中
newArr.push(v)
}
})
console.log( newArr );
// 字符串中存储重复的字母
var str = 'aaabbbbcccddddeeeeefffff';
// 原理相同,只是字符串不能使用forEach()循环
var newStr = '';
// 通过for...in来循环遍历,字符串
// key存储的是每个字母的索引下标
for(var key in str){
// str[key] 是 当前循环,获取的索引下标,对应的字符串中的字母
// newStr.indexOf(str[key]) 判断 新字符串中,是否有当前这个字母
// 如果结果是 -1 证明新字符串中没有这个字母
if(newStr.indexOf(str[key]) === -1){
// 执行字符串拼接操作,将这个字母拼接入新的字符串中
newStr += str[key];
}
}
console.log(newStr);
截取文件扩展名
文件名称由两部分组成:文件名.扩展名
在执行一些操作时,要判断文件类型是否符合规定的类型。例如上传图片,经常要求必须是 jpg 类型的图片,就需要获取 文件名称 中的 扩展名 来进行判断。
原理
文件名是任意的名称形式,可以是 123.456.7989.jpg 123.456.7989.html
我们无法确定扩展名具体的字符个数,只能确定是文件名中 最后一个点 之后的内容,一定是扩展名。
所以需要截取的是 最后一个点 之后的 内容 。
lastIndexOf() + substr()/substring()方法
这里以substr()为例:
var fileName = '123.456.abc.jpg';
// 找到最后一个点的位置 --- lastIndexOf()
// 执行结果是 最后一个点的索引下标
// 实际需求的是,点之后的内容,不包括点
// 实际截取扩展名的起始字符索引,是 点索引+1
var num = fileName.lastIndexOf('.') ;
console.log(num);
// 从这个位置开始,截取至字符串的尾部
// 要截取至字符串末位,只需要定义一个起始字符的索引下标
// substring substr 效果相同,爱用哪个用那个
var res = fileName.substr(num+1);
// 以上部分可以写成一行代码
// var res = fileName.substr( fileName.lastIndexOf('.') +1);
console.log(res);